diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-02-07 10:54:07 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-02-11 15:33:13 -0500 |
commit | 65b62a29f719e937b5be1df472287f4c61e53ac6 (patch) | |
tree | ea6b552a08883d92cfedb891044fa9697cbc5619 /fs | |
parent | 37380e4264dbda9753e470a30d4322097aab7152 (diff) |
NFSv4: Ensure delegation recall and byte range lock removal don't conflict
Add a mutex to the struct nfs4_state_owner to ensure that delegation
recall doesn't conflict with byte range lock removal.
Note that we nest the new mutex _outside_ the state manager reclaim
protection (nfsi->rwsem) in order to avoid deadlocks.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/delegation.c | 7 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 1 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 9 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 1 |
4 files changed, 16 insertions, 2 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 2542cdaa1116..6390a4b5fee7 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -71,8 +71,10 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ | |||
71 | int status = 0; | 71 | int status = 0; |
72 | 72 | ||
73 | if (inode->i_flock == NULL) | 73 | if (inode->i_flock == NULL) |
74 | goto out; | 74 | return 0; |
75 | 75 | ||
76 | if (inode->i_flock == NULL) | ||
77 | goto out; | ||
76 | /* Protect inode->i_flock using the file locks lock */ | 78 | /* Protect inode->i_flock using the file locks lock */ |
77 | lock_flocks(); | 79 | lock_flocks(); |
78 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | 80 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { |
@@ -113,12 +115,15 @@ again: | |||
113 | get_nfs_open_context(ctx); | 115 | get_nfs_open_context(ctx); |
114 | spin_unlock(&inode->i_lock); | 116 | spin_unlock(&inode->i_lock); |
115 | sp = state->owner; | 117 | sp = state->owner; |
118 | /* Block nfs4_proc_unlck */ | ||
119 | mutex_lock(&sp->so_delegreturn_mutex); | ||
116 | seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); | 120 | seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); |
117 | err = nfs4_open_delegation_recall(ctx, state, stateid); | 121 | err = nfs4_open_delegation_recall(ctx, state, stateid); |
118 | if (!err) | 122 | if (!err) |
119 | err = nfs_delegation_claim_locks(ctx, state); | 123 | err = nfs_delegation_claim_locks(ctx, state); |
120 | if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) | 124 | if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) |
121 | err = -EAGAIN; | 125 | err = -EAGAIN; |
126 | mutex_unlock(&sp->so_delegreturn_mutex); | ||
122 | put_nfs_open_context(ctx); | 127 | put_nfs_open_context(ctx); |
123 | if (err != 0) | 128 | if (err != 0) |
124 | return err; | 129 | return err; |
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index b12b73472020..944c9a5c1039 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -93,6 +93,7 @@ struct nfs4_state_owner { | |||
93 | struct list_head so_states; | 93 | struct list_head so_states; |
94 | struct nfs_seqid_counter so_seqid; | 94 | struct nfs_seqid_counter so_seqid; |
95 | seqcount_t so_reclaim_seqcount; | 95 | seqcount_t so_reclaim_seqcount; |
96 | struct mutex so_delegreturn_mutex; | ||
96 | }; | 97 | }; |
97 | 98 | ||
98 | enum { | 99 | enum { |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f7e05ade5572..d51227371c67 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -4485,7 +4485,9 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, | |||
4485 | 4485 | ||
4486 | static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) | 4486 | static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) |
4487 | { | 4487 | { |
4488 | struct nfs_inode *nfsi = NFS_I(state->inode); | 4488 | struct inode *inode = state->inode; |
4489 | struct nfs4_state_owner *sp = state->owner; | ||
4490 | struct nfs_inode *nfsi = NFS_I(inode); | ||
4489 | struct nfs_seqid *seqid; | 4491 | struct nfs_seqid *seqid; |
4490 | struct nfs4_lock_state *lsp; | 4492 | struct nfs4_lock_state *lsp; |
4491 | struct rpc_task *task; | 4493 | struct rpc_task *task; |
@@ -4495,12 +4497,17 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * | |||
4495 | status = nfs4_set_lock_state(state, request); | 4497 | status = nfs4_set_lock_state(state, request); |
4496 | /* Unlock _before_ we do the RPC call */ | 4498 | /* Unlock _before_ we do the RPC call */ |
4497 | request->fl_flags |= FL_EXISTS; | 4499 | request->fl_flags |= FL_EXISTS; |
4500 | /* Exclude nfs_delegation_claim_locks() */ | ||
4501 | mutex_lock(&sp->so_delegreturn_mutex); | ||
4502 | /* Exclude nfs4_reclaim_open_stateid() - note nesting! */ | ||
4498 | down_read(&nfsi->rwsem); | 4503 | down_read(&nfsi->rwsem); |
4499 | if (do_vfs_lock(request->fl_file, request) == -ENOENT) { | 4504 | if (do_vfs_lock(request->fl_file, request) == -ENOENT) { |
4500 | up_read(&nfsi->rwsem); | 4505 | up_read(&nfsi->rwsem); |
4506 | mutex_unlock(&sp->so_delegreturn_mutex); | ||
4501 | goto out; | 4507 | goto out; |
4502 | } | 4508 | } |
4503 | up_read(&nfsi->rwsem); | 4509 | up_read(&nfsi->rwsem); |
4510 | mutex_unlock(&sp->so_delegreturn_mutex); | ||
4504 | if (status != 0) | 4511 | if (status != 0) |
4505 | goto out; | 4512 | goto out; |
4506 | /* Is this a delegated lock? */ | 4513 | /* Is this a delegated lock? */ |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index fff97228cdec..6ace365c6334 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -519,6 +519,7 @@ nfs4_alloc_state_owner(struct nfs_server *server, | |||
519 | atomic_set(&sp->so_count, 1); | 519 | atomic_set(&sp->so_count, 1); |
520 | INIT_LIST_HEAD(&sp->so_lru); | 520 | INIT_LIST_HEAD(&sp->so_lru); |
521 | seqcount_init(&sp->so_reclaim_seqcount); | 521 | seqcount_init(&sp->so_reclaim_seqcount); |
522 | mutex_init(&sp->so_delegreturn_mutex); | ||
522 | return sp; | 523 | return sp; |
523 | } | 524 | } |
524 | 525 | ||