summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2010-10-21 21:17:31 (GMT)
committerJ. Bruce Fields <bfields@redhat.com>2010-10-25 01:07:07 (GMT)
commita663bdd8c5d18d287f7468470816c9e0e66343c1 (patch)
tree9949b3b2b678c1da67e97ee2c312b1a17b98e4c8
parent5d18c1c2a9a74e0f966c257520b8b7f5136c87b3 (diff)
downloadlinux-fsl-qoriq-a663bdd8c5d18d287f7468470816c9e0e66343c1.tar.xz
nfsd4: fix connection allocation in sequence()
We're doing an allocation under a spinlock, and ignoring the possibility of allocation failure. A better fix wouldn't require an unnecessary allocation in the common case, but we'll leave that for later. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c31
1 files changed, 17 insertions, 14 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ce0412f..d4aa1b5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1628,33 +1628,25 @@ out:
return status;
}
-static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s)
+static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_session *s)
{
struct nfsd4_conn *c;
list_for_each_entry(c, &s->se_conns, cn_persession) {
- if (c->cn_xprt == r->rq_xprt) {
+ if (c->cn_xprt == xpt) {
return c;
}
}
return NULL;
}
-static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
- struct nfsd4_conn *c, *new = NULL;
-
- spin_lock(&clp->cl_lock);
- c = __nfsd4_find_conn(rqstp, ses);
- spin_unlock(&clp->cl_lock);
- if (c)
- return;
-
- new = alloc_conn(rqstp, NFS4_CDFC4_FORE);
+ struct nfsd4_conn *c;
spin_lock(&clp->cl_lock);
- c = __nfsd4_find_conn(rqstp, ses);
+ c = __nfsd4_find_conn(new->cn_xprt, ses);
if (c) {
spin_unlock(&clp->cl_lock);
free_conn(new);
@@ -1674,11 +1666,20 @@ nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct nfsd4_session *session;
struct nfsd4_slot *slot;
+ struct nfsd4_conn *conn;
int status;
if (resp->opcnt != 1)
return nfserr_sequence_pos;
+ /*
+ * Will be either used or freed by nfsd4_sequence_check_conn
+ * below.
+ */
+ conn = alloc_conn(rqstp, NFS4_CDFC4_FORE);
+ if (!conn)
+ return nfserr_jukebox;
+
spin_lock(&client_lock);
status = nfserr_badsession;
session = find_in_sessionid_hashtbl(&seq->sessionid);
@@ -1710,7 +1711,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
if (status)
goto out;
- nfsd4_sequence_check_conn(rqstp, session);
+ nfsd4_sequence_check_conn(conn, session);
+ conn = NULL;
/* Success! bump slot seqid */
slot->sl_inuse = true;
@@ -1726,6 +1728,7 @@ out:
nfsd4_get_session(cstate->session);
atomic_inc(&session->se_client->cl_refcount);
}
+ kfree(conn);
spin_unlock(&client_lock);
dprintk("%s: return %d\n", __func__, ntohl(status));
return status;