nfsd4: fix connection allocation in sequence()
authorJ. Bruce Fields <bfields@redhat.com>
Thu, 21 Oct 2010 21:17:31 +0000 (17:17 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Mon, 25 Oct 2010 01:07:07 +0000 (21:07 -0400)
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>
fs/nfsd/nfs4state.c

index ce0412f..d4aa1b5 100644 (file)
@@ -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;