[pnfs] [PATCH 01/16] nfs41: session recovery infrastructure
J. Bruce Fields
bfields at fieldses.org
Wed Jul 16 13:11:37 EDT 2008
On Wed, Jul 16, 2008 at 12:29:50PM +0300, Benny Halevy wrote:
> From: Andy Adamson <andros at netapp.com>
>
> NFSv4.1 session recovery.
> Sessions are created in the "expired" state and session recovery
> is used to initiate session creation as well.
>
> Signed-off-by: Rahul Iyer <iyer at netapp.com>
> Signed-off-by: Benny Halevy <bhalevy at panasas.com>
>
> nfs41: Correctly use new session in exception handling
>
> We will need to handle several layers of session recovery depending on the
> error. As a last resort, we will need to use the big hammer; destroy
> an existing session and create a new one.
>
> The current code creates a new session without destroying the old one.
> Destroy the existing session and initialize a new session before calling
> nfs41_recover_session_sync.
>
> Signed-off-by: Andy Adamson<andros at netapp.com>
> Signed-off-by: Benny Halevy <bhalevy at panasas.com>
>
> nfs41: unexport nfs4_init_session
>
> Signed-off-by: Benny Halevy <bhalevy at panasas.com>
>
> nfs41: correctly expire and validate session
>
> Do not expire session in session_reclaimer error case where the session could
> be ok (create session succeeded) but nfs4_proc_get_lease_time failed. Leave
> the session expiration (as well as new session initialization) to callers.
>
> The session is valid as soon as the create session rpc completes successfully,
> and is not dependant upon a successful nfs4_proc_get_lease_time rpc.
> Move session validation from nfs4_proc_create_session to the success case of
> _nfs4_proc_create_session.
>
> Signed-off-by: Andy Adamson<andros at netapp.com>
> Signed-off-by: Benny Halevy <bhalevy at panasas.com>
>
> nfs41: rename the session expire state
>
> The session expire state used to include session destroy and session init.
> With the session reset state, it now only performs session alloc.
Obviously at some point these changelogs are going to have to get
squashed down to something that explains the motivation for the patch
concisely without the patch history.
--b.
>
> Signed-off-by: Andy Adamson<andros at netapp.com>
> Signed-off-by: Benny Halevy <bhalevy at panasas.com>
> ---
> fs/nfs/Makefile | 1 +
> fs/nfs/client.c | 7 +-
> fs/nfs/internal.h | 4 +
> fs/nfs/nfs41_session_recovery.c | 214 ++++++++++++++++++++++++++++++++
> fs/nfs/nfs4proc.c | 12 ++-
> include/linux/nfs41_session_recovery.h | 42 ++++++
> 6 files changed, 276 insertions(+), 4 deletions(-)
> create mode 100644 fs/nfs/nfs41_session_recovery.c
> create mode 100644 include/linux/nfs41_session_recovery.h
>
> diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
> index ac6170c..de5ea30 100644
> --- a/fs/nfs/Makefile
> +++ b/fs/nfs/Makefile
> @@ -14,4 +14,5 @@ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
> delegation.o idmap.o \
> callback.o callback_xdr.o callback_proc.o \
> nfs4namespace.o
> +nfs-$(CONFIG_NFS_V4_1) += nfs41_session_recovery.o
> nfs-$(CONFIG_SYSCTL) += sysctl.o
> diff --git a/fs/nfs/client.c b/fs/nfs/client.c
> index 3701d2b..f8efdfb 100644
> --- a/fs/nfs/client.c
> +++ b/fs/nfs/client.c
> @@ -37,6 +37,9 @@
> #include <linux/in6.h>
> #include <net/ipv6.h>
> #include <linux/nfs_xdr.h>
> +#if defined(CONFIG_NFS_V4_1)
> +#include <linux/nfs41_session_recovery.h>
> +#endif /* CONFIG_NFS_V4_1 */
>
> #include <asm/system.h>
>
> @@ -1121,8 +1124,8 @@ error:
> /*
> * Allocate and initialize a session if required
> */
> -static int nfs4_init_session(struct nfs_client *clp, struct nfs4_session **spp,
> - struct rpc_clnt *clnt)
> +int nfs4_init_session(struct nfs_client *clp, struct nfs4_session **spp,
> + struct rpc_clnt *clnt)
> {
> int error = 0;
> struct nfs4_session *session = NULL;
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index 45b7c1d..b384482 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -67,6 +67,10 @@ struct nfs_parsed_mount_data {
> };
>
> /* client.c */
> +#ifdef CONFIG_NFS_V4_1
> +extern int nfs4_init_session(struct nfs_client *, struct nfs4_session **,
> + struct rpc_clnt *);
> +#endif /* CONFIG_NFS_V4_1 */
> extern struct rpc_program nfs_program;
>
> extern void nfs_put_client(struct nfs_client *);
> diff --git a/fs/nfs/nfs41_session_recovery.c b/fs/nfs/nfs41_session_recovery.c
> new file mode 100644
> index 0000000..d4d28be
> --- /dev/null
> +++ b/fs/nfs/nfs41_session_recovery.c
> @@ -0,0 +1,214 @@
> +/*
> + * NFSv4.1 session recovery code
> + *
> + * Author: Rahul Iyer <iyer at netapp.com>
> + *
> + * This code is released under GPL. For details see Documentation/COPYING
> + */
> +
> +#if defined(CONFIG_NFS_V4_1)
> +
> +#include <linux/module.h>
> +#include <linux/kthread.h>
> +#include <linux/in.h>
> +#include <linux/fs.h>
> +#include <linux/sunrpc/xdr.h>
> +#include <linux/sunrpc/clnt.h>
> +#include <linux/nfs3.h>
> +#include <linux/nfs_xdr.h>
> +#include <linux/nfs.h>
> +#include <linux/nfs_fs.h>
> +#include <linux/namei.h>
> +#include <linux/nfs_fs_sb.h>
> +#include <linux/nfs41_session_recovery.h>
> +#include "nfs4_fs.h"
> +#include "internal.h"
> +
> +#define NFSDBG_FACILITY NFSDBG_PROC
> +
> +/*
> + * Set the session state == valid. Returns previous value of the session state
> + */
> +int nfs41_set_session_valid(struct nfs4_session *session)
> +{
> + int ret;
> + smp_mb__before_clear_bit();
> + ret = test_and_clear_bit(NFS41_SESSION_ALLOC,
> + &session->session_state);
> + smp_mb__after_clear_bit();
> +
> + return ret;
> +}
> +
> +static int nfs41_start_session_recovery(struct nfs4_session *session)
> +{
> + int ret;
> + ret = test_and_set_bit(NFS41_SESSION_RECOVER, &session->session_state);
> +
> + return ret;
> +}
> +
> +struct reclaimer_arg {
> + struct nfs_client *clp;
> + struct nfs4_session *session;
> +};
> +
> +static int nfs41_end_session_recovery(struct nfs4_session *session)
> +{
> + smp_mb__before_clear_bit();
> + clear_bit(NFS41_SESSION_RECOVER, &session->session_state);
> + smp_mb__after_clear_bit();
> +
> + /*
> + * Wake up sync tasks
> + */
> + wake_up_bit(&session->session_state, NFS41_SESSION_RECOVER);
> + return 0;
> +}
> +
> +static int nfs41_wait_session_recover_sync(struct rpc_clnt *clnt,
> + struct nfs4_session *session)
> +{
> + might_sleep();
> + return wait_on_bit(&session->session_state, NFS41_SESSION_RECOVER,
> + nfs4_wait_bit_killable, TASK_KILLABLE);
> +}
> +
> +static int session_reclaimer(void *arg)
> +{
> + int ret;
> + struct reclaimer_arg *rec = (struct reclaimer_arg *)arg;
> +
> + dprintk("--> %s\n", __func__);
> + allow_signal(SIGKILL);
> +
> + ret = nfs4_proc_create_session(rec->session);
> + if (ret)
> + goto out_error;
> +
> +out:
> + nfs41_end_session_recovery(rec->session);
> + kfree(rec);
> + module_put_and_exit(0);
> + dprintk("<-- %s: status=%d\n", __func__, ret);
> + return ret;
> +out_error:
> + printk(KERN_WARNING "Error: session recovery failed on "
> + "NFSv4.1 server with error %d\n", ret);
> +
> + switch (ret) {
> + case -NFS4ERR_STALE_CLIENTID:
> + case -NFS4ERR_STALE_STATEID:
> + case -NFS4ERR_EXPIRED:
> + set_bit(NFS4CLNT_LEASE_EXPIRED, &rec->clp->cl_state);
> + break;
> + }
> + goto out;
> +}
> +
> +static int nfs41_schedule_session_recovery(struct reclaimer_arg *rec)
> +{
> + struct task_struct *task;
> +
> + dprintk("--> %s: spawning session_reclaimer\n", __func__);
> + __module_get(THIS_MODULE);
> + task = kthread_run(session_reclaimer, rec, "%llx-session-reclaim",
> + (u64 *)rec->session->sess_id);
> +
> + if (!IS_ERR(task)) {
> + dprintk("<-- %s\n", __func__);
> + return 0;
> + }
> +
> + module_put(THIS_MODULE);
> + dprintk("--> %s: failed spawning session_reclaimer: error=%ld\n",
> + __func__, PTR_ERR(task));
> + return PTR_ERR(task);
> +}
> +
> +/*
> + * Session recovery
> + * Called when an op receives a session related error
> + */
> +int nfs41_recover_session(struct nfs_client *clp, struct nfs4_session *session)
> +{
> + struct reclaimer_arg *rec = NULL;
> + int ret;
> +
> + dprintk("--> %s: clp=%p session=%p\n", __func__, clp, session);
> +
> + ret = nfs41_start_session_recovery(session);
> +
> + /*
> + * If we get 1, it means some other thread beat us to us here, so we
> + * just sit back and wait for completion of the recovery process
> + */
> + if (ret) {
> + dprintk("%s: session_recovery already started\n", __func__);
> + ret = 0;
> + goto out;
> + }
> +
> + ret = -ENOMEM;
> + rec = kmalloc(sizeof(*rec), GFP_KERNEL);
> + if (!rec)
> + goto err;
> + rec->clp = clp;
> + rec->session = session;
> +
> + ret = nfs41_schedule_session_recovery(rec);
> + /*
> + * We got an error creating the reclaiming thread, so end the recovery
> + * and bail out
> + */
> + if (ret)
> + goto err;
> +out:
> + dprintk("<-- %s status=%d\n", __func__, ret);
> + return ret;
> +err:
> + nfs41_end_session_recovery(session);
> + kfree(rec);
> + goto out;
> +}
> +
> +int nfs41_recover_session_sync(struct rpc_clnt *clnt, struct nfs_client *clp,
> + struct nfs4_session *session)
> +{
> + int ret;
> +
> + dprintk("--> %s\n", __func__);
> +
> + ret = nfs41_recover_session(clp, session);
> + if (!ret)
> + ret = nfs41_wait_session_recover_sync(clnt, session);
> +
> + dprintk("<-- %s: status=%d\n", __func__, ret);
> + return ret;
> +}
> +
> +/*
> + * nfs41_new_session()
> + *
> + * Big Hammer. Destroy existing session and create a new session
> + */
> +int nfs41_new_session(struct nfs_server *server)
> +{
> + int ret;
> +
> + dprintk(" --> %s\n", __func__);
> +
> + nfs4_put_session(&server->session);
> + ret = nfs4_init_session(server->nfs_client, &server->session,
> + server->client);
> + if (ret)
> + goto out;
> + ret = nfs41_recover_session_sync(server->client, server->nfs_client,
> + server->session);
> +out:
> + dprintk(" <-- %s returns %d\n", __func__, ret);
> + return ret;
> +}
> +EXPORT_SYMBOL(nfs41_new_session);
> +
> +#endif /* CONFIG_NFS_V4_1 */
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index 4ae8a31..091c201 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -56,6 +56,7 @@
> #include "iostat.h"
> #if defined(CONFIG_NFS_V4_1)
> #include "callback.h"
> +#include <linux/nfs41_session_recovery.h>
> #endif /* CONFIG_NFS_V4_1 */
>
> #define NFSDBG_FACILITY NFSDBG_PROC
> @@ -67,7 +68,7 @@ struct nfs4_opendata;
> static int _nfs4_proc_open(struct nfs4_opendata *data);
> static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
> static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs_client *);
> -static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
> +static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
> static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
> static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
> static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
> @@ -3315,7 +3316,7 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
> /* This is the error handling routine for processes that are allowed
> * to sleep.
> */
> -static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
> +static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
> {
> struct nfs_client *clp = server->nfs_client;
> int ret = errorcode;
> @@ -4470,6 +4471,8 @@ struct nfs4_session *nfs4_alloc_session(void)
> if (!session)
> return NULL;
>
> + nfs41_set_session_alloc(session);
> +
> atomic_set(&session->ref_count, 1);
>
> nfs4_init_channel(&session->fore_channel);
> @@ -4582,6 +4585,8 @@ static int _nfs4_proc_create_session(struct nfs4_session *session)
> &session->fore_channel.chan_attrs);
> nfs4_adjust_channel_attrs(&args.bc_attrs,
> &session->back_channel.chan_attrs);
> + nfs41_set_session_valid(session); /* Activate session */
> +
> /* Increment the clientid slot sequence id */
> clp->cl_seqid++;
> }
> @@ -4687,6 +4692,9 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
> server = list_entry(clp->cl_superblocks.next, struct nfs_server,
> client_link);
>
> + if (nfs41_test_session_alloc(server->session))
> + return -NFS4ERR_STALE_CLIENTID;
> +
> /*
> * Why do we need this??
> */
> diff --git a/include/linux/nfs41_session_recovery.h b/include/linux/nfs41_session_recovery.h
> new file mode 100644
> index 0000000..773dab7
> --- /dev/null
> +++ b/include/linux/nfs41_session_recovery.h
> @@ -0,0 +1,42 @@
> +/*
> + * Session Recovery header file
> + *
> + * Author: Rahul Iyer <iyer at netapp.com>
> + *
> + * This code is released under GPL. For details see Documentation/COPYING
> + */
> +
> +#ifndef __NFS41_SESSION_RECOVERY_H__
> +#define __NFS41_SESSION_RECOVERY_H__
> +
> +#if defined(CONFIG_NFS_V4_1)
> +
> +/*
> + * Session state bits
> + */
> +enum nfs41_session_state {
> + NFS41_SESSION_ALLOC = 0,
> + NFS41_SESSION_RECOVER,
> +};
> +
> +/*
> + * Set the session state to alloc
> + */
> +static inline int nfs41_set_session_alloc(struct nfs4_session *session)
> +{
> + return test_and_set_bit(NFS41_SESSION_ALLOC, &session->session_state);
> +}
> +
> +static inline int nfs41_test_session_alloc(struct nfs4_session *session)
> +{
> + return test_bit(NFS41_SESSION_ALLOC, &session->session_state);
> +}
> +
> +int nfs41_set_session_valid(struct nfs4_session *);
> +int nfs41_recover_session(struct nfs_client *, struct nfs4_session *);
> +int nfs41_recover_session_sync(struct rpc_clnt *, struct nfs_client *,
> + struct nfs4_session *);
> +int nfs41_new_session(struct nfs_server *);
> +
> +#endif /* CONFIG_NFS_V4_1 */
> +#endif /* __NFS41_SESSION_RECOVERY_H__ */
> --
> 1.5.6.3
>
> _______________________________________________
> pNFS mailing list
> pNFS at linux-nfs.org
> http://linux-nfs.org/cgi-bin/mailman/listinfo/pnfs
More information about the pNFS
mailing list