diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-12-23 15:21:44 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-12-23 15:21:44 -0500 |
commit | 19e03c570e6099ffaf24e5628d4fe1a8acbe820d (patch) | |
tree | 6e7eb831bbfe732044105f7f7a477fda5e912b6c /fs/nfs/nfs4proc.c | |
parent | 65de872ed6501a68e918a49a5c2fa7fca9c6ce21 (diff) |
NFSv4: Ensure that file unlock requests don't conflict with state recovery
The unlock path is currently failing to take the nfs_client->cl_sem read
lock, and hence the recovery path may see locks disappear from underneath
it.
Also ensure that it takes the nfs_inode->rwsem read lock so that it there
is no conflict with delegation recalls.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 26 |
1 files changed, 16 insertions, 10 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 660c5dcfb0a5..aec4e47c462d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3273,6 +3273,8 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, | |||
3273 | 3273 | ||
3274 | static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) | 3274 | static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) |
3275 | { | 3275 | { |
3276 | struct nfs_client *clp = state->owner->so_client; | ||
3277 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
3276 | struct nfs_seqid *seqid; | 3278 | struct nfs_seqid *seqid; |
3277 | struct nfs4_lock_state *lsp; | 3279 | struct nfs4_lock_state *lsp; |
3278 | struct rpc_task *task; | 3280 | struct rpc_task *task; |
@@ -3282,8 +3284,15 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * | |||
3282 | status = nfs4_set_lock_state(state, request); | 3284 | status = nfs4_set_lock_state(state, request); |
3283 | /* Unlock _before_ we do the RPC call */ | 3285 | /* Unlock _before_ we do the RPC call */ |
3284 | request->fl_flags |= FL_EXISTS; | 3286 | request->fl_flags |= FL_EXISTS; |
3285 | if (do_vfs_lock(request->fl_file, request) == -ENOENT) | 3287 | down_read(&clp->cl_sem); |
3288 | down_read(&nfsi->rwsem); | ||
3289 | if (do_vfs_lock(request->fl_file, request) == -ENOENT) { | ||
3290 | up_read(&nfsi->rwsem); | ||
3291 | up_read(&clp->cl_sem); | ||
3286 | goto out; | 3292 | goto out; |
3293 | } | ||
3294 | up_read(&nfsi->rwsem); | ||
3295 | up_read(&clp->cl_sem); | ||
3287 | if (status != 0) | 3296 | if (status != 0) |
3288 | goto out; | 3297 | goto out; |
3289 | /* Is this a delegated lock? */ | 3298 | /* Is this a delegated lock? */ |
@@ -3510,6 +3519,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request | |||
3510 | static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) | 3519 | static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) |
3511 | { | 3520 | { |
3512 | struct nfs_client *clp = state->owner->so_client; | 3521 | struct nfs_client *clp = state->owner->so_client; |
3522 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
3513 | unsigned char fl_flags = request->fl_flags; | 3523 | unsigned char fl_flags = request->fl_flags; |
3514 | int status; | 3524 | int status; |
3515 | 3525 | ||
@@ -3522,18 +3532,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock | |||
3522 | if (status < 0) | 3532 | if (status < 0) |
3523 | goto out; | 3533 | goto out; |
3524 | down_read(&clp->cl_sem); | 3534 | down_read(&clp->cl_sem); |
3535 | down_read(&nfsi->rwsem); | ||
3525 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | 3536 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { |
3526 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
3527 | /* Yes: cache locks! */ | 3537 | /* Yes: cache locks! */ |
3528 | down_read(&nfsi->rwsem); | ||
3529 | /* ...but avoid races with delegation recall... */ | 3538 | /* ...but avoid races with delegation recall... */ |
3530 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | 3539 | request->fl_flags = fl_flags & ~FL_SLEEP; |
3531 | request->fl_flags = fl_flags & ~FL_SLEEP; | 3540 | status = do_vfs_lock(request->fl_file, request); |
3532 | status = do_vfs_lock(request->fl_file, request); | 3541 | goto out_unlock; |
3533 | up_read(&nfsi->rwsem); | ||
3534 | goto out_unlock; | ||
3535 | } | ||
3536 | up_read(&nfsi->rwsem); | ||
3537 | } | 3542 | } |
3538 | status = _nfs4_do_setlk(state, cmd, request, 0); | 3543 | status = _nfs4_do_setlk(state, cmd, request, 0); |
3539 | if (status != 0) | 3544 | if (status != 0) |
@@ -3543,6 +3548,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock | |||
3543 | if (do_vfs_lock(request->fl_file, request) < 0) | 3548 | if (do_vfs_lock(request->fl_file, request) < 0) |
3544 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__); | 3549 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__); |
3545 | out_unlock: | 3550 | out_unlock: |
3551 | up_read(&nfsi->rwsem); | ||
3546 | up_read(&clp->cl_sem); | 3552 | up_read(&clp->cl_sem); |
3547 | out: | 3553 | out: |
3548 | request->fl_flags = fl_flags; | 3554 | request->fl_flags = fl_flags; |