Skip to content

Commit

Permalink
NFSv4: Ensure that file unlock requests don't conflict with state rec…
Browse files Browse the repository at this point in the history
…overy

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>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Dec 23, 2008
1 parent 65de872 commit 19e03c5
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 10 deletions.
26 changes: 16 additions & 10 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3273,6 +3273,8 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,

static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs_client *clp = state->owner->so_client;
struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_seqid *seqid;
struct nfs4_lock_state *lsp;
struct rpc_task *task;
Expand All @@ -3282,8 +3284,15 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
status = nfs4_set_lock_state(state, request);
/* Unlock _before_ we do the RPC call */
request->fl_flags |= FL_EXISTS;
if (do_vfs_lock(request->fl_file, request) == -ENOENT)
down_read(&clp->cl_sem);
down_read(&nfsi->rwsem);
if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
goto out;
}
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
if (status != 0)
goto out;
/* Is this a delegated lock? */
Expand Down Expand Up @@ -3510,6 +3519,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs_client *clp = state->owner->so_client;
struct nfs_inode *nfsi = NFS_I(state->inode);
unsigned char fl_flags = request->fl_flags;
int status;

Expand All @@ -3522,18 +3532,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
if (status < 0)
goto out;
down_read(&clp->cl_sem);
down_read(&nfsi->rwsem);
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
struct nfs_inode *nfsi = NFS_I(state->inode);
/* Yes: cache locks! */
down_read(&nfsi->rwsem);
/* ...but avoid races with delegation recall... */
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
request->fl_flags = fl_flags & ~FL_SLEEP;
status = do_vfs_lock(request->fl_file, request);
up_read(&nfsi->rwsem);
goto out_unlock;
}
up_read(&nfsi->rwsem);
request->fl_flags = fl_flags & ~FL_SLEEP;
status = do_vfs_lock(request->fl_file, request);
goto out_unlock;
}
status = _nfs4_do_setlk(state, cmd, request, 0);
if (status != 0)
Expand All @@ -3543,6 +3548,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
if (do_vfs_lock(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
out_unlock:
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
out:
request->fl_flags = fl_flags;
Expand Down
4 changes: 4 additions & 0 deletions fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -849,9 +849,11 @@ static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_s
static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
{
struct inode *inode = state->inode;
struct nfs_inode *nfsi = NFS_I(inode);
struct file_lock *fl;
int status = 0;

down_write(&nfsi->rwsem);
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
continue;
Expand All @@ -874,8 +876,10 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
goto out_err;
}
}
up_write(&nfsi->rwsem);
return 0;
out_err:
up_write(&nfsi->rwsem);
return status;
}

Expand Down

0 comments on commit 19e03c5

Please sign in to comment.