2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-04-03 16:42:36 +01:00
/* General filesystem local caching manager
*
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*/
# define FSCACHE_DEBUG_LEVEL CACHE
# include <linux/module.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/completion.h>
# include <linux/slab.h>
2010-07-20 22:09:01 +02:00
# include <linux/seq_file.h>
2018-04-04 13:41:27 +01:00
# define CREATE_TRACE_POINTS
2009-04-03 16:42:36 +01:00
# include "internal.h"
MODULE_DESCRIPTION ( " FS Cache Manager " ) ;
MODULE_AUTHOR ( " Red Hat, Inc. " ) ;
MODULE_LICENSE ( " GPL " ) ;
unsigned fscache_defer_lookup = 1 ;
module_param_named ( defer_lookup , fscache_defer_lookup , uint ,
S_IWUSR | S_IRUGO ) ;
MODULE_PARM_DESC ( fscache_defer_lookup ,
" Defer cookie lookup to background thread " ) ;
unsigned fscache_defer_create = 1 ;
module_param_named ( defer_create , fscache_defer_create , uint ,
S_IWUSR | S_IRUGO ) ;
MODULE_PARM_DESC ( fscache_defer_create ,
" Defer cookie creation to background thread " ) ;
unsigned fscache_debug ;
module_param_named ( debug , fscache_debug , uint ,
S_IWUSR | S_IRUGO ) ;
MODULE_PARM_DESC ( fscache_debug ,
" FS-Cache debugging mask " ) ;
struct kobject * fscache_root ;
2010-07-20 22:09:01 +02:00
struct workqueue_struct * fscache_object_wq ;
2010-07-20 22:09:01 +02:00
struct workqueue_struct * fscache_op_wq ;
2010-07-20 22:09:01 +02:00
DEFINE_PER_CPU ( wait_queue_head_t , fscache_object_cong_wait ) ;
/* these values serve as lower bounds, will be adjusted in fscache_init() */
static unsigned fscache_object_max_active = 4 ;
2010-07-20 22:09:01 +02:00
static unsigned fscache_op_max_active = 2 ;
2010-07-20 22:09:01 +02:00
# ifdef CONFIG_SYSCTL
static struct ctl_table_header * fscache_sysctl_header ;
static int fscache_max_active_sysctl ( struct ctl_table * table , int write ,
2020-04-24 08:43:38 +02:00
void * buffer , size_t * lenp , loff_t * ppos )
2010-07-20 22:09:01 +02:00
{
struct workqueue_struct * * wqp = table - > extra1 ;
unsigned int * datap = table - > data ;
int ret ;
ret = proc_dointvec ( table , write , buffer , lenp , ppos ) ;
if ( ret = = 0 )
workqueue_set_max_active ( * wqp , * datap ) ;
return ret ;
}
2014-08-06 16:03:24 -07:00
static struct ctl_table fscache_sysctls [ ] = {
2010-07-20 22:09:01 +02:00
{
. procname = " object_max_active " ,
. data = & fscache_object_max_active ,
. maxlen = sizeof ( unsigned ) ,
. mode = 0644 ,
. proc_handler = fscache_max_active_sysctl ,
. extra1 = & fscache_object_wq ,
} ,
2010-07-20 22:09:01 +02:00
{
. procname = " operation_max_active " ,
. data = & fscache_op_max_active ,
. maxlen = sizeof ( unsigned ) ,
. mode = 0644 ,
. proc_handler = fscache_max_active_sysctl ,
. extra1 = & fscache_op_wq ,
} ,
2010-07-20 22:09:01 +02:00
{ }
} ;
2014-08-06 16:03:24 -07:00
static struct ctl_table fscache_sysctls_root [ ] = {
2010-07-20 22:09:01 +02:00
{
. procname = " fscache " ,
. mode = 0555 ,
. child = fscache_sysctls ,
} ,
{ }
} ;
# endif
2009-04-03 16:42:36 +01:00
2021-06-17 14:21:00 +01:00
/*
* Mixing scores ( in bits ) for ( 7 , 20 ) :
* Input delta : 1 - bit 2 - bit
* 1 round : 330.3 9201.6
* 2 rounds : 1246.4 25475.4
* 3 rounds : 1907.1 31295.1
* 4 rounds : 2042.3 31718.6
* Perfect : 2048 31744
* ( 32 * 64 ) ( 32 * 31 / 2 * 64 )
*/
# define HASH_MIX(x, y, a) \
( x ^ = ( a ) , \
y ^ = x , x = rol32 ( x , 7 ) , \
x + = y , y = rol32 ( y , 20 ) , \
y * = 9 )
static inline unsigned int fold_hash ( unsigned long x , unsigned long y )
{
/* Use arch-optimized multiply if one exists */
return __hash_32 ( y ^ __hash_32 ( x ) ) ;
}
/*
* Generate a hash . This is derived from full_name_hash ( ) , but we want to be
* sure it is arch independent and that it doesn ' t change as bits of the
* computed hash value might appear on disk . The caller also guarantees that
* the hashed data will be a series of aligned 32 - bit words .
*/
unsigned int fscache_hash ( unsigned int salt , unsigned int * data , unsigned int n )
{
unsigned int a , x = 0 , y = salt ;
for ( ; n ; n - - ) {
a = * data + + ;
HASH_MIX ( x , y , a ) ;
}
return fold_hash ( x , y ) ;
}
2009-04-03 16:42:36 +01:00
/*
* initialise the fs caching module
*/
static int __init fscache_init ( void )
{
2010-07-20 22:09:01 +02:00
unsigned int nr_cpus = num_possible_cpus ( ) ;
unsigned int cpu ;
2009-04-03 16:42:36 +01:00
int ret ;
2010-07-20 22:09:01 +02:00
fscache_object_max_active =
clamp_val ( nr_cpus ,
fscache_object_max_active , WQ_UNBOUND_MAX_ACTIVE ) ;
ret = - ENOMEM ;
fscache_object_wq = alloc_workqueue ( " fscache_object " , WQ_UNBOUND ,
fscache_object_max_active ) ;
if ( ! fscache_object_wq )
goto error_object_wq ;
2010-07-20 22:09:01 +02:00
fscache_op_max_active =
clamp_val ( fscache_object_max_active / 2 ,
fscache_op_max_active , WQ_UNBOUND_MAX_ACTIVE ) ;
ret = - ENOMEM ;
fscache_op_wq = alloc_workqueue ( " fscache_operation " , WQ_UNBOUND ,
fscache_op_max_active ) ;
if ( ! fscache_op_wq )
goto error_op_wq ;
2010-07-20 22:09:01 +02:00
for_each_possible_cpu ( cpu )
init_waitqueue_head ( & per_cpu ( fscache_object_cong_wait , cpu ) ) ;
2009-04-03 16:42:37 +01:00
ret = fscache_proc_init ( ) ;
if ( ret < 0 )
goto error_proc ;
2010-07-20 22:09:01 +02:00
# ifdef CONFIG_SYSCTL
ret = - ENOMEM ;
fscache_sysctl_header = register_sysctl_table ( fscache_sysctls_root ) ;
if ( ! fscache_sysctl_header )
goto error_sysctl ;
# endif
2009-04-03 16:42:38 +01:00
fscache_cookie_jar = kmem_cache_create ( " fscache_cookie_jar " ,
sizeof ( struct fscache_cookie ) ,
2018-10-17 15:23:45 +01:00
0 , 0 , NULL ) ;
2009-04-03 16:42:38 +01:00
if ( ! fscache_cookie_jar ) {
2014-06-04 16:05:38 -07:00
pr_notice ( " Failed to allocate a cookie jar \n " ) ;
2009-04-03 16:42:38 +01:00
ret = - ENOMEM ;
goto error_cookie_jar ;
}
2009-04-03 16:42:37 +01:00
fscache_root = kobject_create_and_add ( " fscache " , kernel_kobj ) ;
if ( ! fscache_root )
goto error_kobj ;
2014-06-04 16:05:38 -07:00
pr_notice ( " Loaded \n " ) ;
2009-04-03 16:42:36 +01:00
return 0 ;
2009-04-03 16:42:37 +01:00
error_kobj :
2009-04-03 16:42:38 +01:00
kmem_cache_destroy ( fscache_cookie_jar ) ;
error_cookie_jar :
2010-07-20 22:09:01 +02:00
# ifdef CONFIG_SYSCTL
unregister_sysctl_table ( fscache_sysctl_header ) ;
error_sysctl :
# endif
2009-04-03 16:42:37 +01:00
fscache_proc_cleanup ( ) ;
2009-04-03 16:42:37 +01:00
error_proc :
2010-07-20 22:09:01 +02:00
destroy_workqueue ( fscache_op_wq ) ;
error_op_wq :
2010-07-20 22:09:01 +02:00
destroy_workqueue ( fscache_object_wq ) ;
error_object_wq :
2009-04-03 16:42:36 +01:00
return ret ;
}
fs_initcall ( fscache_init ) ;
/*
* clean up on module removal
*/
static void __exit fscache_exit ( void )
{
_enter ( " " ) ;
2009-04-03 16:42:37 +01:00
kobject_put ( fscache_root ) ;
2009-04-03 16:42:38 +01:00
kmem_cache_destroy ( fscache_cookie_jar ) ;
2010-07-24 11:10:09 +02:00
# ifdef CONFIG_SYSCTL
2010-07-20 22:09:01 +02:00
unregister_sysctl_table ( fscache_sysctl_header ) ;
2010-07-24 11:10:09 +02:00
# endif
2009-04-03 16:42:37 +01:00
fscache_proc_cleanup ( ) ;
2010-07-20 22:09:01 +02:00
destroy_workqueue ( fscache_op_wq ) ;
2010-07-20 22:09:01 +02:00
destroy_workqueue ( fscache_object_wq ) ;
2014-06-04 16:05:38 -07:00
pr_notice ( " Unloaded \n " ) ;
2009-04-03 16:42:36 +01:00
}
module_exit ( fscache_exit ) ;