diff --git a/fs/nfs/client.c b/fs/nfs/client.c index bb7432d83b5a43c5d77e3ea45fa2aa0a4a2f8a6e..d28a987f569e81ca4d289d84661763709b082651 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -366,7 +366,8 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; /* Don't match clients that failed to initialise properly */ - if (clp->cl_cons_state != NFS_CS_READY) + if (!(clp->cl_cons_state == NFS_CS_READY || + clp->cl_cons_state == NFS_CS_SESSION_INITING)) continue; /* Different NFS versions cannot share the same nfs_client */ @@ -499,7 +500,7 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in nfs_free_client(new); error = wait_event_killable(nfs_client_active_wq, - clp->cl_cons_state != NFS_CS_INITING); + clp->cl_cons_state < NFS_CS_INITING); if (error < 0) { nfs_put_client(clp); return ERR_PTR(-ERESTARTSYS); @@ -520,7 +521,7 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in /* * Mark a server as ready or failed */ -static void nfs_mark_client_ready(struct nfs_client *clp, int state) +void nfs_mark_client_ready(struct nfs_client *clp, int state) { clp->cl_cons_state = state; wake_up_all(&nfs_client_active_wq); @@ -1135,7 +1136,8 @@ static int nfs4_init_client(struct nfs_client *clp, if (error < 0) goto error; - nfs_mark_client_ready(clp, NFS_CS_READY); + if (!nfs4_has_session(clp)) + nfs_mark_client_ready(clp, NFS_CS_READY); return 0; error: diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index f62bc52261558dff3a0486e25ae3eb5beed99a1f..f3b310e8ea03588eb2e3d0223380f7caf4971e3c 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -100,6 +100,7 @@ extern void nfs_free_server(struct nfs_server *server); extern struct nfs_server *nfs_clone_server(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); +extern void nfs_mark_client_ready(struct nfs_client *clp, int state); #ifdef CONFIG_PROC_FS extern int __init nfs_fs_proc_init(void); extern void nfs_fs_proc_exit(void); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index eccf4e93e7d7a8fb33832a17aab662a06b562c75..288717abaddcb5f9df477e8f8cf0a7adfe372136 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -44,6 +44,7 @@ enum nfs4_client_state { NFS4CLNT_RECLAIM_REBOOT, NFS4CLNT_RECLAIM_NOGRACE, NFS4CLNT_DELEGRETURN, + NFS4CLNT_SESSION_SETUP, }; /* @@ -208,6 +209,7 @@ extern int nfs4_setup_sequence(struct nfs_client *clp, int cache_reply, struct rpc_task *task); extern void nfs4_destroy_session(struct nfs4_session *session); extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); +extern int nfs4_proc_create_session(struct nfs_client *, int reset); #else /* CONFIG_NFS_v4_1 */ static inline int nfs4_setup_sequence(struct nfs_client *clp, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0b121474024832553280ad7e25893827b11f2fd1..7fc0c9c8f5e3001efbfebdd9c791141ac61778f3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4382,6 +4382,16 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) session = kzalloc(sizeof(struct nfs4_session), GFP_KERNEL); if (!session) return NULL; + + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + /* + * The create session reply races with the server back + * channel probe. Mark the client NFS_CS_SESSION_INITING + * so that the client back channel can find the + * nfs_client struct + */ + clp->cl_cons_state = NFS_CS_SESSION_INITING; + tbl = &session->fc_slot_table; spin_lock_init(&tbl->slot_tbl_lock); rpc_init_wait_queue(&tbl->slot_tbl_waitq, "Slot table"); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index bc683ed477e1045c100c72a0216558152a5520f6..df5b4807daa72d729a7ed140adfa800883a66918 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1113,6 +1113,27 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) return status; } +#ifdef CONFIG_NFS_V4_1 + +static int nfs4_initialize_session(struct nfs_client *clp) +{ + int status; + + status = nfs4_proc_create_session(clp, 0); + if (!status) { + nfs_mark_client_ready(clp, NFS_CS_READY); + } else if (status == -NFS4ERR_STALE_CLIENTID) { + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state); + } else { + nfs_mark_client_ready(clp, status); + } + return status; +} +#else /* CONFIG_NFS_V4_1 */ +static int nfs4_initialize_session(struct nfs_client *clp) { return 0; } +#endif /* CONFIG_NFS_V4_1 */ + static void nfs4_state_manager(struct nfs_client *clp) { int status = 0; @@ -1126,6 +1147,9 @@ static void nfs4_state_manager(struct nfs_client *clp) set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); if (status == -EAGAIN) continue; + if (clp->cl_cons_state == + NFS_CS_SESSION_INITING) + nfs_mark_client_ready(clp, status); goto out_error; } clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); @@ -1136,7 +1160,16 @@ static void nfs4_state_manager(struct nfs_client *clp) if (status != 0) continue; } - + /* Setup the session */ + if (nfs4_has_session(clp) && + test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) { + status = nfs4_initialize_session(clp); + if (status) { + if (status == -NFS4ERR_STALE_CLIENTID) + continue; + goto out_error; + } + } /* First recover reboot state... */ if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 435ed556efb594d5006a586603639b4e3f2c75de..d0902ccec9cedaa3eab23f75c8ea9ad9c07379bb 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -24,6 +24,7 @@ struct nfs_client { int cl_cons_state; /* current construction state (-ve: init error) */ #define NFS_CS_READY 0 /* ready to be used */ #define NFS_CS_INITING 1 /* busy initialising */ +#define NFS_CS_SESSION_INITING 2 /* busy initialising session */ unsigned long cl_res_state; /* NFS resources state */ #define NFS_CS_CALLBACK 1 /* - callback started */ #define NFS_CS_IDMAP 2 /* - idmap started */