NFSv4.x: Allow multiple callbacks in flight
authorTrond Myklebust <trond.myklebust@primarydata.com>
Sat, 23 Jan 2016 20:18:18 +0000 (15:18 -0500)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 25 Jan 2016 14:36:21 +0000 (09:36 -0500)
Hook the callback channel into the same session management machinery
as we use for the forward channel.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/nfs4session.c
fs/nfs/nfs4session.h

index ff8195bd75ea11ae996e504dd546fa59776aa4d7..5fe1cecbf9f033ce0603639e771c1d82520599b0 100644 (file)
@@ -37,10 +37,11 @@ enum nfs4_callback_opnum {
        OP_CB_ILLEGAL = 10044,
 };
 
+struct nfs4_slot;
 struct cb_process_state {
        __be32                  drc_status;
        struct nfs_client       *clp;
-       u32                     slotid;
+       struct nfs4_slot        *slot;
        u32                     minorversion;
        struct net              *net;
 };
index 79c93b3bbfec781dd3e6b4baec3cd65fd96037d3..efd079d2894607f42e7f5a40a1897de92c517def 100644 (file)
@@ -367,7 +367,7 @@ validate_seqid(const struct nfs4_slot_table *tbl, const struct nfs4_slot *slot,
        if (args->csa_sequenceid == slot->seq_nr) {
                dprintk("%s seqid %u is a replay\n",
                        __func__, args->csa_sequenceid);
-               if (tbl->highest_used_slotid != NFS4_NO_SLOT)
+               if (nfs4_test_locked_slot(tbl, slot->slot_nr))
                        return htonl(NFS4ERR_DELAY);
                /* Signal process_op to set this error on next op */
                if (args->csa_cachethis == 0)
@@ -476,12 +476,18 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
                goto out_unlock;
        }
 
+       status = htonl(NFS4ERR_BADSLOT);
+       slot = nfs4_lookup_slot(tbl, args->csa_slotid);
+       if (IS_ERR(slot))
+               goto out_unlock;
        status = validate_seqid(tbl, slot, args);
        if (status)
                goto out_unlock;
-
-       cps->slotid = args->csa_slotid;
-       tbl->highest_used_slotid = args->csa_slotid;
+       if (!nfs4_try_to_lock_slot(tbl, slot)) {
+               status = htonl(NFS4ERR_DELAY);
+               goto out_unlock;
+       }
+       cps->slot = slot;
 
        memcpy(&res->csr_sessionid, &args->csa_sessionid,
               sizeof(res->csr_sessionid));
index 646cdac73488e96041f2bcad33b4220b096da684..976c90608e5618103d385a3436cafe461873a867 100644 (file)
@@ -752,7 +752,8 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        return htonl(NFS_OK);
 }
 
-static void nfs4_callback_free_slot(struct nfs4_session *session)
+static void nfs4_callback_free_slot(struct nfs4_session *session,
+               struct nfs4_slot *slot)
 {
        struct nfs4_slot_table *tbl = &session->bc_slot_table;
 
@@ -761,15 +762,17 @@ static void nfs4_callback_free_slot(struct nfs4_session *session)
         * Let the state manager know callback processing done.
         * A single slot, so highest used slotid is either 0 or -1
         */
-       tbl->highest_used_slotid = NFS4_NO_SLOT;
+       nfs4_free_slot(tbl, slot);
        nfs4_slot_tbl_drain_complete(tbl);
        spin_unlock(&tbl->slot_tbl_lock);
 }
 
 static void nfs4_cb_free_slot(struct cb_process_state *cps)
 {
-       if (cps->slotid != NFS4_NO_SLOT)
-               nfs4_callback_free_slot(cps->clp->cl_session);
+       if (cps->slot) {
+               nfs4_callback_free_slot(cps->clp->cl_session, cps->slot);
+               cps->slot = NULL;
+       }
 }
 
 #else /* CONFIG_NFS_V4_1 */
