aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-11-04 15:32:58 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-11-04 15:32:58 -0500
commit4cecb76ff86db46d2823550256c828b6597f418e (patch)
treedb80c6f4423d2559661234e8d1cd34b2c15f7750
parent462aae65f6ea41de05ebc2a54a9e6b95563591f3 (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>
-rw-r--r--fs/nfs/nfs4_fs.h3
-rw-r--r--fs/nfs/nfs4proc.c35
-rw-r--r--fs/nfs/nfs4state.c58
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);
214extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); 214extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
215extern int nfs4_proc_async_renew(struct nfs4_client *); 215extern int nfs4_proc_async_renew(struct nfs4_client *);
216extern int nfs4_proc_renew(struct nfs4_client *); 216extern int nfs4_proc_renew(struct nfs4_client *);
217extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); 217extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state);
218extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); 218extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
219extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); 219extern 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
248extern void nfs4_put_open_state(struct nfs4_state *); 248extern void nfs4_put_open_state(struct nfs4_state *);
249extern void nfs4_close_state(struct nfs4_state *, mode_t); 249extern void nfs4_close_state(struct nfs4_state *, mode_t);
250extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); 250extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
251extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
251extern void nfs4_schedule_state_recovery(struct nfs4_client *); 252extern void nfs4_schedule_state_recovery(struct nfs4_client *);
252extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); 253extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
253extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); 254extern 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 */
964int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode) 963int 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
369void
370nfs4_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
369static struct nfs4_state * 386static 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 }
529out:
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}