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 | |
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')
-rw-r--r-- | fs/nfs/nfs4_fs.h | 3 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 35 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 58 |
3 files changed, 49 insertions, 47 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 78a53f5a9f18..53969022d239 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -214,7 +214,7 @@ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); | |||
214 | extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); | 214 | extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); |
215 | extern int nfs4_proc_async_renew(struct nfs4_client *); | 215 | extern int nfs4_proc_async_renew(struct nfs4_client *); |
216 | extern int nfs4_proc_renew(struct nfs4_client *); | 216 | extern int nfs4_proc_renew(struct nfs4_client *); |
217 | extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); | 217 | extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); |
218 | extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); | 218 | extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); |
219 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); | 219 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); |
220 | 220 | ||
@@ -248,6 +248,7 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state | |||
248 | extern void nfs4_put_open_state(struct nfs4_state *); | 248 | extern void nfs4_put_open_state(struct nfs4_state *); |
249 | extern void nfs4_close_state(struct nfs4_state *, mode_t); | 249 | extern void nfs4_close_state(struct nfs4_state *, mode_t); |
250 | extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); | 250 | extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); |
251 | extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); | ||
251 | extern void nfs4_schedule_state_recovery(struct nfs4_client *); | 252 | extern void nfs4_schedule_state_recovery(struct nfs4_client *); |
252 | extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); | 253 | extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); |
253 | extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); | 254 | extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); |
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, | |||
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; |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 2d5a6a2b9dec..959374d833a7 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -366,6 +366,23 @@ nfs4_alloc_open_state(void) | |||
366 | return state; | 366 | return state; |
367 | } | 367 | } |
368 | 368 | ||
369 | void | ||
370 | nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode) | ||
371 | { | ||
372 | if (state->state == mode) | ||
373 | return; | ||
374 | /* NB! List reordering - see the reclaim code for why. */ | ||
375 | if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { | ||
376 | if (mode & FMODE_WRITE) | ||
377 | list_move(&state->open_states, &state->owner->so_states); | ||
378 | else | ||
379 | list_move_tail(&state->open_states, &state->owner->so_states); | ||
380 | } | ||
381 | if (mode == 0) | ||
382 | list_del_init(&state->inode_states); | ||
383 | state->state = mode; | ||
384 | } | ||
385 | |||
369 | static struct nfs4_state * | 386 | static struct nfs4_state * |
370 | __nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) | 387 | __nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) |
371 | { | 388 | { |
@@ -376,10 +393,6 @@ __nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) | |||
376 | list_for_each_entry(state, &nfsi->open_states, inode_states) { | 393 | list_for_each_entry(state, &nfsi->open_states, inode_states) { |
377 | if (state->owner->so_cred != cred) | 394 | if (state->owner->so_cred != cred) |
378 | continue; | 395 | continue; |
379 | if ((mode & FMODE_READ) != 0 && state->nreaders == 0) | ||
380 | continue; | ||
381 | if ((mode & FMODE_WRITE) != 0 && state->nwriters == 0) | ||
382 | continue; | ||
383 | if ((state->state & mode) != mode) | 396 | if ((state->state & mode) != mode) |
384 | continue; | 397 | continue; |
385 | atomic_inc(&state->count); | 398 | atomic_inc(&state->count); |
@@ -400,7 +413,7 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) | |||
400 | 413 | ||
401 | list_for_each_entry(state, &nfsi->open_states, inode_states) { | 414 | list_for_each_entry(state, &nfsi->open_states, inode_states) { |
402 | /* Is this in the process of being freed? */ | 415 | /* Is this in the process of being freed? */ |
403 | if (state->nreaders == 0 && state->nwriters == 0) | 416 | if (state->state == 0) |
404 | continue; | 417 | continue; |
405 | if (state->owner == owner) { | 418 | if (state->owner == owner) { |
406 | atomic_inc(&state->count); | 419 | atomic_inc(&state->count); |
@@ -481,7 +494,6 @@ void nfs4_put_open_state(struct nfs4_state *state) | |||
481 | spin_unlock(&inode->i_lock); | 494 | spin_unlock(&inode->i_lock); |
482 | spin_unlock(&owner->so_lock); | 495 | spin_unlock(&owner->so_lock); |
483 | iput(inode); | 496 | iput(inode); |
484 | BUG_ON (state->state != 0); | ||
485 | nfs4_free_open_state(state); | 497 | nfs4_free_open_state(state); |
486 | nfs4_put_state_owner(owner); | 498 | nfs4_put_state_owner(owner); |
487 | } | 499 | } |
@@ -493,7 +505,7 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode) | |||
493 | { | 505 | { |
494 | struct inode *inode = state->inode; | 506 | struct inode *inode = state->inode; |
495 | struct nfs4_state_owner *owner = state->owner; | 507 | struct nfs4_state_owner *owner = state->owner; |
496 | int newstate; | 508 | int oldstate, newstate = 0; |
497 | 509 | ||
498 | atomic_inc(&owner->so_count); | 510 | atomic_inc(&owner->so_count); |
499 | /* Protect against nfs4_find_state() */ | 511 | /* Protect against nfs4_find_state() */ |
@@ -503,30 +515,20 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode) | |||
503 | state->nreaders--; | 515 | state->nreaders--; |
504 | if (mode & FMODE_WRITE) | 516 | if (mode & FMODE_WRITE) |
505 | state->nwriters--; | 517 | state->nwriters--; |
506 | if (state->nwriters == 0) { | 518 | oldstate = newstate = state->state; |
507 | if (state->nreaders == 0) | 519 | if (state->nreaders == 0) |
508 | list_del_init(&state->inode_states); | 520 | newstate &= ~FMODE_READ; |
509 | /* See reclaim code */ | 521 | if (state->nwriters == 0) |
510 | list_move_tail(&state->open_states, &owner->so_states); | 522 | newstate &= ~FMODE_WRITE; |
523 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | ||
524 | nfs4_state_set_mode_locked(state, newstate); | ||
525 | oldstate = newstate; | ||
511 | } | 526 | } |
512 | spin_unlock(&inode->i_lock); | 527 | spin_unlock(&inode->i_lock); |
513 | spin_unlock(&owner->so_lock); | 528 | spin_unlock(&owner->so_lock); |
514 | newstate = 0; | 529 | |
515 | if (state->state != 0) { | 530 | if (oldstate != newstate && nfs4_do_close(inode, state) == 0) |
516 | if (state->nreaders) | 531 | return; |
517 | newstate |= FMODE_READ; | ||
518 | if (state->nwriters) | ||
519 | newstate |= FMODE_WRITE; | ||
520 | if (state->state == newstate) | ||
521 | goto out; | ||
522 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | ||
523 | state->state = newstate; | ||
524 | goto out; | ||
525 | } | ||
526 | if (nfs4_do_close(inode, state, newstate) == 0) | ||
527 | return; | ||
528 | } | ||
529 | out: | ||
530 | nfs4_put_open_state(state); | 532 | nfs4_put_open_state(state); |
531 | nfs4_put_state_owner(owner); | 533 | nfs4_put_state_owner(owner); |
532 | } | 534 | } |