aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2013-03-16 20:54:34 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2013-03-25 12:04:10 -0400
commit5521abfdcf4d67c3441d4414f29e1acd7cc43380 (patch)
treed3f2554e5f066bc45fde92f338b46e0910675bd2
parent9b20614988199fb03580b335a28250922e902098 (diff)
NFSv4: Resend the READ/WRITE RPC call if a stateid change causes an error
Adds logic to ensure that if the server returns a BAD_STATEID, or other state related error, then we check if the stateid has already changed. If it has, then rather than start state recovery, we should just resend the failed RPC call with the new stateid. Allow nfs4_select_rw_stateid to notify that the stateid is unstable by having it return -EWOULDBLOCK if an RPC is underway that might change the stateid. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/nfs4_fs.h4
-rw-r--r--fs/nfs/nfs4proc.c64
-rw-r--r--fs/nfs/nfs4state.c32
3 files changed, 86 insertions, 14 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 8309e98c44f9..627a74f0e248 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -234,7 +234,7 @@ extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr
234extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); 234extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
235extern int nfs4_release_lockowner(struct nfs4_lock_state *); 235extern int nfs4_release_lockowner(struct nfs4_lock_state *);
236extern const struct xattr_handler *nfs4_xattr_handlers[]; 236extern const struct xattr_handler *nfs4_xattr_handlers[];
237extern void nfs4_set_rw_stateid(nfs4_stateid *stateid, 237extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
238 const struct nfs_open_context *ctx, 238 const struct nfs_open_context *ctx,
239 const struct nfs_lock_context *l_ctx, 239 const struct nfs_lock_context *l_ctx,
240 fmode_t fmode); 240 fmode_t fmode);
@@ -358,7 +358,7 @@ extern void nfs41_handle_server_scope(struct nfs_client *,
358 struct nfs41_server_scope **); 358 struct nfs41_server_scope **);
359extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); 359extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
360extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); 360extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
361extern void nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *, 361extern int nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *,
362 fmode_t, const struct nfs_lockowner *); 362 fmode_t, const struct nfs_lockowner *);
363 363
364extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask); 364extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 26176ce3d96a..6ad06121d88c 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3454,7 +3454,7 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
3454 return err; 3454 return err;
3455} 3455}
3456 3456
3457void nfs4_set_rw_stateid(nfs4_stateid *stateid, 3457int nfs4_set_rw_stateid(nfs4_stateid *stateid,
3458 const struct nfs_open_context *ctx, 3458 const struct nfs_open_context *ctx,
3459 const struct nfs_lock_context *l_ctx, 3459 const struct nfs_lock_context *l_ctx,
3460 fmode_t fmode) 3460 fmode_t fmode)
@@ -3463,10 +3463,37 @@ void nfs4_set_rw_stateid(nfs4_stateid *stateid,
3463 3463
3464 if (l_ctx != NULL) 3464 if (l_ctx != NULL)
3465 lockowner = &l_ctx->lockowner; 3465 lockowner = &l_ctx->lockowner;
3466 nfs4_select_rw_stateid(stateid, ctx->state, fmode, lockowner); 3466 return nfs4_select_rw_stateid(stateid, ctx->state, fmode, lockowner);
3467} 3467}
3468EXPORT_SYMBOL_GPL(nfs4_set_rw_stateid); 3468EXPORT_SYMBOL_GPL(nfs4_set_rw_stateid);
3469 3469
3470static bool nfs4_stateid_is_current(nfs4_stateid *stateid,
3471 const struct nfs_open_context *ctx,
3472 const struct nfs_lock_context *l_ctx,
3473 fmode_t fmode)
3474{
3475 nfs4_stateid current_stateid;
3476
3477 if (nfs4_set_rw_stateid(&current_stateid, ctx, l_ctx, fmode))
3478 return false;
3479 return nfs4_stateid_match(stateid, &current_stateid);
3480}
3481
3482static bool nfs4_error_stateid_expired(int err)
3483{
3484 switch (err) {
3485 case -NFS4ERR_DELEG_REVOKED:
3486 case -NFS4ERR_ADMIN_REVOKED:
3487 case -NFS4ERR_BAD_STATEID:
3488 case -NFS4ERR_STALE_STATEID:
3489 case -NFS4ERR_OLD_STATEID:
3490 case -NFS4ERR_OPENMODE:
3491 case -NFS4ERR_EXPIRED:
3492 return true;
3493 }
3494 return false;
3495}
3496
3470void __nfs4_read_done_cb(struct nfs_read_data *data) 3497void __nfs4_read_done_cb(struct nfs_read_data *data)
3471{ 3498{
3472 nfs_invalidate_atime(data->header->inode); 3499 nfs_invalidate_atime(data->header->inode);
@@ -3487,6 +3514,20 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
3487 return 0; 3514 return 0;
3488} 3515}
3489 3516
3517static bool nfs4_read_stateid_changed(struct rpc_task *task,
3518 struct nfs_readargs *args)
3519{
3520
3521 if (!nfs4_error_stateid_expired(task->tk_status) ||
3522 nfs4_stateid_is_current(&args->stateid,
3523 args->context,
3524 args->lock_context,
3525 FMODE_READ))
3526 return false;
3527 rpc_restart_call_prepare(task);
3528 return true;
3529}
3530
3490static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) 3531static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
3491{ 3532{
3492 3533
@@ -3494,7 +3535,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
3494 3535
3495 if (!nfs4_sequence_done(task, &data->res.seq_res)) 3536 if (!nfs4_sequence_done(task, &data->res.seq_res))
3496 return -EAGAIN; 3537 return -EAGAIN;
3497 3538 if (nfs4_read_stateid_changed(task, &data->args))
3539 return -EAGAIN;
3498 return data->read_done_cb ? data->read_done_cb(task, data) : 3540 return data->read_done_cb ? data->read_done_cb(task, data) :
3499 nfs4_read_done_cb(task, data); 3541 nfs4_read_done_cb(task, data);
3500} 3542}
@@ -3533,10 +3575,26 @@ static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data
3533 return 0; 3575 return 0;
3534} 3576}
3535 3577
3578static bool nfs4_write_stateid_changed(struct rpc_task *task,
3579 struct nfs_writeargs *args)
3580{
3581
3582 if (!nfs4_error_stateid_expired(task->tk_status) ||
3583 nfs4_stateid_is_current(&args->stateid,
3584 args->context,
3585 args->lock_context,
3586 FMODE_WRITE))
3587 return false;
3588 rpc_restart_call_prepare(task);
3589 return true;
3590}
3591
3536static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) 3592static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
3537{ 3593{
3538 if (!nfs4_sequence_done(task, &data->res.seq_res)) 3594 if (!nfs4_sequence_done(task, &data->res.seq_res))
3539 return -EAGAIN; 3595 return -EAGAIN;
3596 if (nfs4_write_stateid_changed(task, &data->args))
3597 return -EAGAIN;
3540 return data->write_done_cb ? data->write_done_cb(task, data) : 3598 return data->write_done_cb ? data->write_done_cb(task, data) :
3541 nfs4_write_done_cb(task, data); 3599 nfs4_write_done_cb(task, data);
3542} 3600}
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 8db102c7add6..4e95bd72f480 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -989,13 +989,14 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
989 return 0; 989 return 0;
990} 990}
991 991
992static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state, 992static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
993 struct nfs4_state *state,
993 const struct nfs_lockowner *lockowner) 994 const struct nfs_lockowner *lockowner)
994{ 995{
995 struct nfs4_lock_state *lsp; 996 struct nfs4_lock_state *lsp;
996 fl_owner_t fl_owner; 997 fl_owner_t fl_owner;
997 pid_t fl_pid; 998 pid_t fl_pid;
998 bool ret = false; 999 int ret = -ENOENT;
999 1000
1000 1001
1001 if (lockowner == NULL) 1002 if (lockowner == NULL)
@@ -1010,7 +1011,10 @@ static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
1010 lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); 1011 lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE);
1011 if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { 1012 if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
1012 nfs4_stateid_copy(dst, &lsp->ls_stateid); 1013 nfs4_stateid_copy(dst, &lsp->ls_stateid);
1013 ret = true; 1014 ret = 0;
1015 smp_rmb();
1016 if (!list_empty(&lsp->ls_seqid.list))
1017 ret = -EWOULDBLOCK;
1014 } 1018 }
1015 spin_unlock(&state->state_lock); 1019 spin_unlock(&state->state_lock);
1016 nfs4_put_lock_state(lsp); 1020 nfs4_put_lock_state(lsp);
@@ -1018,28 +1022,38 @@ out:
1018 return ret; 1022 return ret;
1019} 1023}
1020 1024
1021static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) 1025static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
1022{ 1026{
1027 int ret;
1023 int seq; 1028 int seq;
1024 1029
1025 do { 1030 do {
1026 seq = read_seqbegin(&state->seqlock); 1031 seq = read_seqbegin(&state->seqlock);
1027 nfs4_stateid_copy(dst, &state->stateid); 1032 nfs4_stateid_copy(dst, &state->stateid);
1033 ret = 0;
1034 smp_rmb();
1035 if (!list_empty(&state->owner->so_seqid.list))
1036 ret = -EWOULDBLOCK;
1028 } while (read_seqretry(&state->seqlock, seq)); 1037 } while (read_seqretry(&state->seqlock, seq));
1038 return ret;
1029} 1039}
1030 1040
1031/* 1041/*
1032 * Byte-range lock aware utility to initialize the stateid of read/write 1042 * Byte-range lock aware utility to initialize the stateid of read/write
1033 * requests. 1043 * requests.
1034 */ 1044 */
1035void nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, 1045int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
1036 fmode_t fmode, const struct nfs_lockowner *lockowner) 1046 fmode_t fmode, const struct nfs_lockowner *lockowner)
1037{ 1047{
1048 int ret = 0;
1038 if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) 1049 if (nfs4_copy_delegation_stateid(dst, state->inode, fmode))
1039 return; 1050 goto out;
1040 if (nfs4_copy_lock_stateid(dst, state, lockowner)) 1051 ret = nfs4_copy_lock_stateid(dst, state, lockowner);
1041 return; 1052 if (ret != -ENOENT)
1042 nfs4_copy_open_stateid(dst, state); 1053 goto out;
1054 ret = nfs4_copy_open_stateid(dst, state);
1055out:
1056 return ret;
1043} 1057}
1044 1058
1045struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask) 1059struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)