From 2c820d9a97f07b273b2c8a5960bd52b1b5864c68 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 21 May 2012 22:45:33 -0400 Subject: NFS: Force server to drop NFSv4 state nfs4_reset_all_state() refreshes the boot verifier a server sees to trigger that server to wipe this client's state. This function is invoked when an NFSv4.1 server reports that it has revoked some or all of a client's NFSv4 state. To facilitate server trunking discovery, we will eventually want to move the cl_boot_time field to a more global structure. The Uniform Client String model (and specifically, server trunking detection) requires that all servers see the same boot verifier until the client actually does reboot, and not a fresh verifier every time the client unmounts and remounts the server. Without the cl_boot_time field, however, nfs4_reset_all_state() will have to find some other way to force the server to purge the client's NFSv4 state. Because these verifiers are opaque (ie, the server doesn't know or care that they happen to be timestamps), we can force the server to wipe NFSv4 state by updating the boot verifier as we do now, then immediately afterwards establish a fresh client ID using the old boot verifier again. Hopefully there are no extra paranoid server implementations that keep track of the client's boot verifiers and prevent clients from reusing a previous one. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 1526fdf..e6da021 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -24,6 +24,7 @@ enum nfs4_client_state { NFS4CLNT_RECALL_SLOT, NFS4CLNT_LEASE_CONFIRM, NFS4CLNT_SERVER_SCOPE_MISMATCH, + NFS4CLNT_PURGE_STATE, }; enum nfs4_session_state { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ab6b2e5..81ccdbb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3908,8 +3908,15 @@ static void nfs4_construct_boot_verifier(struct nfs_client *clp, { __be32 verf[2]; - verf[0] = (__be32)clp->cl_boot_time.tv_sec; - verf[1] = (__be32)clp->cl_boot_time.tv_nsec; + if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) { + /* An impossible timestamp guarantees this value + * will never match a generated boot time. */ + verf[0] = 0; + verf[1] = (__be32)(NSEC_PER_SEC + 1); + } else { + verf[0] = (__be32)clp->cl_boot_time.tv_sec; + verf[1] = (__be32)clp->cl_boot_time.tv_nsec; + } memcpy(bootverf->data, verf, sizeof(bootverf->data)); } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index f8c06de..32cce4a 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1615,7 +1615,7 @@ void nfs41_handle_recall_slot(struct nfs_client *clp) static void nfs4_reset_all_state(struct nfs_client *clp) { if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { - clp->cl_boot_time = CURRENT_TIME; + set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); nfs4_state_start_reclaim_nograce(clp); nfs4_schedule_state_manager(clp); } @@ -1631,7 +1631,6 @@ static void nfs41_handle_server_reboot(struct nfs_client *clp) static void nfs41_handle_state_revoked(struct nfs_client *clp) { - /* Temporary */ nfs4_reset_all_state(clp); } @@ -1652,6 +1651,10 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags) { if (!flags) return; + + dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n", + __func__, clp->cl_hostname, clp->cl_clientid, flags); + if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED) nfs41_handle_server_reboot(clp); if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED | @@ -1762,6 +1765,12 @@ static void nfs4_state_manager(struct nfs_client *clp) /* Ensure exclusive access to NFSv4 state */ do { + if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) { + nfs4_reclaim_lease(clp); + clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + } + 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); -- cgit v0.10.2