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

Commit 21b75b01 authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

nfsd4: fix 4.1 connection registration race



If a connection is closed just after a sequence or create_session
is sent over it, we could end up trying to register a callback that will
never get called since the xprt is already marked dead.

Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent c8ddb271
Loading
Loading
Loading
Loading
+12 −4
Original line number Original line Diff line number Diff line
@@ -673,16 +673,17 @@ static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
	spin_unlock(&clp->cl_lock);
	spin_unlock(&clp->cl_lock);
}
}


static void nfsd4_register_conn(struct nfsd4_conn *conn)
static int nfsd4_register_conn(struct nfsd4_conn *conn)
{
{
	conn->cn_xpt_user.callback = nfsd4_conn_lost;
	conn->cn_xpt_user.callback = nfsd4_conn_lost;
	register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
	return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
}
}


static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
{
{
	struct nfsd4_conn *conn;
	struct nfsd4_conn *conn;
	u32 flags = NFS4_CDFC4_FORE;
	u32 flags = NFS4_CDFC4_FORE;
	int ret;


	if (ses->se_flags & SESSION4_BACK_CHAN)
	if (ses->se_flags & SESSION4_BACK_CHAN)
		flags |= NFS4_CDFC4_BACK;
		flags |= NFS4_CDFC4_BACK;
@@ -690,7 +691,10 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
	if (!conn)
	if (!conn)
		return nfserr_jukebox;
		return nfserr_jukebox;
	nfsd4_hash_conn(conn, ses);
	nfsd4_hash_conn(conn, ses);
	nfsd4_register_conn(conn);
	ret = nfsd4_register_conn(conn);
	if (ret)
		/* oops; xprt is already down: */
		nfsd4_conn_lost(&conn->cn_xpt_user);
	return nfs_ok;
	return nfs_ok;
}
}


@@ -1644,6 +1648,7 @@ static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_sessi
{
{
	struct nfs4_client *clp = ses->se_client;
	struct nfs4_client *clp = ses->se_client;
	struct nfsd4_conn *c;
	struct nfsd4_conn *c;
	int ret;


	spin_lock(&clp->cl_lock);
	spin_lock(&clp->cl_lock);
	c = __nfsd4_find_conn(new->cn_xprt, ses);
	c = __nfsd4_find_conn(new->cn_xprt, ses);
@@ -1654,7 +1659,10 @@ static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_sessi
	}
	}
	__nfsd4_hash_conn(new, ses);
	__nfsd4_hash_conn(new, ses);
	spin_unlock(&clp->cl_lock);
	spin_unlock(&clp->cl_lock);
	nfsd4_register_conn(new);
	ret = nfsd4_register_conn(new);
	if (ret)
		/* oops; xprt is already down: */
		nfsd4_conn_lost(&new->cn_xpt_user);
	return;
	return;
}
}


+14 −4
Original line number Original line Diff line number Diff line
@@ -82,18 +82,28 @@ struct svc_xprt {
	struct net		*xpt_net;
	struct net		*xpt_net;
};
};


static inline void register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
{
	spin_lock(&xpt->xpt_lock);
	spin_lock(&xpt->xpt_lock);
	list_add(&u->list, &xpt->xpt_users);
	list_del_init(&u->list);
	spin_unlock(&xpt->xpt_lock);
	spin_unlock(&xpt->xpt_lock);
}
}


static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
static inline int register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
{
	spin_lock(&xpt->xpt_lock);
	spin_lock(&xpt->xpt_lock);
	list_del_init(&u->list);
	if (test_bit(XPT_CLOSE, &xpt->xpt_flags)) {
		/*
		 * The connection is about to be deleted soon (or,
		 * worse, may already be deleted--in which case we've
		 * already notified the xpt_users).
		 */
		spin_unlock(&xpt->xpt_lock);
		spin_unlock(&xpt->xpt_lock);
		return -ENOTCONN;
	}
	list_add(&u->list, &xpt->xpt_users);
	spin_unlock(&xpt->xpt_lock);
	return 0;
}
}


int	svc_reg_xprt_class(struct svc_xprt_class *);
int	svc_reg_xprt_class(struct svc_xprt_class *);