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

Commit fd954ae1 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4.1: Don't loop forever in nfs4_proc_create_session



If a server for some reason keeps sending NFS4ERR_DELAY errors, we can end
up looping forever inside nfs4_proc_create_session, and so the usual
mechanisms for detecting if the nfs_client is dead don't work.

Fix this by ensuring that we loop inside the nfs4_state_manager thread
instead.

Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent fb8a5ba8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ enum nfs4_client_state {
	NFS4CLNT_LAYOUTRECALL,
	NFS4CLNT_SESSION_RESET,
	NFS4CLNT_RECALL_SLOT,
	NFS4CLNT_LEASE_CONFIRM,
};

enum nfs4_session_state {
+7 −38
Original line number Diff line number Diff line
@@ -3754,18 +3754,17 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
		status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
		if (status != -NFS4ERR_CLID_INUSE)
			break;
		if (signalled())
		if (loop != 0) {
			++clp->cl_id_uniquifier;
			break;
		if (loop++ & 1)
		}
		++loop;
		ssleep(clp->cl_lease_time / HZ + 1);
		else
			if (++clp->cl_id_uniquifier == 0)
				break;
	}
	return status;
}

static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
		struct nfs4_setclientid_res *arg,
		struct rpc_cred *cred)
{
@@ -3790,26 +3789,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
	return status;
}

int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
		struct nfs4_setclientid_res *arg,
		struct rpc_cred *cred)
{
	long timeout = 0;
	int err;
	do {
		err = _nfs4_proc_setclientid_confirm(clp, arg, cred);
		switch (err) {
			case 0:
				return err;
			case -NFS4ERR_RESOURCE:
				/* The IBM lawyers misread another document! */
			case -NFS4ERR_DELAY:
				err = nfs4_delay(clp->cl_rpcclient, &timeout);
		}
	} while (err == 0);
	return err;
}

struct nfs4_delegreturndata {
	struct nfs4_delegreturnargs args;
	struct nfs4_delegreturnres res;
@@ -5222,20 +5201,10 @@ int nfs4_proc_create_session(struct nfs_client *clp)
	int status;
	unsigned *ptr;
	struct nfs4_session *session = clp->cl_session;
	long timeout = 0;
	int err;

	dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);

	do {
	status = _nfs4_proc_create_session(clp);
		if (status == -NFS4ERR_DELAY) {
			err = nfs4_delay(clp->cl_rpcclient, &timeout);
			if (err)
				status = err;
		}
	} while (status == -NFS4ERR_DELAY);

	if (status)
		goto out;

+31 −15
Original line number Diff line number Diff line
@@ -64,10 +64,15 @@ static LIST_HEAD(nfs4_clientid_list);

int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{
	struct nfs4_setclientid_res clid;
	struct nfs4_setclientid_res clid = {
		.clientid = clp->cl_clientid,
		.confirm = clp->cl_confirm,
	};
	unsigned short port;
	int status;

	if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
		goto do_confirm;
	port = nfs_callback_tcpport;
	if (clp->cl_addr.ss_family == AF_INET6)
		port = nfs_callback_tcpport6;
@@ -75,10 +80,14 @@ int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
	status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
	if (status != 0)
		goto out;
	clp->cl_clientid = clid.clientid;
	clp->cl_confirm = clid.confirm;
	set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
do_confirm:
	status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
	if (status != 0)
		goto out;
	clp->cl_clientid = clid.clientid;
	clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
	nfs4_schedule_state_renewal(clp);
out:
	return status;
@@ -230,13 +239,18 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{
	int status;

	if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
		goto do_confirm;
	nfs4_begin_drain_session(clp);
	status = nfs4_proc_exchange_id(clp, cred);
	if (status != 0)
		goto out;
	set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
do_confirm:
	status = nfs4_proc_create_session(clp);
	if (status != 0)
		goto out;
	clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
	nfs41_setup_state_renewal(clp);
	nfs_mark_client_ready(clp, NFS_CS_READY);
out:
@@ -1584,11 +1598,14 @@ static int nfs4_recall_slot(struct nfs_client *clp) { return 0; }
 */
static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
{
	if (nfs4_has_session(clp)) {
	switch (status) {
		case -NFS4ERR_DELAY:
	case -NFS4ERR_CLID_INUSE:
	case -NFS4ERR_STALE_CLIENTID:
		clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
		break;
	case -NFS4ERR_DELAY:
	case -EAGAIN:
		ssleep(1);
		break;

	case -EKEYEXPIRED:
@@ -1598,7 +1615,6 @@ static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
	default:
		return;
	}
	}
	set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
}

+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ struct nfs_client {

#ifdef CONFIG_NFS_V4
	u64			cl_clientid;	/* constant */
	nfs4_verifier		cl_confirm;	/* Clientid verifier */
	unsigned long		cl_state;

	spinlock_t		cl_lock;