diff options
author | Jeff Layton <jlayton@redhat.com> | 2013-06-21 08:58:20 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-06-29 04:57:46 -0400 |
commit | 7b2296afb392bc21a50f42e7c7f4b19d3fea8c6d (patch) | |
tree | 360a8f35cf75f0bbcd1b984a6348f4c9e715e159 /fs/locks.c | |
parent | 3999e49364193f7dbbba66e2be655fe91ba1fced (diff) |
locks: give the blocked_hash its own spinlock
There's no reason we have to protect the blocked_hash and file_lock_list
with the same spinlock. With the tests I have, breaking it in two gives
a barely measurable performance benefit, but it seems reasonable to make
this locking as granular as possible.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/locks.c')
-rw-r--r-- | fs/locks.c | 41 |
1 files changed, 22 insertions, 19 deletions
diff --git a/fs/locks.c b/fs/locks.c index 6242e0b1c69c..04e2c1fdb157 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -159,10 +159,11 @@ int lease_break_time = 45; | |||
159 | * by the file_lock_lock. | 159 | * by the file_lock_lock. |
160 | */ | 160 | */ |
161 | static HLIST_HEAD(file_lock_list); | 161 | static HLIST_HEAD(file_lock_list); |
162 | static DEFINE_SPINLOCK(file_lock_lock); | ||
162 | 163 | ||
163 | /* | 164 | /* |
164 | * The blocked_hash is used to find POSIX lock loops for deadlock detection. | 165 | * The blocked_hash is used to find POSIX lock loops for deadlock detection. |
165 | * It is protected by file_lock_lock. | 166 | * It is protected by blocked_lock_lock. |
166 | * | 167 | * |
167 | * We hash locks by lockowner in order to optimize searching for the lock a | 168 | * We hash locks by lockowner in order to optimize searching for the lock a |
168 | * particular lockowner is waiting on. | 169 | * particular lockowner is waiting on. |
@@ -175,8 +176,8 @@ static HLIST_HEAD(file_lock_list); | |||
175 | static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS); | 176 | static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS); |
176 | 177 | ||
177 | /* | 178 | /* |
178 | * This lock protects the blocked_hash and the file_lock_list. Generally, if | 179 | * This lock protects the blocked_hash. Generally, if you're accessing it, you |
179 | * you're accessing one of those lists, you want to be holding this lock. | 180 | * want to be holding this lock. |
180 | * | 181 | * |
181 | * In addition, it also protects the fl->fl_block list, and the fl->fl_next | 182 | * In addition, it also protects the fl->fl_block list, and the fl->fl_next |
182 | * pointer for file_lock structures that are acting as lock requests (in | 183 | * pointer for file_lock structures that are acting as lock requests (in |
@@ -191,7 +192,7 @@ static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS); | |||
191 | * both the i_lock and the blocked_lock_lock (acquired in that order). Deleting | 192 | * both the i_lock and the blocked_lock_lock (acquired in that order). Deleting |
192 | * an entry from the list however only requires the file_lock_lock. | 193 | * an entry from the list however only requires the file_lock_lock. |
193 | */ | 194 | */ |
194 | static DEFINE_SPINLOCK(file_lock_lock); | 195 | static DEFINE_SPINLOCK(blocked_lock_lock); |
195 | 196 | ||
196 | static struct kmem_cache *filelock_cache __read_mostly; | 197 | static struct kmem_cache *filelock_cache __read_mostly; |
197 | 198 | ||
@@ -544,7 +545,7 @@ locks_delete_global_blocked(struct file_lock *waiter) | |||
544 | /* Remove waiter from blocker's block list. | 545 | /* Remove waiter from blocker's block list. |
545 | * When blocker ends up pointing to itself then the list is empty. | 546 | * When blocker ends up pointing to itself then the list is empty. |
546 | * | 547 | * |
547 | * Must be called with file_lock_lock held. | 548 | * Must be called with blocked_lock_lock held. |
548 | */ | 549 | */ |
549 | static void __locks_delete_block(struct file_lock *waiter) | 550 | static void __locks_delete_block(struct file_lock *waiter) |
550 | { | 551 | { |
@@ -555,9 +556,9 @@ static void __locks_delete_block(struct file_lock *waiter) | |||
555 | 556 | ||
556 | static void locks_delete_block(struct file_lock *waiter) | 557 | static void locks_delete_block(struct file_lock *waiter) |
557 | { | 558 | { |
558 | spin_lock(&file_lock_lock); | 559 | spin_lock(&blocked_lock_lock); |
559 | __locks_delete_block(waiter); | 560 | __locks_delete_block(waiter); |
560 | spin_unlock(&file_lock_lock); | 561 | spin_unlock(&blocked_lock_lock); |
561 | } | 562 | } |
562 | 563 | ||
563 | /* Insert waiter into blocker's block list. | 564 | /* Insert waiter into blocker's block list. |
@@ -565,9 +566,9 @@ static void locks_delete_block(struct file_lock *waiter) | |||
565 | * the order they blocked. The documentation doesn't require this but | 566 | * the order they blocked. The documentation doesn't require this but |
566 | * it seems like the reasonable thing to do. | 567 | * it seems like the reasonable thing to do. |
567 | * | 568 | * |
568 | * Must be called with both the i_lock and file_lock_lock held. The fl_block | 569 | * Must be called with both the i_lock and blocked_lock_lock held. The fl_block |
569 | * list itself is protected by the file_lock_list, but by ensuring that the | 570 | * list itself is protected by the file_lock_list, but by ensuring that the |
570 | * i_lock is also held on insertions we can avoid taking the file_lock_lock | 571 | * i_lock is also held on insertions we can avoid taking the blocked_lock_lock |
571 | * in some cases when we see that the fl_block list is empty. | 572 | * in some cases when we see that the fl_block list is empty. |
572 | */ | 573 | */ |
573 | static void __locks_insert_block(struct file_lock *blocker, | 574 | static void __locks_insert_block(struct file_lock *blocker, |
@@ -584,9 +585,9 @@ static void __locks_insert_block(struct file_lock *blocker, | |||
584 | static void locks_insert_block(struct file_lock *blocker, | 585 | static void locks_insert_block(struct file_lock *blocker, |
585 | struct file_lock *waiter) | 586 | struct file_lock *waiter) |
586 | { | 587 | { |
587 | spin_lock(&file_lock_lock); | 588 | spin_lock(&blocked_lock_lock); |
588 | __locks_insert_block(blocker, waiter); | 589 | __locks_insert_block(blocker, waiter); |
589 | spin_unlock(&file_lock_lock); | 590 | spin_unlock(&blocked_lock_lock); |
590 | } | 591 | } |
591 | 592 | ||
592 | /* | 593 | /* |
@@ -601,12 +602,12 @@ static void locks_wake_up_blocks(struct file_lock *blocker) | |||
601 | * blocked requests are only added to the list under the i_lock, and | 602 | * blocked requests are only added to the list under the i_lock, and |
602 | * the i_lock is always held here. Note that removal from the fl_block | 603 | * the i_lock is always held here. Note that removal from the fl_block |
603 | * list does not require the i_lock, so we must recheck list_empty() | 604 | * list does not require the i_lock, so we must recheck list_empty() |
604 | * after acquiring the file_lock_lock. | 605 | * after acquiring the blocked_lock_lock. |
605 | */ | 606 | */ |
606 | if (list_empty(&blocker->fl_block)) | 607 | if (list_empty(&blocker->fl_block)) |
607 | return; | 608 | return; |
608 | 609 | ||
609 | spin_lock(&file_lock_lock); | 610 | spin_lock(&blocked_lock_lock); |
610 | while (!list_empty(&blocker->fl_block)) { | 611 | while (!list_empty(&blocker->fl_block)) { |
611 | struct file_lock *waiter; | 612 | struct file_lock *waiter; |
612 | 613 | ||
@@ -618,7 +619,7 @@ static void locks_wake_up_blocks(struct file_lock *blocker) | |||
618 | else | 619 | else |
619 | wake_up(&waiter->fl_wait); | 620 | wake_up(&waiter->fl_wait); |
620 | } | 621 | } |
621 | spin_unlock(&file_lock_lock); | 622 | spin_unlock(&blocked_lock_lock); |
622 | } | 623 | } |
623 | 624 | ||
624 | /* Insert file lock fl into an inode's lock list at the position indicated | 625 | /* Insert file lock fl into an inode's lock list at the position indicated |
@@ -772,7 +773,7 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl) | |||
772 | return NULL; | 773 | return NULL; |
773 | } | 774 | } |
774 | 775 | ||
775 | /* Must be called with the file_lock_lock held! */ | 776 | /* Must be called with the blocked_lock_lock held! */ |
776 | static int posix_locks_deadlock(struct file_lock *caller_fl, | 777 | static int posix_locks_deadlock(struct file_lock *caller_fl, |
777 | struct file_lock *block_fl) | 778 | struct file_lock *block_fl) |
778 | { | 779 | { |
@@ -920,12 +921,12 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str | |||
920 | * locks list must be done while holding the same lock! | 921 | * locks list must be done while holding the same lock! |
921 | */ | 922 | */ |
922 | error = -EDEADLK; | 923 | error = -EDEADLK; |
923 | spin_lock(&file_lock_lock); | 924 | spin_lock(&blocked_lock_lock); |
924 | if (likely(!posix_locks_deadlock(request, fl))) { | 925 | if (likely(!posix_locks_deadlock(request, fl))) { |
925 | error = FILE_LOCK_DEFERRED; | 926 | error = FILE_LOCK_DEFERRED; |
926 | __locks_insert_block(fl, request); | 927 | __locks_insert_block(fl, request); |
927 | } | 928 | } |
928 | spin_unlock(&file_lock_lock); | 929 | spin_unlock(&blocked_lock_lock); |
929 | goto out; | 930 | goto out; |
930 | } | 931 | } |
931 | } | 932 | } |
@@ -2212,12 +2213,12 @@ posix_unblock_lock(struct file_lock *waiter) | |||
2212 | { | 2213 | { |
2213 | int status = 0; | 2214 | int status = 0; |
2214 | 2215 | ||
2215 | spin_lock(&file_lock_lock); | 2216 | spin_lock(&blocked_lock_lock); |
2216 | if (waiter->fl_next) | 2217 | if (waiter->fl_next) |
2217 | __locks_delete_block(waiter); | 2218 | __locks_delete_block(waiter); |
2218 | else | 2219 | else |
2219 | status = -ENOENT; | 2220 | status = -ENOENT; |
2220 | spin_unlock(&file_lock_lock); | 2221 | spin_unlock(&blocked_lock_lock); |
2221 | return status; | 2222 | return status; |
2222 | } | 2223 | } |
2223 | EXPORT_SYMBOL(posix_unblock_lock); | 2224 | EXPORT_SYMBOL(posix_unblock_lock); |
@@ -2332,6 +2333,7 @@ static void *locks_start(struct seq_file *f, loff_t *pos) | |||
2332 | loff_t *p = f->private; | 2333 | loff_t *p = f->private; |
2333 | 2334 | ||
2334 | spin_lock(&file_lock_lock); | 2335 | spin_lock(&file_lock_lock); |
2336 | spin_lock(&blocked_lock_lock); | ||
2335 | *p = (*pos + 1); | 2337 | *p = (*pos + 1); |
2336 | return seq_hlist_start(&file_lock_list, *pos); | 2338 | return seq_hlist_start(&file_lock_list, *pos); |
2337 | } | 2339 | } |
@@ -2345,6 +2347,7 @@ static void *locks_next(struct seq_file *f, void *v, loff_t *pos) | |||
2345 | 2347 | ||
2346 | static void locks_stop(struct seq_file *f, void *v) | 2348 | static void locks_stop(struct seq_file *f, void *v) |
2347 | { | 2349 | { |
2350 | spin_unlock(&blocked_lock_lock); | ||
2348 | spin_unlock(&file_lock_lock); | 2351 | spin_unlock(&file_lock_lock); |
2349 | } | 2352 | } |
2350 | 2353 | ||