summaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4proc.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-11-04 21:32:58 +0100
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-11-04 21:32:58 +0100
commit4cecb76ff86db46d2823550256c828b6597f418e (patch)
treedb80c6f4423d2559661234e8d1cd34b2c15f7750 /fs/nfs/nfs4proc.c
parent[USB]: Make early handoff a final fixup instead of a header one. (diff)
downloadlinux-4cecb76ff86db46d2823550256c828b6597f418e.tar.xz
linux-4cecb76ff86db46d2823550256c828b6597f418e.zip
NFSv4: Fix a race between open() and close()
We must not remove the nfs4_state structure from the inode open lists before we are in sequence lock. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r--fs/nfs/nfs4proc.c35
1 files changed, 17 insertions, 18 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 933e13b383f8..02fddd0e27e8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -217,13 +217,12 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
/* Protect against nfs4_find_state() */
spin_lock(&state->owner->so_lock);
spin_lock(&inode->i_lock);
- state->state |= open_flags;
- /* NB! List reordering - see the reclaim code for why. */
- if ((open_flags & FMODE_WRITE) && 0 == state->nwriters++)
- list_move(&state->open_states, &state->owner->so_states);
+ memcpy(&state->stateid, stateid, sizeof(state->stateid));
+ if ((open_flags & FMODE_WRITE))
+ state->nwriters++;
if (open_flags & FMODE_READ)
state->nreaders++;
- memcpy(&state->stateid, stateid, sizeof(state->stateid));
+ nfs4_state_set_mode_locked(state, state->state | open_flags);
spin_unlock(&inode->i_lock);
spin_unlock(&state->owner->so_lock);
}
@@ -896,7 +895,6 @@ static void nfs4_close_done(struct rpc_task *task)
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- state->state = calldata->arg.open_flags;
nfs4_schedule_state_recovery(server->nfs4_state);
break;
default:
@@ -906,7 +904,6 @@ static void nfs4_close_done(struct rpc_task *task)
}
}
nfs_refresh_inode(calldata->inode, calldata->res.fattr);
- state->state = calldata->arg.open_flags;
nfs4_free_closedata(calldata);
}
@@ -920,24 +917,26 @@ static void nfs4_close_begin(struct rpc_task *task)
.rpc_resp = &calldata->res,
.rpc_cred = state->owner->so_cred,
};
- int mode = 0;
+ int mode = 0, old_mode;
int status;
status = nfs_wait_on_sequence(calldata->arg.seqid, task);
if (status != 0)
return;
- /* Don't reorder reads */
- smp_rmb();
/* Recalculate the new open mode in case someone reopened the file
* while we were waiting in line to be scheduled.
*/
- if (state->nreaders != 0)
- mode |= FMODE_READ;
- if (state->nwriters != 0)
- mode |= FMODE_WRITE;
- if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- state->state = mode;
- if (mode == state->state) {
+ spin_lock(&state->owner->so_lock);
+ spin_lock(&calldata->inode->i_lock);
+ mode = old_mode = state->state;
+ if (state->nreaders == 0)
+ mode &= ~FMODE_READ;
+ if (state->nwriters == 0)
+ mode &= ~FMODE_WRITE;
+ nfs4_state_set_mode_locked(state, mode);
+ spin_unlock(&calldata->inode->i_lock);
+ spin_unlock(&state->owner->so_lock);
+ if (mode == old_mode || test_bit(NFS_DELEGATED_STATE, &state->flags)) {
nfs4_free_closedata(calldata);
task->tk_exit = NULL;
rpc_exit(task, 0);
@@ -961,7 +960,7 @@ static void nfs4_close_begin(struct rpc_task *task)
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
-int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
+int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_closedata *calldata;