@@ -893,7 +896,6 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        struct cb_process_state cps = {
                .drc_status = 0,
                .clp = NULL,
-               .slotid = NFS4_NO_SLOT,
                .net = SVC_NET(rqstp),
        };
        unsigned int nops = 0;
index e23366effcfb1e43bcb81983bcbaeacf2e512002..332d06e64fa910fcfa54ca3fc304e9869f05bc6f 100644 (file)
@@ -135,6 +135,43 @@ static struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table  *tbl,
        return ERR_PTR(-ENOMEM);
 }
 
+static void nfs4_lock_slot(struct nfs4_slot_table *tbl,
+               struct nfs4_slot *slot)
+{
+       u32 slotid = slot->slot_nr;
+
+       __set_bit(slotid, tbl->used_slots);
+       if (slotid > tbl->highest_used_slotid ||
+           tbl->highest_used_slotid == NFS4_NO_SLOT)
+               tbl->highest_used_slotid = slotid;
+       slot->generation = tbl->generation;
+}
+
+/*
+ * nfs4_try_to_lock_slot - Given a slot try to allocate it
+ *
+ * Note: must be called with the slot_tbl_lock held.
+ */
+bool nfs4_try_to_lock_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot)
+{
+       if (nfs4_test_locked_slot(tbl, slot->slot_nr))
+               return false;
+       nfs4_lock_slot(tbl, slot);
+       return true;
+}
+
+/*
+ * nfs4_lookup_slot - Find a slot but don't allocate it
+ *
+ * Note: must be called with the slot_tbl_lock held.
+ */
+struct nfs4_slot *nfs4_lookup_slot(struct nfs4_slot_table *tbl, u32 slotid)
+{
+       if (slotid <= tbl->max_slotid)
+               return nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT);
+       return ERR_PTR(-E2BIG);
+}
+
 /*
  * nfs4_alloc_slot - efficiently look for a free slot
  *
@@ -153,18 +190,11 @@ struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl)
                __func__, tbl->used_slots[0], tbl->highest_used_slotid,
                tbl->max_slotid + 1);
        slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1);
-       if (slotid > tbl->max_slotid)
-               goto out;
-       ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT);
-       if (IS_ERR(ret))
-               goto out;
-       __set_bit(slotid, tbl->used_slots);
-       if (slotid > tbl->highest_used_slotid ||
-                       tbl->highest_used_slotid == NFS4_NO_SLOT)
-               tbl->highest_used_slotid = slotid;
-       ret->generation = tbl->generation;
-
-out:
+       if (slotid <= tbl->max_slotid) {
+               ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT);
+               if (!IS_ERR(ret))
+                       nfs4_lock_slot(tbl, ret);
+       }
        dprintk("<-- %s used_slots=%04lx highest_used=%u slotid=%u\n",
                __func__, tbl->used_slots[0], tbl->highest_used_slotid,
                !IS_ERR(ret) ? ret->slot_nr : NFS4_NO_SLOT);
index e3ea2c5324d68e92591058e896bf478538302a5a..5b51298d1d03765684dd9ea79599169c01c71aaa 100644 (file)
@@ -77,6 +77,8 @@ extern int nfs4_setup_slot_table(struct nfs4_slot_table *tbl,
                unsigned int max_reqs, const char *queue);
 extern void nfs4_shutdown_slot_table(struct nfs4_slot_table *tbl);
 extern struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl);
+extern struct nfs4_slot *nfs4_lookup_slot(struct nfs4_slot_table *tbl, u32 slotid);
+extern bool nfs4_try_to_lock_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot);
 extern void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot);
 extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl);
 bool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl,
@@ -88,6 +90,12 @@ static inline bool nfs4_slot_tbl_draining(struct nfs4_slot_table *tbl)
        return !!test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state);
 }
 
+static inline bool nfs4_test_locked_slot(const struct nfs4_slot_table *tbl,
+               u32 slotid)
+{
+       return !!test_bit(slotid, tbl->used_slots);
+}
+
 #if defined(CONFIG_NFS_V4_1)
 extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl,
                u32 target_highest_slotid);
This page took 0.030304 seconds and 5 git commands to generate.