Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b38c368 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4.1: Fix session initialisation races



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: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Cc: Andy Adamson <andros@netapp.com>
parent acdeb69d
Loading
Loading
Loading
Loading
+0 −16
Original line number Diff line number Diff line
@@ -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
 */
+1 −2
Original line number Diff line number Diff line
@@ -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,
@@ -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);
+1 −22
Original line number Diff line number Diff line
@@ -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;

+52 −27
Original line number Diff line number Diff line
@@ -5603,19 +5603,39 @@ 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)
@@ -5626,30 +5646,35 @@ int nfs4_init_session(struct nfs_server *server)

		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;
	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 = nfs4_client_recover_expired_lease(clp);
	if (!ret)
	ret = nfs41_check_session_ready(clp);
	if (ret)
		return ret;
	/* Test for the DS role */
	if (!is_ds_client(clp))
			ret = -ENODEV;
	if (!ret)
		ret = nfs4_check_client_ready(clp);
	return ret;

		return -ENODEV;
	return 0;
}
EXPORT_SYMBOL_GPL(nfs4_init_ds_session);