diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-11-04 15:32:58 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-11-04 15:32:58 -0500 |
commit | 4cecb76ff86db46d2823550256c828b6597f418e (patch) | |
tree | db80c6f4423d2559661234e8d1cd34b2c15f7750 /fs/nfs/nfs4proc.c | |
parent | 462aae65f6ea41de05ebc2a54a9e6b95563591f3 (diff) |
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.c | 35 |
1 files changed, 17 insertions, 18 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 933e13b383f..02fddd0e27e 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, | |||
217 | /* Protect against nfs4_find_state() */ | 217 | /* Protect against nfs4_find_state() */ |
218 | spin_lock(&state->owner->so_lock); | 218 | spin_lock(&state->owner->so_lock); |
219 | spin_lock(&inode->i_lock); | 219 | spin_lock(&inode->i_lock); |
220 | state->state |= open_flags; | 220 | memcpy(&state->stateid, stateid, sizeof(state->stateid)); |
221 | /* NB! List reordering - see the reclaim code for why. */ | 221 | if ((open_flags & FMODE_WRITE)) |
222 | if ((open_flags & FMODE_WRITE) && 0 == state->nwriters++) | 222 | state->nwriters++; |
223 | list_move(&state->open_states, &state->owner->so_states); | ||
224 | if (open_flags & FMODE_READ) | 223 | if (open_flags & FMODE_READ) |
225 | state->nreaders++; | 224 | state->nreaders++; |
226 | memcpy(&state->stateid, stateid, sizeof(state->stateid)); | 225 | nfs4_state_set_mode_locked(state, state->state | open_flags); |
227 | spin_unlock(&inode->i_lock); | 226 | spin_unlock(&inode->i_lock); |
228 | spin_unlock(&state->owner->so_lock); | 227 | spin_unlock(&state->owner->so_lock); |
229 | } | 228 | } |
@@ -896,7 +895,6 @@ static void nfs4_close_done(struct rpc_task *task) | |||
896 | break; | 895 | break; |
897 | case -NFS4ERR_STALE_STATEID: | 896 | case -NFS4ERR_STALE_STATEID: |
898 | case -NFS4ERR_EXPIRED: | 897 | case -NFS4ERR_EXPIRED: |
899 | state->state = calldata->arg.open_flags; | ||
900 | nfs4_schedule_state_recovery(server->nfs4_state); | 898 | nfs4_schedule_state_recovery(server->nfs4_state); |
901 | break; | 899 | break; |
902 | default: | 900 | default: |
@@ -906,7 +904,6 @@ static void nfs4_close_done(struct rpc_task *task) | |||
906 | } | 904 | } |
907 | } | 905 | } |
908 | nfs_refresh_inode(calldata->inode, calldata->res.fattr); | 906 | nfs_refresh_inode(calldata->inode, calldata->res.fattr); |
909 | state->state = calldata->arg.open_flags; | ||
910 | nfs4_free_closedata(calldata); | 907 | nfs4_free_closedata(calldata); |
911 | } | 908 | } |
912 | 909 | ||
@@ -920,24 +917,26 @@ static void nfs4_close_begin(struct rpc_task *task) | |||
920 | .rpc_resp = &calldata->res, | 917 | .rpc_resp = &calldata->res, |
921 | .rpc_cred = state->owner->so_cred, | 918 | .rpc_cred = state->owner->so_cred, |
922 | }; | 919 | }; |
923 | int mode = 0; | 920 | int mode = 0, old_mode; |
924 | int status; | 921 | int status; |
925 | 922 | ||
926 | status = nfs_wait_on_sequence(calldata->arg.seqid, task); | 923 | status = nfs_wait_on_sequence(calldata->arg.seqid, task); |
927 | if (status != 0) | 924 | if (status != 0) |
928 | return; | 925 | return; |
929 | /* Don't reorder reads */ | ||
930 | smp_rmb(); | ||
931 | /* Recalculate the new open mode in case someone reopened the file | 926 | /* Recalculate the new open mode in case someone reopened the file |
932 | * while we were waiting in line to be scheduled. | 927 | * while we were waiting in line to be scheduled. |
933 | */ | 928 | */ |
934 | if (state->nreaders != 0) | 929 | spin_lock(&state->owner->so_lock); |
935 | mode |= FMODE_READ; | 930 | spin_lock(&calldata->inode->i_lock); |
936 | if (state->nwriters != 0) | 931 | mode = old_mode = state->state; |
937 | mode |= FMODE_WRITE; | 932 | if (state->nreaders == 0) |
938 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) | 933 | mode &= ~FMODE_READ; |
939 | state->state = mode; | 934 | if (state->nwriters == 0) |
940 | if (mode == state->state) { | 935 | mode &= ~FMODE_WRITE; |
936 | nfs4_state_set_mode_locked(state, mode); | ||
937 | spin_unlock(&calldata->inode->i_lock); | ||
938 | spin_unlock(&state->owner->so_lock); | ||
939 | if (mode == old_mode || test_bit(NFS_DELEGATED_STATE, &state->flags)) { | ||
941 | nfs4_free_closedata(calldata); | 940 | nfs4_free_closedata(calldata); |
942 | task->tk_exit = NULL; | 941 | task->tk_exit = NULL; |
943 | rpc_exit(task, 0); | 942 | rpc_exit(task, 0); |
@@ -961,7 +960,7 @@ static void nfs4_close_begin(struct rpc_task *task) | |||
961 | * | 960 | * |
962 | * NOTE: Caller must be holding the sp->so_owner semaphore! | 961 | * NOTE: Caller must be holding the sp->so_owner semaphore! |
963 | */ | 962 | */ |
964 | int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode) | 963 | int nfs4_do_close(struct inode *inode, struct nfs4_state *state) |
965 | { | 964 | { |
966 | struct nfs_server *server = NFS_SERVER(inode); | 965 | struct nfs_server *server = NFS_SERVER(inode); |
967 | struct nfs4_closedata *calldata; | 966 | struct nfs4_closedata *calldata; |