diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-03-14 16:57:48 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-03-25 12:04:10 -0400 |
commit | 5d422301f97b821301efcdb6fc9d1a83a5c102d6 (patch) | |
tree | ed7117feb89a0a804669f9f017d2ce3ef7fee10c /fs | |
parent | 3ed5e2a2c394df4e03a680842c2d07a8680f133b (diff) |
NFSv4: Fail I/O if the state recovery fails irrevocably
If state recovery fails with an ESTALE or a ENOENT, then we shouldn't
keep retrying. Instead, mark the stateid as being invalid and
fail the I/O with an EIO error.
For other operations such as POSIX and BSD file locking, truncate
etc, fail with an EBADF to indicate that this file descriptor is no
longer valid.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4_fs.h | 8 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayout.c | 12 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 37 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 19 | ||||
-rw-r--r-- | fs/nfs/pnfs.c | 2 |
5 files changed, 61 insertions, 17 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 944c9a5c1039..9ce90135bf22 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -149,6 +149,7 @@ enum { | |||
149 | NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */ | 149 | NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */ |
150 | NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */ | 150 | NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */ |
151 | NFS_STATE_POSIX_LOCKS, /* Posix locks are supported */ | 151 | NFS_STATE_POSIX_LOCKS, /* Posix locks are supported */ |
152 | NFS_STATE_RECOVERY_FAILED, /* OPEN stateid state recovery failed */ | ||
152 | }; | 153 | }; |
153 | 154 | ||
154 | struct nfs4_state { | 155 | struct nfs4_state { |
@@ -347,7 +348,7 @@ extern int nfs4_wait_clnt_recover(struct nfs_client *clp); | |||
347 | extern int nfs4_client_recover_expired_lease(struct nfs_client *clp); | 348 | extern int nfs4_client_recover_expired_lease(struct nfs_client *clp); |
348 | extern void nfs4_schedule_state_manager(struct nfs_client *); | 349 | extern void nfs4_schedule_state_manager(struct nfs_client *); |
349 | extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); | 350 | extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); |
350 | extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *); | 351 | extern int nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *); |
351 | extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags); | 352 | extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags); |
352 | extern void nfs41_handle_server_scope(struct nfs_client *, | 353 | extern void nfs41_handle_server_scope(struct nfs_client *, |
353 | struct nfs41_server_scope **); | 354 | struct nfs41_server_scope **); |
@@ -412,6 +413,11 @@ static inline bool nfs4_stateid_match(const nfs4_stateid *dst, const nfs4_statei | |||
412 | return memcmp(dst, src, sizeof(*dst)) == 0; | 413 | return memcmp(dst, src, sizeof(*dst)) == 0; |
413 | } | 414 | } |
414 | 415 | ||
416 | static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state) | ||
417 | { | ||
418 | return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0; | ||
419 | } | ||
420 | |||
415 | #else | 421 | #else |
416 | 422 | ||
417 | #define nfs4_close_state(a, b) do { } while (0) | 423 | #define nfs4_close_state(a, b) do { } while (0) |
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 4fb234d3aefb..1ee5737211d7 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
@@ -158,11 +158,14 @@ static int filelayout_async_handle_error(struct rpc_task *task, | |||
158 | case -NFS4ERR_OPENMODE: | 158 | case -NFS4ERR_OPENMODE: |
159 | if (state == NULL) | 159 | if (state == NULL) |
160 | break; | 160 | break; |
161 | nfs4_schedule_stateid_recovery(mds_server, state); | 161 | if (nfs4_schedule_stateid_recovery(mds_server, state) < 0) |
162 | goto out_bad_stateid; | ||
162 | goto wait_on_recovery; | 163 | goto wait_on_recovery; |
163 | case -NFS4ERR_EXPIRED: | 164 | case -NFS4ERR_EXPIRED: |
164 | if (state != NULL) | 165 | if (state != NULL) { |
165 | nfs4_schedule_stateid_recovery(mds_server, state); | 166 | if (nfs4_schedule_stateid_recovery(mds_server, state) < 0) |
167 | goto out_bad_stateid; | ||
168 | } | ||
166 | nfs4_schedule_lease_recovery(mds_client); | 169 | nfs4_schedule_lease_recovery(mds_client); |
167 | goto wait_on_recovery; | 170 | goto wait_on_recovery; |
168 | /* DS session errors */ | 171 | /* DS session errors */ |
@@ -226,6 +229,9 @@ reset: | |||
226 | out: | 229 | out: |
227 | task->tk_status = 0; | 230 | task->tk_status = 0; |
228 | return -EAGAIN; | 231 | return -EAGAIN; |
232 | out_bad_stateid: | ||
233 | task->tk_status = -EIO; | ||
234 | return 0; | ||
229 | wait_on_recovery: | 235 | wait_on_recovery: |
230 | rpc_sleep_on(&mds_client->cl_rpcwaitq, task, NULL); | 236 | rpc_sleep_on(&mds_client->cl_rpcwaitq, task, NULL); |
231 | if (test_bit(NFS4CLNT_MANAGER_RUNNING, &mds_client->cl_state) == 0) | 237 | if (test_bit(NFS4CLNT_MANAGER_RUNNING, &mds_client->cl_state) == 0) |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 26431cf62ddb..c3bbb6c53d61 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -295,7 +295,9 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc | |||
295 | } | 295 | } |
296 | if (state == NULL) | 296 | if (state == NULL) |
297 | break; | 297 | break; |
298 | nfs4_schedule_stateid_recovery(server, state); | 298 | ret = nfs4_schedule_stateid_recovery(server, state); |
299 | if (ret < 0) | ||
300 | break; | ||
299 | goto wait_on_recovery; | 301 | goto wait_on_recovery; |
300 | case -NFS4ERR_DELEG_REVOKED: | 302 | case -NFS4ERR_DELEG_REVOKED: |
301 | case -NFS4ERR_ADMIN_REVOKED: | 303 | case -NFS4ERR_ADMIN_REVOKED: |
@@ -303,11 +305,16 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc | |||
303 | if (state == NULL) | 305 | if (state == NULL) |
304 | break; | 306 | break; |
305 | nfs_remove_bad_delegation(state->inode); | 307 | nfs_remove_bad_delegation(state->inode); |
306 | nfs4_schedule_stateid_recovery(server, state); | 308 | ret = nfs4_schedule_stateid_recovery(server, state); |
309 | if (ret < 0) | ||
310 | break; | ||
307 | goto wait_on_recovery; | 311 | goto wait_on_recovery; |
308 | case -NFS4ERR_EXPIRED: | 312 | case -NFS4ERR_EXPIRED: |
309 | if (state != NULL) | 313 | if (state != NULL) { |
310 | nfs4_schedule_stateid_recovery(server, state); | 314 | ret = nfs4_schedule_stateid_recovery(server, state); |
315 | if (ret < 0) | ||
316 | break; | ||
317 | } | ||
311 | case -NFS4ERR_STALE_STATEID: | 318 | case -NFS4ERR_STALE_STATEID: |
312 | case -NFS4ERR_STALE_CLIENTID: | 319 | case -NFS4ERR_STALE_CLIENTID: |
313 | nfs4_schedule_lease_recovery(clp); | 320 | nfs4_schedule_lease_recovery(clp); |
@@ -2053,7 +2060,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, | |||
2053 | 2060 | ||
2054 | nfs_fattr_init(fattr); | 2061 | nfs_fattr_init(fattr); |
2055 | 2062 | ||
2056 | if (state != NULL) { | 2063 | if (state != NULL && nfs4_valid_open_stateid(state)) { |
2057 | struct nfs_lockowner lockowner = { | 2064 | struct nfs_lockowner lockowner = { |
2058 | .l_owner = current->files, | 2065 | .l_owner = current->files, |
2059 | .l_pid = current->tgid, | 2066 | .l_pid = current->tgid, |
@@ -2201,6 +2208,8 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) | |||
2201 | calldata->arg.fmode &= ~FMODE_WRITE; | 2208 | calldata->arg.fmode &= ~FMODE_WRITE; |
2202 | } | 2209 | } |
2203 | } | 2210 | } |
2211 | if (!nfs4_valid_open_stateid(state)) | ||
2212 | call_close = 0; | ||
2204 | spin_unlock(&state->owner->so_lock); | 2213 | spin_unlock(&state->owner->so_lock); |
2205 | 2214 | ||
2206 | if (!call_close) { | 2215 | if (!call_close) { |
@@ -3980,11 +3989,14 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, | |||
3980 | case -NFS4ERR_OPENMODE: | 3989 | case -NFS4ERR_OPENMODE: |
3981 | if (state == NULL) | 3990 | if (state == NULL) |
3982 | break; | 3991 | break; |
3983 | nfs4_schedule_stateid_recovery(server, state); | 3992 | if (nfs4_schedule_stateid_recovery(server, state) < 0) |
3993 | goto stateid_invalid; | ||
3984 | goto wait_on_recovery; | 3994 | goto wait_on_recovery; |
3985 | case -NFS4ERR_EXPIRED: | 3995 | case -NFS4ERR_EXPIRED: |
3986 | if (state != NULL) | 3996 | if (state != NULL) { |
3987 | nfs4_schedule_stateid_recovery(server, state); | 3997 | if (nfs4_schedule_stateid_recovery(server, state) < 0) |
3998 | goto stateid_invalid; | ||
3999 | } | ||
3988 | case -NFS4ERR_STALE_STATEID: | 4000 | case -NFS4ERR_STALE_STATEID: |
3989 | case -NFS4ERR_STALE_CLIENTID: | 4001 | case -NFS4ERR_STALE_CLIENTID: |
3990 | nfs4_schedule_lease_recovery(clp); | 4002 | nfs4_schedule_lease_recovery(clp); |
@@ -4016,6 +4028,9 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, | |||
4016 | } | 4028 | } |
4017 | task->tk_status = nfs4_map_errors(task->tk_status); | 4029 | task->tk_status = nfs4_map_errors(task->tk_status); |
4018 | return 0; | 4030 | return 0; |
4031 | stateid_invalid: | ||
4032 | task->tk_status = -EIO; | ||
4033 | return 0; | ||
4019 | wait_on_recovery: | 4034 | wait_on_recovery: |
4020 | rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL); | 4035 | rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL); |
4021 | if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0) | 4036 | if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0) |
@@ -4632,12 +4647,18 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) | |||
4632 | data->res.open_seqid = data->arg.open_seqid; | 4647 | data->res.open_seqid = data->arg.open_seqid; |
4633 | } else | 4648 | } else |
4634 | data->arg.new_lock_owner = 0; | 4649 | data->arg.new_lock_owner = 0; |
4650 | if (!nfs4_valid_open_stateid(state)) { | ||
4651 | data->rpc_status = -EBADF; | ||
4652 | task->tk_action = NULL; | ||
4653 | goto out_release_open_seqid; | ||
4654 | } | ||
4635 | data->timestamp = jiffies; | 4655 | data->timestamp = jiffies; |
4636 | if (nfs4_setup_sequence(data->server, | 4656 | if (nfs4_setup_sequence(data->server, |
4637 | &data->arg.seq_args, | 4657 | &data->arg.seq_args, |
4638 | &data->res.seq_res, | 4658 | &data->res.seq_res, |
4639 | task) == 0) | 4659 | task) == 0) |
4640 | return; | 4660 | return; |
4661 | out_release_open_seqid: | ||
4641 | nfs_release_seqid(data->arg.open_seqid); | 4662 | nfs_release_seqid(data->arg.open_seqid); |
4642 | out_release_lock_seqid: | 4663 | out_release_lock_seqid: |
4643 | nfs_release_seqid(data->arg.lock_seqid); | 4664 | nfs_release_seqid(data->arg.lock_seqid); |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6ace365c6334..fec1c5bb4863 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -699,6 +699,8 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) | |||
699 | list_for_each_entry(state, &nfsi->open_states, inode_states) { | 699 | list_for_each_entry(state, &nfsi->open_states, inode_states) { |
700 | if (state->owner != owner) | 700 | if (state->owner != owner) |
701 | continue; | 701 | continue; |
702 | if (!nfs4_valid_open_stateid(state)) | ||
703 | continue; | ||
702 | if (atomic_inc_not_zero(&state->count)) | 704 | if (atomic_inc_not_zero(&state->count)) |
703 | return state; | 705 | return state; |
704 | } | 706 | } |
@@ -1286,14 +1288,17 @@ static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_s | |||
1286 | return 1; | 1288 | return 1; |
1287 | } | 1289 | } |
1288 | 1290 | ||
1289 | void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state) | 1291 | int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state) |
1290 | { | 1292 | { |
1291 | struct nfs_client *clp = server->nfs_client; | 1293 | struct nfs_client *clp = server->nfs_client; |
1292 | 1294 | ||
1295 | if (!nfs4_valid_open_stateid(state)) | ||
1296 | return -EBADF; | ||
1293 | nfs4_state_mark_reclaim_nograce(clp, state); | 1297 | nfs4_state_mark_reclaim_nograce(clp, state); |
1294 | dprintk("%s: scheduling stateid recovery for server %s\n", __func__, | 1298 | dprintk("%s: scheduling stateid recovery for server %s\n", __func__, |
1295 | clp->cl_hostname); | 1299 | clp->cl_hostname); |
1296 | nfs4_schedule_state_manager(clp); | 1300 | nfs4_schedule_state_manager(clp); |
1301 | return 0; | ||
1297 | } | 1302 | } |
1298 | EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery); | 1303 | EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery); |
1299 | 1304 | ||
@@ -1323,6 +1328,11 @@ void nfs_inode_find_state_and_recover(struct inode *inode, | |||
1323 | nfs4_schedule_state_manager(clp); | 1328 | nfs4_schedule_state_manager(clp); |
1324 | } | 1329 | } |
1325 | 1330 | ||
1331 | static void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error) | ||
1332 | { | ||
1333 | set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags); | ||
1334 | } | ||
1335 | |||
1326 | 1336 | ||
1327 | static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) | 1337 | static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) |
1328 | { | 1338 | { |
@@ -1398,6 +1408,8 @@ restart: | |||
1398 | list_for_each_entry(state, &sp->so_states, open_states) { | 1408 | list_for_each_entry(state, &sp->so_states, open_states) { |
1399 | if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) | 1409 | if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) |
1400 | continue; | 1410 | continue; |
1411 | if (!nfs4_valid_open_stateid(state)) | ||
1412 | continue; | ||
1401 | if (state->state == 0) | 1413 | if (state->state == 0) |
1402 | continue; | 1414 | continue; |
1403 | atomic_inc(&state->count); | 1415 | atomic_inc(&state->count); |
@@ -1430,10 +1442,7 @@ restart: | |||
1430 | * Open state on this file cannot be recovered | 1442 | * Open state on this file cannot be recovered |
1431 | * All we can do is revert to using the zero stateid. | 1443 | * All we can do is revert to using the zero stateid. |
1432 | */ | 1444 | */ |
1433 | memset(&state->stateid, 0, | 1445 | nfs4_state_mark_recovery_failed(state, status); |
1434 | sizeof(state->stateid)); | ||
1435 | /* Mark the file as being 'closed' */ | ||
1436 | state->state = 0; | ||
1437 | break; | 1446 | break; |
1438 | case -NFS4ERR_ADMIN_REVOKED: | 1447 | case -NFS4ERR_ADMIN_REVOKED: |
1439 | case -NFS4ERR_STALE_STATEID: | 1448 | case -NFS4ERR_STALE_STATEID: |
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 4bdffe0ba025..c5bd758e5637 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c | |||
@@ -718,6 +718,8 @@ pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo, | |||
718 | spin_lock(&lo->plh_inode->i_lock); | 718 | spin_lock(&lo->plh_inode->i_lock); |
719 | if (pnfs_layoutgets_blocked(lo, 1)) { | 719 | if (pnfs_layoutgets_blocked(lo, 1)) { |
720 | status = -EAGAIN; | 720 | status = -EAGAIN; |
721 | } else if (!nfs4_valid_open_stateid(open_state)) { | ||
722 | status = -EBADF; | ||
721 | } else if (list_empty(&lo->plh_segs)) { | 723 | } else if (list_empty(&lo->plh_segs)) { |
722 | int seq; | 724 | int seq; |
723 | 725 | ||