Re: [PATCH v4] NFSv4.0: nfs4_do_fsinfo() should not do implicit lease renewals

From: Trond Myklebust
Date: Tue Jan 28 2020 - 10:42:44 EST


On Tue, 2020-01-28 at 15:12 +0000, Robert Milkowski wrote:
> From: Robert Milkowski <rmilkowski@xxxxxxxxx>
>
> Currently, each time nfs4_do_fsinfo() is called it will do an
> implicit
> NFS4 lease renewal, which is not compliant with the NFS4
> specification.
> This can result in a lease being expired by an NFS server.
>
> Commit 83ca7f5ab31f ("NFS: Avoid PUTROOTFH when managing leases")
> introduced implicit client lease renewal in nfs4_do_fsinfo(),
> which can result in the NFSv4.0 lease to expire on a server side,
> and servers returning NFS4ERR_EXPIRED or NFS4ERR_STALE_CLIENTID.
>
> This can easily be reproduced by frequently unmounting a sub-mount,
> then stat'ing it to get it mounted again, which will delay or even
> completely prevent client from sending RENEW operations if no other
> NFS operations are issued. Eventually nfs server will expire client's
> lease and return an error on file access or next RENEW.
>
> This can also happen when a sub-mount is automatically unmounted
> due to inactivity (after nfs_mountpoint_expiry_timeout), then it is
> mounted again via stat(). This can result in a short window during
> which client's lease will expire on a server but not on a client.
> This specific case was observed on production systems.
>
> This patch removes the implicit lease renewal from nfs4_do_fsinfo().
>
> Fixes: 83ca7f5ab31f ("NFS: Avoid PUTROOTFH when managing leases")
> Signed-off-by: Robert Milkowski <rmilkowski@xxxxxxxxx>
> ---
> fs/nfs/nfs4_fs.h | 4 +---
> fs/nfs/nfs4proc.c | 12 ++++++++----
> fs/nfs/nfs4renewd.c | 5 +----
> fs/nfs/nfs4state.c | 4 +---
> 4 files changed, 11 insertions(+), 14 deletions(-)
>
> diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
> index a7a73b1..a5db055 100644
> --- a/fs/nfs/nfs4_fs.h
> +++ b/fs/nfs/nfs4_fs.h
> @@ -446,9 +446,7 @@ extern int nfs4_detect_session_trunking(struct
> nfs_client *clp,
> extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
> extern void nfs4_kill_renewd(struct nfs_client *);
> extern void nfs4_renew_state(struct work_struct *);
> -extern void nfs4_set_lease_period(struct nfs_client *clp,
> - unsigned long lease,
> - unsigned long lastrenewed);
> +extern void nfs4_set_lease_period(struct nfs_client *clp, unsigned
> long lease);
>
>
> /* nfs4state.c */
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index 76d3716..7b2d88b 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -5019,16 +5019,13 @@ static int nfs4_do_fsinfo(struct nfs_server
> *server, struct nfs_fh *fhandle, str
> struct nfs4_exception exception = {
> .interruptible = true,
> };
> - unsigned long now = jiffies;
> int err;
>
> do {
> err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
> trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err);
> if (err == 0) {
> - nfs4_set_lease_period(server->nfs_client,
> - fsinfo->lease_time * HZ,
> - now);
> + nfs4_set_lease_period(server->nfs_client,
> fsinfo->lease_time * HZ);
> break;
> }
> err = nfs4_handle_exception(server, err, &exception);
> @@ -6084,6 +6081,7 @@ int nfs4_proc_setclientid(struct nfs_client
> *clp, u32 program,
> .callback_data = &setclientid,
> .flags = RPC_TASK_TIMEOUT | RPC_TASK_NO_ROUND_ROBIN,
> };
> + unsigned long now = jiffies;
> int status;
>
> /* nfs_client_id4 */
> @@ -6116,6 +6114,9 @@ int nfs4_proc_setclientid(struct nfs_client
> *clp, u32 program,
> clp->cl_acceptor =
> rpcauth_stringify_acceptor(setclientid.sc_cred);
> put_rpccred(setclientid.sc_cred);
> }
> +
> + if(status == 0)
> + do_renew_lease(clp, now);
> out:
> trace_nfs4_setclientid(clp, status);
> dprintk("NFS reply setclientid: %d\n", status);
> @@ -8203,6 +8204,7 @@ static int _nfs4_proc_exchange_id(struct
> nfs_client *clp, const struct cred *cre
> struct rpc_task *task;
> struct nfs41_exchange_id_args *argp;
> struct nfs41_exchange_id_res *resp;
> + unsigned long now = jiffies;
> int status;
>
> task = nfs4_run_exchange_id(clp, cred, sp4_how, NULL);
> @@ -8223,6 +8225,8 @@ static int _nfs4_proc_exchange_id(struct
> nfs_client *clp, const struct cred *cre
> if (status != 0)
> goto out;
>
> + do_renew_lease(clp, now);
> +
> clp->cl_clientid = resp->clientid;
> clp->cl_exchange_flags = resp->flags;
> clp->cl_seqid = resp->seqid;
> diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c
> index 6ea431b..ff876dd 100644
> --- a/fs/nfs/nfs4renewd.c
> +++ b/fs/nfs/nfs4renewd.c
> @@ -138,15 +138,12 @@
> *
> * @clp: pointer to nfs_client
> * @lease: new value for lease period
> - * @lastrenewed: time at which lease was last renewed
> */
> void nfs4_set_lease_period(struct nfs_client *clp,
> - unsigned long lease,
> - unsigned long lastrenewed)
> + unsigned long lease)
> {
> spin_lock(&clp->cl_lock);
> clp->cl_lease_time = lease;
> - clp->cl_last_renewal = lastrenewed;
> spin_unlock(&clp->cl_lock);
>
> /* Cap maximum reconnect timeout at 1/2 lease period */
> diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
> index 3455232..f0b0027 100644
> --- a/fs/nfs/nfs4state.c
> +++ b/fs/nfs/nfs4state.c
> @@ -92,17 +92,15 @@ static int nfs4_setup_state_renewal(struct
> nfs_client *clp)
> {
> int status;
> struct nfs_fsinfo fsinfo;
> - unsigned long now;
>
> if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
> nfs4_schedule_state_renewal(clp);
> return 0;
> }
>
> - now = jiffies;
> status = nfs4_proc_get_lease_time(clp, &fsinfo);
> if (status == 0) {
> - nfs4_set_lease_period(clp, fsinfo.lease_time * HZ,
> now);
> + nfs4_set_lease_period(clp, fsinfo.lease_time * HZ);
> nfs4_schedule_state_renewal(clp);
> }
>

Thanks! This one looks good to me.

Reviewed-by: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx>

--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@xxxxxxxxxxxxxxx