diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 0ac6bbb8eaa4365d77416ae77d271c4a035bf48a..1c6fbd1cda978601136a5753b3e511cb58dd5d2e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -40,6 +40,9 @@ struct idmap; enum nfs4_client_state { NFS4CLNT_STATE_RECOVER = 0, NFS4CLNT_LEASE_EXPIRED, + NFS4CLNT_RECLAIM_REBOOT, + NFS4CLNT_RECLAIM_NOGRACE, + NFS4CLNT_CB_PATH_DOWN, }; /* @@ -128,6 +131,8 @@ enum { NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */ NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */ NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */ + NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */ + NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */ }; struct nfs4_state { @@ -160,6 +165,7 @@ struct nfs4_exception { }; struct nfs4_state_recovery_ops { + int state_flag_bit; int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); int (*recover_lock)(struct nfs4_state *, struct file_lock *); }; @@ -187,7 +193,7 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; -extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops; +extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops; extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2]; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d53aa2dace84c7ca3c78ffa9aee0ffb0ad19113d..279ab36b5a67ab5456412038fcf015f889a2e020 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -984,7 +984,7 @@ static int nfs4_recover_expired_lease(struct nfs_server *server) ret = nfs4_wait_clnt_recover(server->client, clp); if (ret != 0) return ret; - if (!test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) + if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) break; nfs4_schedule_state_recovery(clp); } @@ -2942,7 +2942,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre spin_lock(&clp->cl_lock); clp->cl_lease_time = fsinfo.lease_time * HZ; clp->cl_last_renewal = now; - clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); spin_unlock(&clp->cl_lock); } return status; @@ -3690,11 +3689,13 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, } struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { + .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, }; -struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops = { +struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = { + .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE, .recover_open = nfs4_open_expired, .recover_lock = nfs4_lock_expired, }; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 300faba9a18a52e06d9d93e80314d6cd4249c2b1..e5cd8cacdcee46c42351daac2fe491e67a0c0867 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -821,6 +821,27 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp) nfs4_recover_state(clp); } +static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state) +{ + + set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); + /* Don't recover state that expired before the reboot */ + if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) { + clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); + return 0; + } + set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); + return 1; +} + +static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state) +{ + set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags); + clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); + set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); + return 1; +} + static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) { struct inode *inode = state->inode; @@ -869,6 +890,8 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs * server that doesn't support a grace period. */ list_for_each_entry(state, &sp->so_states, open_states) { + if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) + continue; if (state->state == 0) continue; status = ops->recover_open(sp, state); @@ -888,8 +911,7 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", __func__, status); case -ENOENT: - case -NFS4ERR_RECLAIM_BAD: - case -NFS4ERR_RECLAIM_CONFLICT: + case -ESTALE: /* * Open state on this file cannot be recovered * All we can do is revert to using the zero stateid. @@ -899,8 +921,13 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs /* Mark the file as being 'closed' */ state->state = 0; break; + case -NFS4ERR_RECLAIM_BAD: + case -NFS4ERR_RECLAIM_CONFLICT: + nfs4_state_mark_reclaim_nograce(sp->so_client, state); + break; case -NFS4ERR_EXPIRED: case -NFS4ERR_NO_GRACE: + nfs4_state_mark_reclaim_nograce(sp->so_client, state); case -NFS4ERR_STALE_CLIENTID: goto out_err; } @@ -910,12 +937,26 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs return status; } -static void nfs4_state_mark_reclaim(struct nfs_client *clp) +static void nfs4_clear_open_state(struct nfs4_state *state) +{ + struct nfs4_lock_state *lock; + + clear_bit(NFS_DELEGATED_STATE, &state->flags); + clear_bit(NFS_O_RDONLY_STATE, &state->flags); + clear_bit(NFS_O_WRONLY_STATE, &state->flags); + clear_bit(NFS_O_RDWR_STATE, &state->flags); + list_for_each_entry(lock, &state->lock_states, ls_locks) { + lock->ls_seqid.counter = 0; + lock->ls_seqid.flags = 0; + lock->ls_flags &= ~NFS_LOCK_INITIALIZED; + } +} + +static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state)) { struct nfs4_state_owner *sp; struct rb_node *pos; struct nfs4_state *state; - struct nfs4_lock_state *lock; /* Reset all sequence ids to zero */ for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { @@ -924,20 +965,60 @@ static void nfs4_state_mark_reclaim(struct nfs_client *clp) sp->so_seqid.flags = 0; spin_lock(&sp->so_lock); list_for_each_entry(state, &sp->so_states, open_states) { - clear_bit(NFS_DELEGATED_STATE, &state->flags); - clear_bit(NFS_O_RDONLY_STATE, &state->flags); - clear_bit(NFS_O_WRONLY_STATE, &state->flags); - clear_bit(NFS_O_RDWR_STATE, &state->flags); - list_for_each_entry(lock, &state->lock_states, ls_locks) { - lock->ls_seqid.counter = 0; - lock->ls_seqid.flags = 0; - lock->ls_flags &= ~NFS_LOCK_INITIALIZED; - } + if (mark_reclaim(clp, state)) + nfs4_clear_open_state(state); } spin_unlock(&sp->so_lock); } } +static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) +{ + /* Mark all delegations for reclaim */ + nfs_delegation_mark_reclaim(clp); + nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot); +} + +static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) +{ + struct nfs4_state_owner *sp; + struct rb_node *pos; + struct nfs4_state *state; + + if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) + return; + + for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { + sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); + spin_lock(&sp->so_lock); + list_for_each_entry(state, &sp->so_states, open_states) { + if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags)) + continue; + nfs4_state_mark_reclaim_nograce(clp, state); + } + spin_unlock(&sp->so_lock); + } + + nfs_delegation_reap_unclaimed(clp); +} + +static void nfs_delegation_clear_all(struct nfs_client *clp) +{ + nfs_delegation_mark_reclaim(clp); + nfs_delegation_reap_unclaimed(clp); +} + +static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) +{ + nfs_delegation_clear_all(clp); + nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); +} + +static void nfs4_state_end_reclaim_nograce(struct nfs_client *clp) +{ + clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); +} + static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops) { struct rb_node *pos; @@ -964,11 +1045,25 @@ static int nfs4_check_lease(struct nfs_client *clp) /* Yes there are: try to renew the old lease */ status = nfs4_proc_renew(clp, cred); put_rpccred(cred); + switch (status) { + case -NFS4ERR_CB_PATH_DOWN: + set_bit(NFS4CLNT_CB_PATH_DOWN, &clp->cl_state); + break; + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_LEASE_MOVED: + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + nfs4_state_start_reclaim_reboot(clp); + break; + case -NFS4ERR_EXPIRED: + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + nfs4_state_start_reclaim_nograce(clp); + } return status; } /* "reboot" to ensure we clear all state on the server */ clp->cl_boot_time = CURRENT_TIME; + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); return status; } @@ -993,7 +1088,6 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) static int reclaimer(void *ptr) { struct nfs_client *clp = ptr; - const struct nfs4_state_recovery_ops *ops; int status = 0; allow_signal(SIGKILL); @@ -1001,47 +1095,51 @@ static int reclaimer(void *ptr) /* Ensure exclusive access to NFSv4 state */ down_write(&clp->cl_sem); while (!list_empty(&clp->cl_superblocks)) { - ops = &nfs4_network_partition_recovery_ops; status = nfs4_check_lease(clp); - switch (status) { - case 0: - case -NFS4ERR_CB_PATH_DOWN: - goto out; - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_LEASE_MOVED: - ops = &nfs4_reboot_recovery_ops; - } - /* We're going to have to re-establish a clientid */ - nfs4_state_mark_reclaim(clp); + if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { + /* We're going to have to re-establish a clientid */ + status = nfs4_reclaim_lease(clp); + if (status) { + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + if (status == -EAGAIN) + continue; + goto out_error; + } + } - status = nfs4_reclaim_lease(clp); - if (status) { - if (status == -EAGAIN) + /* First recover reboot state... */ + if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { + /* Note: list is protected by exclusive lock on cl->cl_sem */ + status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops); + if (status == -NFS4ERR_STALE_CLIENTID) { + set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); continue; - goto out_error; + } + nfs4_state_end_reclaim_reboot(clp); + continue; } - /* Mark all delegations for reclaim */ - nfs_delegation_mark_reclaim(clp); - /* Note: list is protected by exclusive lock on cl->cl_sem */ - status = nfs4_do_reclaim(clp, ops); - if (status < 0) { - if (status == -NFS4ERR_NO_GRACE) { - ops = &nfs4_network_partition_recovery_ops; - status = nfs4_do_reclaim(clp, ops); - } - if (status == -NFS4ERR_STALE_CLIENTID) - continue; - if (status == -NFS4ERR_EXPIRED) - continue; + /* Now recover expired state... */ + if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { + /* Note: list is protected by exclusive lock on cl->cl_sem */ + status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops); + if (status < 0) { + set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); + if (status == -NFS4ERR_STALE_CLIENTID) + continue; + if (status == -NFS4ERR_EXPIRED) + continue; + goto out_error; + } else + nfs4_state_end_reclaim_nograce(clp); + continue; } - nfs_delegation_reap_unclaimed(clp); break; } out: up_write(&clp->cl_sem); - if (status == -NFS4ERR_CB_PATH_DOWN) + if (test_and_clear_bit(NFS4CLNT_CB_PATH_DOWN, &clp->cl_state)) nfs_handle_cb_pathdown(clp); nfs4_clear_recover_bit(clp); nfs_put_client(clp); @@ -1050,7 +1148,8 @@ static int reclaimer(void *ptr) out_error: printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %s" " with error %d\n", clp->cl_hostname, -status); - set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) + nfs4_state_end_reclaim_reboot(clp); goto out; }