aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2013-06-21 08:58:22 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-07-08 05:36:42 -0400
commit7012b02a2b2c42bb1e1d95040a6e3bb59c7284f7 (patch)
tree6a1c54cdca608ad75db6282cc4c0c7a6bb5f7673
parent0bc77381c1b1600e659eb7322c39d1753615722d (diff)
locks: move file_lock_list to a set of percpu hlist_heads and convert file_lock_lock to an lglock
The file_lock_list is only used for /proc/locks. The vastly common case is for locks to be put onto the list and come off again, without ever being traversed. Help optimize for this use-case by moving to percpu hlist_head-s. At the same time, we can make the locking less contentious by moving to an lglock. When iterating over the lists for /proc/locks, we must take the global lock and then iterate over each CPU's list in turn. This change necessitates a new fl_link_cpu field to keep track of which CPU the entry is on. On x86_64 at least, this field is placed within an existing hole in the struct to avoid growing the size. Signed-off-by: Jeff Layton <jlayton@redhat.com> Acked-by: J. Bruce Fields <bfields@fieldses.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/locks.c69
-rw-r--r--include/linux/fs.h1
2 files changed, 50 insertions, 20 deletions
diff --git a/fs/locks.c b/fs/locks.c
index c98e1a1431ea..b27a3005d78d 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -127,6 +127,8 @@
127#include <linux/rcupdate.h> 127#include <linux/rcupdate.h>
128#include <linux/pid_namespace.h> 128#include <linux/pid_namespace.h>
129#include <linux/hashtable.h> 129#include <linux/hashtable.h>
130#include <linux/percpu.h>
131#include <linux/lglock.h>
130 132
131#include <asm/uaccess.h> 133#include <asm/uaccess.h>
132 134
@@ -155,11 +157,13 @@ int lease_break_time = 45;
155 for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) 157 for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next)
156 158
157/* 159/*
158 * The global file_lock_list is only used for displaying /proc/locks. Protected 160 * The global file_lock_list is only used for displaying /proc/locks, so we
159 * by the file_lock_lock. 161 * keep a list on each CPU, with each list protected by its own spinlock via
162 * the file_lock_lglock. Note that alterations to the list also require that
163 * the relevant i_lock is held.
160 */ 164 */
161static HLIST_HEAD(file_lock_list); 165DEFINE_STATIC_LGLOCK(file_lock_lglock);
162static DEFINE_SPINLOCK(file_lock_lock); 166static DEFINE_PER_CPU(struct hlist_head, file_lock_list);
163 167
164/* 168/*
165 * The blocked_hash is used to find POSIX lock loops for deadlock detection. 169 * The blocked_hash is used to find POSIX lock loops for deadlock detection.
@@ -506,20 +510,30 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
506 return fl1->fl_owner == fl2->fl_owner; 510 return fl1->fl_owner == fl2->fl_owner;
507} 511}
508 512
513/* Must be called with the i_lock held! */
509static inline void 514static inline void
510locks_insert_global_locks(struct file_lock *fl) 515locks_insert_global_locks(struct file_lock *fl)
511{ 516{
512 spin_lock(&file_lock_lock); 517 lg_local_lock(&file_lock_lglock);
513 hlist_add_head(&fl->fl_link, &file_lock_list); 518 fl->fl_link_cpu = smp_processor_id();
514 spin_unlock(&file_lock_lock); 519 hlist_add_head(&fl->fl_link, this_cpu_ptr(&file_lock_list));
520 lg_local_unlock(&file_lock_lglock);
515} 521}
516 522
523/* Must be called with the i_lock held! */
517static inline void 524static inline void
518locks_delete_global_locks(struct file_lock *fl) 525locks_delete_global_locks(struct file_lock *fl)
519{ 526{
520 spin_lock(&file_lock_lock); 527 /*
528 * Avoid taking lock if already unhashed. This is safe since this check
529 * is done while holding the i_lock, and new insertions into the list
530 * also require that it be held.
531 */
532 if (hlist_unhashed(&fl->fl_link))
533 return;
534 lg_local_lock_cpu(&file_lock_lglock, fl->fl_link_cpu);
521 hlist_del_init(&fl->fl_link); 535 hlist_del_init(&fl->fl_link);
522 spin_unlock(&file_lock_lock); 536 lg_local_unlock_cpu(&file_lock_lglock, fl->fl_link_cpu);
523} 537}
524 538
525static unsigned long 539static unsigned long
@@ -2243,6 +2257,11 @@ EXPORT_SYMBOL_GPL(vfs_cancel_lock);
2243#include <linux/proc_fs.h> 2257#include <linux/proc_fs.h>
2244#include <linux/seq_file.h> 2258#include <linux/seq_file.h>
2245 2259
2260struct locks_iterator {
2261 int li_cpu;
2262 loff_t li_pos;
2263};
2264
2246static void lock_get_status(struct seq_file *f, struct file_lock *fl, 2265static void lock_get_status(struct seq_file *f, struct file_lock *fl,
2247 loff_t id, char *pfx) 2266 loff_t id, char *pfx)
2248{ 2267{
@@ -2316,39 +2335,41 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
2316 2335
2317static int locks_show(struct seq_file *f, void *v) 2336static int locks_show(struct seq_file *f, void *v)
2318{ 2337{
2338 struct locks_iterator *iter = f->private;
2319 struct file_lock *fl, *bfl; 2339 struct file_lock *fl, *bfl;
2320 2340
2321 fl = hlist_entry(v, struct file_lock, fl_link); 2341 fl = hlist_entry(v, struct file_lock, fl_link);
2322 2342
2323 lock_get_status(f, fl, *((loff_t *)f->private), ""); 2343 lock_get_status(f, fl, iter->li_pos, "");
2324 2344
2325 list_for_each_entry(bfl, &fl->fl_block, fl_block) 2345 list_for_each_entry(bfl, &fl->fl_block, fl_block)
2326 lock_get_status(f, bfl, *((loff_t *)f->private), " ->"); 2346 lock_get_status(f, bfl, iter->li_pos, " ->");
2327 2347
2328 return 0; 2348 return 0;
2329} 2349}
2330 2350
2331static void *locks_start(struct seq_file *f, loff_t *pos) 2351static void *locks_start(struct seq_file *f, loff_t *pos)
2332{ 2352{
2333 loff_t *p = f->private; 2353 struct locks_iterator *iter = f->private;
2334 2354
2335 spin_lock(&file_lock_lock); 2355 iter->li_pos = *pos + 1;
2356 lg_global_lock(&file_lock_lglock);
2336 spin_lock(&blocked_lock_lock); 2357 spin_lock(&blocked_lock_lock);
2337 *p = (*pos + 1); 2358 return seq_hlist_start_percpu(&file_lock_list, &iter->li_cpu, *pos);
2338 return seq_hlist_start(&file_lock_list, *pos);
2339} 2359}
2340 2360
2341static void *locks_next(struct seq_file *f, void *v, loff_t *pos) 2361static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
2342{ 2362{
2343 loff_t *p = f->private; 2363 struct locks_iterator *iter = f->private;
2344 ++*p; 2364
2345 return seq_hlist_next(v, &file_lock_list, pos); 2365 ++iter->li_pos;
2366 return seq_hlist_next_percpu(v, &file_lock_list, &iter->li_cpu, pos);
2346} 2367}
2347 2368
2348static void locks_stop(struct seq_file *f, void *v) 2369static void locks_stop(struct seq_file *f, void *v)
2349{ 2370{
2350 spin_unlock(&blocked_lock_lock); 2371 spin_unlock(&blocked_lock_lock);
2351 spin_unlock(&file_lock_lock); 2372 lg_global_unlock(&file_lock_lglock);
2352} 2373}
2353 2374
2354static const struct seq_operations locks_seq_operations = { 2375static const struct seq_operations locks_seq_operations = {
@@ -2360,7 +2381,8 @@ static const struct seq_operations locks_seq_operations = {
2360 2381
2361static int locks_open(struct inode *inode, struct file *filp) 2382static int locks_open(struct inode *inode, struct file *filp)
2362{ 2383{
2363 return seq_open_private(filp, &locks_seq_operations, sizeof(loff_t)); 2384 return seq_open_private(filp, &locks_seq_operations,
2385 sizeof(struct locks_iterator));
2364} 2386}
2365 2387
2366static const struct file_operations proc_locks_operations = { 2388static const struct file_operations proc_locks_operations = {
@@ -2460,9 +2482,16 @@ EXPORT_SYMBOL(lock_may_write);
2460 2482
2461static int __init filelock_init(void) 2483static int __init filelock_init(void)
2462{ 2484{
2485 int i;
2486
2463 filelock_cache = kmem_cache_create("file_lock_cache", 2487 filelock_cache = kmem_cache_create("file_lock_cache",
2464 sizeof(struct file_lock), 0, SLAB_PANIC, NULL); 2488 sizeof(struct file_lock), 0, SLAB_PANIC, NULL);
2465 2489
2490 lg_lock_init(&file_lock_lglock, "file_lock_lglock");
2491
2492 for_each_possible_cpu(i)
2493 INIT_HLIST_HEAD(per_cpu_ptr(&file_lock_list, i));
2494
2466 return 0; 2495 return 0;
2467} 2496}
2468 2497
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 99be011e00de..834c9e5113d9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -954,6 +954,7 @@ struct file_lock {
954 unsigned int fl_flags; 954 unsigned int fl_flags;
955 unsigned char fl_type; 955 unsigned char fl_type;
956 unsigned int fl_pid; 956 unsigned int fl_pid;
957 int fl_link_cpu; /* what cpu's list is this on? */
957 struct pid *fl_nspid; 958 struct pid *fl_nspid;
958 wait_queue_head_t fl_wait; 959 wait_queue_head_t fl_wait;
959 struct file *fl_file; 960 struct file *fl_file;