aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2016-10-20 09:34:31 -0400
committerJ. Bruce Fields <bfields@redhat.com>2016-10-24 16:51:21 -0400
commit0cc11a61b80a1ab1d12f1597b27b8b45ef8bac4a (patch)
tree391b3b25eb0ac4780c5498f76fef1927f59fe907 /fs
parent07d9a380680d1c0eb51ef87ff2eab5c994949e69 (diff)
nfsd: move blocked lock handling under a dedicated spinlock
Bruce was hitting some lockdep warnings in testing, showing that we could hit a deadlock with the new CB_NOTIFY_LOCK handling, involving a rather complex situation involving four different spinlocks. The crux of the matter is that we end up taking the nn->client_lock in the lm_notify handler. The simplest fix is to just declare a new per-nfsd_net spinlock to protect the new CB_NOTIFY_LOCK structures. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfsd/netns.h5
-rw-r--r--fs/nfsd/nfs4state.c28
2 files changed, 20 insertions, 13 deletions
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index b10d557f9c9e..ee36efd5aece 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -84,6 +84,8 @@ struct nfsd_net {
84 struct list_head client_lru; 84 struct list_head client_lru;
85 struct list_head close_lru; 85 struct list_head close_lru;
86 struct list_head del_recall_lru; 86 struct list_head del_recall_lru;
87
88 /* protected by blocked_locks_lock */
87 struct list_head blocked_locks_lru; 89 struct list_head blocked_locks_lru;
88 90
89 struct delayed_work laundromat_work; 91 struct delayed_work laundromat_work;
@@ -91,6 +93,9 @@ struct nfsd_net {
91 /* client_lock protects the client lru list and session hash table */ 93 /* client_lock protects the client lru list and session hash table */
92 spinlock_t client_lock; 94 spinlock_t client_lock;
93 95
96 /* protects blocked_locks_lru */
97 spinlock_t blocked_locks_lock;
98
94 struct file *rec_file; 99 struct file *rec_file;
95 bool in_grace; 100 bool in_grace;
96 const struct nfsd4_client_tracking_ops *client_tracking_ops; 101 const struct nfsd4_client_tracking_ops *client_tracking_ops;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9752beb78659..95474196a17e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -217,7 +217,7 @@ find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
217{ 217{
218 struct nfsd4_blocked_lock *cur, *found = NULL; 218 struct nfsd4_blocked_lock *cur, *found = NULL;
219 219
220 spin_lock(&nn->client_lock); 220 spin_lock(&nn->blocked_locks_lock);
221 list_for_each_entry(cur, &lo->lo_blocked, nbl_list) { 221 list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
222 if (fh_match(fh, &cur->nbl_fh)) { 222 if (fh_match(fh, &cur->nbl_fh)) {
223 list_del_init(&cur->nbl_list); 223 list_del_init(&cur->nbl_list);
@@ -226,7 +226,7 @@ find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
226 break; 226 break;
227 } 227 }
228 } 228 }
229 spin_unlock(&nn->client_lock); 229 spin_unlock(&nn->blocked_locks_lock);
230 if (found) 230 if (found)
231 posix_unblock_lock(&found->nbl_lock); 231 posix_unblock_lock(&found->nbl_lock);
232 return found; 232 return found;
@@ -4665,7 +4665,7 @@ nfs4_laundromat(struct nfsd_net *nn)
4665 * indefinitely once the lock does become free. 4665 * indefinitely once the lock does become free.
4666 */ 4666 */
4667 BUG_ON(!list_empty(&reaplist)); 4667 BUG_ON(!list_empty(&reaplist));
4668 spin_lock(&nn->client_lock); 4668 spin_lock(&nn->blocked_locks_lock);
4669 while (!list_empty(&nn->blocked_locks_lru)) { 4669 while (!list_empty(&nn->blocked_locks_lru)) {
4670 nbl = list_first_entry(&nn->blocked_locks_lru, 4670 nbl = list_first_entry(&nn->blocked_locks_lru,
4671 struct nfsd4_blocked_lock, nbl_lru); 4671 struct nfsd4_blocked_lock, nbl_lru);
@@ -4678,7 +4678,7 @@ nfs4_laundromat(struct nfsd_net *nn)
4678 list_move(&nbl->nbl_lru, &reaplist); 4678 list_move(&nbl->nbl_lru, &reaplist);
4679 list_del_init(&nbl->nbl_list); 4679 list_del_init(&nbl->nbl_list);
4680 } 4680 }
4681 spin_unlock(&nn->client_lock); 4681 spin_unlock(&nn->blocked_locks_lock);
4682 4682
4683 while (!list_empty(&reaplist)) { 4683 while (!list_empty(&reaplist)) {
4684 nbl = list_first_entry(&nn->blocked_locks_lru, 4684 nbl = list_first_entry(&nn->blocked_locks_lru,
@@ -5439,13 +5439,13 @@ nfsd4_lm_notify(struct file_lock *fl)
5439 bool queue = false; 5439 bool queue = false;
5440 5440
5441 /* An empty list means that something else is going to be using it */ 5441 /* An empty list means that something else is going to be using it */
5442 spin_lock(&nn->client_lock); 5442 spin_lock(&nn->blocked_locks_lock);
5443 if (!list_empty(&nbl->nbl_list)) { 5443 if (!list_empty(&nbl->nbl_list)) {
5444 list_del_init(&nbl->nbl_list); 5444 list_del_init(&nbl->nbl_list);
5445 list_del_init(&nbl->nbl_lru); 5445 list_del_init(&nbl->nbl_lru);
5446 queue = true; 5446 queue = true;
5447 } 5447 }
5448 spin_unlock(&nn->client_lock); 5448 spin_unlock(&nn->blocked_locks_lock);
5449 5449
5450 if (queue) 5450 if (queue)
5451 nfsd4_run_cb(&nbl->nbl_cb); 5451 nfsd4_run_cb(&nbl->nbl_cb);
@@ -5868,10 +5868,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
5868 5868
5869 if (fl_flags & FL_SLEEP) { 5869 if (fl_flags & FL_SLEEP) {
5870 nbl->nbl_time = jiffies; 5870 nbl->nbl_time = jiffies;
5871 spin_lock(&nn->client_lock); 5871 spin_lock(&nn->blocked_locks_lock);
5872 list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked); 5872 list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
5873 list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru); 5873 list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
5874 spin_unlock(&nn->client_lock); 5874 spin_unlock(&nn->blocked_locks_lock);
5875 } 5875 }
5876 5876
5877 err = vfs_lock_file(filp, F_SETLK, file_lock, conflock); 5877 err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
@@ -5900,10 +5900,10 @@ out:
5900 if (nbl) { 5900 if (nbl) {
5901 /* dequeue it if we queued it before */ 5901 /* dequeue it if we queued it before */
5902 if (fl_flags & FL_SLEEP) { 5902 if (fl_flags & FL_SLEEP) {
5903 spin_lock(&nn->client_lock); 5903 spin_lock(&nn->blocked_locks_lock);
5904 list_del_init(&nbl->nbl_list); 5904 list_del_init(&nbl->nbl_list);
5905 list_del_init(&nbl->nbl_lru); 5905 list_del_init(&nbl->nbl_lru);
5906 spin_unlock(&nn->client_lock); 5906 spin_unlock(&nn->blocked_locks_lock);
5907 } 5907 }
5908 free_blocked_lock(nbl); 5908 free_blocked_lock(nbl);
5909 } 5909 }
@@ -6943,9 +6943,11 @@ static int nfs4_state_create_net(struct net *net)
6943 INIT_LIST_HEAD(&nn->client_lru); 6943 INIT_LIST_HEAD(&nn->client_lru);
6944 INIT_LIST_HEAD(&nn->close_lru); 6944 INIT_LIST_HEAD(&nn->close_lru);
6945 INIT_LIST_HEAD(&nn->del_recall_lru); 6945 INIT_LIST_HEAD(&nn->del_recall_lru);
6946 INIT_LIST_HEAD(&nn->blocked_locks_lru);
6947 spin_lock_init(&nn->client_lock); 6946 spin_lock_init(&nn->client_lock);
6948 6947
6948 spin_lock_init(&nn->blocked_locks_lock);
6949 INIT_LIST_HEAD(&nn->blocked_locks_lru);
6950
6949 INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); 6951 INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
6950 get_net(net); 6952 get_net(net);
6951 6953
@@ -7063,14 +7065,14 @@ nfs4_state_shutdown_net(struct net *net)
7063 } 7065 }
7064 7066
7065 BUG_ON(!list_empty(&reaplist)); 7067 BUG_ON(!list_empty(&reaplist));
7066 spin_lock(&nn->client_lock); 7068 spin_lock(&nn->blocked_locks_lock);
7067 while (!list_empty(&nn->blocked_locks_lru)) { 7069 while (!list_empty(&nn->blocked_locks_lru)) {
7068 nbl = list_first_entry(&nn->blocked_locks_lru, 7070 nbl = list_first_entry(&nn->blocked_locks_lru,
7069 struct nfsd4_blocked_lock, nbl_lru); 7071 struct nfsd4_blocked_lock, nbl_lru);
7070 list_move(&nbl->nbl_lru, &reaplist); 7072 list_move(&nbl->nbl_lru, &reaplist);
7071 list_del_init(&nbl->nbl_list); 7073 list_del_init(&nbl->nbl_list);
7072 } 7074 }
7073 spin_unlock(&nn->client_lock); 7075 spin_unlock(&nn->blocked_locks_lock);
7074 7076
7075 while (!list_empty(&reaplist)) { 7077 while (!list_empty(&reaplist)) {
7076 nbl = list_first_entry(&nn->blocked_locks_lru, 7078 nbl = list_first_entry(&nn->blocked_locks_lru,