Skip to content

Commit

Permalink
NFSv4.1: Fix session initialisation races
Browse files Browse the repository at this point in the history
Session initialisation is not complete until the lease manager
has run. We need to ensure that both nfs4_init_session and
nfs4_init_ds_session do so, and that they check for any resulting
errors in clp->cl_cons_state.

Only after this is done, can nfs4_ds_connect check the contents
of clp->cl_exchange_flags.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Andy Adamson <andros@netapp.com>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed May 23, 2012
1 parent acdeb69 commit 7b38c36
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 67 deletions.
16 changes: 0 additions & 16 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,22 +591,6 @@ void nfs_mark_client_ready(struct nfs_client *clp, int state)
wake_up_all(&nfs_client_active_wq);
}

/*
* With sessions, the client is not marked ready until after a
* successful EXCHANGE_ID and CREATE_SESSION.
*
* Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
* other versions of NFS can be tried.
*/
int nfs4_check_client_ready(struct nfs_client *clp)
{
if (!nfs4_has_session(clp))
return 0;
if (clp->cl_cons_state < NFS_CS_READY)
return -EPROTONOSUPPORT;
return 0;
}

/*
* Initialise the timeout values for a connection
*/
Expand Down
3 changes: 1 addition & 2 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ extern struct nfs_server *nfs_clone_server(struct nfs_server *,
struct nfs_fattr *,
rpc_authflavor_t);
extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
extern int nfs4_check_client_ready(struct nfs_client *clp);
extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
const struct sockaddr *ds_addr,
int ds_addrlen, int ds_proto,
Expand Down Expand Up @@ -234,7 +233,7 @@ extern const u32 nfs41_maxwrite_overhead;
extern struct rpc_procinfo nfs4_procedures[];
#endif

extern int nfs4_init_ds_session(struct nfs_client *clp);
extern int nfs4_init_ds_session(struct nfs_client *, unsigned long);

/* proc.c */
void nfs_close_context(struct nfs_open_context *ctx, int is_sync);
Expand Down
23 changes: 1 addition & 22 deletions fs/nfs/nfs4filelayoutdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,28 +203,7 @@ nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds)
goto out;
}

if ((clp->cl_exchange_flags & EXCHGID4_FLAG_MASK_PNFS) != 0) {
if (!is_ds_client(clp)) {
status = -ENODEV;
goto out_put;
}
ds->ds_clp = clp;
dprintk("%s [existing] server=%s\n", __func__,
ds->ds_remotestr);
goto out;
}

/*
* Do not set NFS_CS_CHECK_LEASE_TIME instead set the DS lease to
* be equal to the MDS lease. Renewal is scheduled in create_session.
*/
spin_lock(&mds_srv->nfs_client->cl_lock);
clp->cl_lease_time = mds_srv->nfs_client->cl_lease_time;
spin_unlock(&mds_srv->nfs_client->cl_lock);
clp->cl_last_renewal = jiffies;

/* New nfs_client */
status = nfs4_init_ds_session(clp);
status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
if (status)
goto out_put;

Expand Down
79 changes: 52 additions & 27 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -5603,53 +5603,78 @@ int nfs4_proc_destroy_session(struct nfs4_session *session)
return status;
}

/*
* With sessions, the client is not marked ready until after a
* successful EXCHANGE_ID and CREATE_SESSION.
*
* Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
* other versions of NFS can be tried.
*/
static int nfs41_check_session_ready(struct nfs_client *clp)
{
int ret;

if (clp->cl_cons_state == NFS_CS_SESSION_INITING) {
ret = nfs4_client_recover_expired_lease(clp);
if (ret)
return ret;
}
if (clp->cl_cons_state < NFS_CS_READY)
return -EPROTONOSUPPORT;
return 0;
}

int nfs4_init_session(struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_session *session;
unsigned int rsize, wsize;
int ret;

if (!nfs4_has_session(clp))
return 0;

session = clp->cl_session;
if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
return 0;
spin_lock(&clp->cl_lock);
if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {

rsize = server->rsize;
if (rsize == 0)
rsize = NFS_MAX_FILE_IO_SIZE;
wsize = server->wsize;
if (wsize == 0)
wsize = NFS_MAX_FILE_IO_SIZE;
rsize = server->rsize;
if (rsize == 0)
rsize = NFS_MAX_FILE_IO_SIZE;
wsize = server->wsize;
if (wsize == 0)
wsize = NFS_MAX_FILE_IO_SIZE;

session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
}
spin_unlock(&clp->cl_lock);

ret = nfs4_recover_expired_lease(server);
if (!ret)
ret = nfs4_check_client_ready(clp);
return ret;
return nfs41_check_session_ready(clp);
}

int nfs4_init_ds_session(struct nfs_client *clp)
int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time)
{
struct nfs4_session *session = clp->cl_session;
int ret;

if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
return 0;

ret = nfs4_client_recover_expired_lease(clp);
if (!ret)
/* Test for the DS role */
if (!is_ds_client(clp))
ret = -ENODEV;
if (!ret)
ret = nfs4_check_client_ready(clp);
return ret;
spin_lock(&clp->cl_lock);
if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
/*
* Do not set NFS_CS_CHECK_LEASE_TIME instead set the
* DS lease to be equal to the MDS lease.
*/
clp->cl_lease_time = lease_time;
clp->cl_last_renewal = jiffies;
}
spin_unlock(&clp->cl_lock);

ret = nfs41_check_session_ready(clp);
if (ret)
return ret;
/* Test for the DS role */
if (!is_ds_client(clp))
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_GPL(nfs4_init_ds_session);

Expand Down

0 comments on commit 7b38c36

Please sign in to comment.