743162013d
The current "wait_on_bit" interface requires an 'action' function to be provided which does the actual waiting. There are over 20 such functions, many of them identical. Most cases can be satisfied by one of just two functions, one which uses io_schedule() and one which just uses schedule(). So: Rename wait_on_bit and wait_on_bit_lock to wait_on_bit_action and wait_on_bit_lock_action to make it explicit that they need an action function. Introduce new wait_on_bit{,_lock} and wait_on_bit{,_lock}_io which are *not* given an action function but implicitly use a standard one. The decision to error-out if a signal is pending is now made based on the 'mode' argument rather than being encoded in the action function. All instances of the old wait_on_bit and wait_on_bit_lock which can use the new version have been changed accordingly and their action functions have been discarded. wait_on_bit{_lock} does not return any specific error code in the event of a signal so the caller must check for non-zero and interpolate their own error code as appropriate. The wait_on_bit() call in __fscache_wait_on_invalidate() was ambiguous as it specified TASK_UNINTERRUPTIBLE but used fscache_wait_bit_interruptible as an action function. David Howells confirms this should be uniformly "uninterruptible" The main remaining user of wait_on_bit{,_lock}_action is NFS which needs to use a freezer-aware schedule() call. A comment in fs/gfs2/glock.c notes that having multiple 'action' functions is useful as they display differently in the 'wchan' field of 'ps'. (and /proc/$PID/wchan). As the new bit_wait{,_io} functions are tagged "__sched", they will not show up at all, but something higher in the stack. So the distinction will still be visible, only with different function names (gds2_glock_wait versus gfs2_glock_dq_wait in the gfs2/glock.c case). Since first version of this patch (against 3.15) two new action functions appeared, on in NFS and one in CIFS. CIFS also now uses an action function that makes the same freezer aware schedule call as NFS. Signed-off-by: NeilBrown <neilb@suse.de> Acked-by: David Howells <dhowells@redhat.com> (fscache, keys) Acked-by: Steven Whitehouse <swhiteho@redhat.com> (gfs2) Acked-by: Peter Zijlstra <peterz@infradead.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Steve French <sfrench@samba.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Link: http://lkml.kernel.org/r/20140707051603.28027.72349.stgit@notabene.brown Signed-off-by: Ingo Molnar <mingo@kernel.org>
207 lines
4.9 KiB
C
207 lines
4.9 KiB
C
/* General filesystem local caching manager
|
|
*
|
|
* Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#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>
|
|
#include <linux/seq_file.h>
|
|
#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;
|
|
struct workqueue_struct *fscache_object_wq;
|
|
struct workqueue_struct *fscache_op_wq;
|
|
|
|
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;
|
|
static unsigned fscache_op_max_active = 2;
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
static struct ctl_table_header *fscache_sysctl_header;
|
|
|
|
static int fscache_max_active_sysctl(struct ctl_table *table, int write,
|
|
void __user *buffer,
|
|
size_t *lenp, loff_t *ppos)
|
|
{
|
|
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;
|
|
}
|
|
|
|
struct ctl_table fscache_sysctls[] = {
|
|
{
|
|
.procname = "object_max_active",
|
|
.data = &fscache_object_max_active,
|
|
.maxlen = sizeof(unsigned),
|
|
.mode = 0644,
|
|
.proc_handler = fscache_max_active_sysctl,
|
|
.extra1 = &fscache_object_wq,
|
|
},
|
|
{
|
|
.procname = "operation_max_active",
|
|
.data = &fscache_op_max_active,
|
|
.maxlen = sizeof(unsigned),
|
|
.mode = 0644,
|
|
.proc_handler = fscache_max_active_sysctl,
|
|
.extra1 = &fscache_op_wq,
|
|
},
|
|
{}
|
|
};
|
|
|
|
struct ctl_table fscache_sysctls_root[] = {
|
|
{
|
|
.procname = "fscache",
|
|
.mode = 0555,
|
|
.child = fscache_sysctls,
|
|
},
|
|
{}
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* initialise the fs caching module
|
|
*/
|
|
static int __init fscache_init(void)
|
|
{
|
|
unsigned int nr_cpus = num_possible_cpus();
|
|
unsigned int cpu;
|
|
int ret;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
for_each_possible_cpu(cpu)
|
|
init_waitqueue_head(&per_cpu(fscache_object_cong_wait, cpu));
|
|
|
|
ret = fscache_proc_init();
|
|
if (ret < 0)
|
|
goto error_proc;
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
ret = -ENOMEM;
|
|
fscache_sysctl_header = register_sysctl_table(fscache_sysctls_root);
|
|
if (!fscache_sysctl_header)
|
|
goto error_sysctl;
|
|
#endif
|
|
|
|
fscache_cookie_jar = kmem_cache_create("fscache_cookie_jar",
|
|
sizeof(struct fscache_cookie),
|
|
0,
|
|
0,
|
|
fscache_cookie_init_once);
|
|
if (!fscache_cookie_jar) {
|
|
pr_notice("Failed to allocate a cookie jar\n");
|
|
ret = -ENOMEM;
|
|
goto error_cookie_jar;
|
|
}
|
|
|
|
fscache_root = kobject_create_and_add("fscache", kernel_kobj);
|
|
if (!fscache_root)
|
|
goto error_kobj;
|
|
|
|
pr_notice("Loaded\n");
|
|
return 0;
|
|
|
|
error_kobj:
|
|
kmem_cache_destroy(fscache_cookie_jar);
|
|
error_cookie_jar:
|
|
#ifdef CONFIG_SYSCTL
|
|
unregister_sysctl_table(fscache_sysctl_header);
|
|
error_sysctl:
|
|
#endif
|
|
fscache_proc_cleanup();
|
|
error_proc:
|
|
destroy_workqueue(fscache_op_wq);
|
|
error_op_wq:
|
|
destroy_workqueue(fscache_object_wq);
|
|
error_object_wq:
|
|
return ret;
|
|
}
|
|
|
|
fs_initcall(fscache_init);
|
|
|
|
/*
|
|
* clean up on module removal
|
|
*/
|
|
static void __exit fscache_exit(void)
|
|
{
|
|
_enter("");
|
|
|
|
kobject_put(fscache_root);
|
|
kmem_cache_destroy(fscache_cookie_jar);
|
|
#ifdef CONFIG_SYSCTL
|
|
unregister_sysctl_table(fscache_sysctl_header);
|
|
#endif
|
|
fscache_proc_cleanup();
|
|
destroy_workqueue(fscache_op_wq);
|
|
destroy_workqueue(fscache_object_wq);
|
|
pr_notice("Unloaded\n");
|
|
}
|
|
|
|
module_exit(fscache_exit);
|
|
|
|
/*
|
|
* wait_on_atomic_t() sleep function for uninterruptible waiting
|
|
*/
|
|
int fscache_wait_atomic_t(atomic_t *p)
|
|
{
|
|
schedule();
|
|
return 0;
|
|
}
|