xprtrdma: Refactor MR recovery work queues
[deliverable/linux.git] / net / sunrpc / xprtrdma / fmr_ops.c
index df5fe17861056ea3b891191cad7e93b6e9579458..4837ced20b65c5cd7e5ba4c01bbbb1186ba4dfa5 100644 (file)
  * verb (fmr_op_unmap).
  */
 
-/* Transport recovery
- *
- * After a transport reconnect, fmr_op_map re-uses the MR already
- * allocated for the RPC, but generates a fresh rkey then maps the
- * MR again. This process is synchronous.
- */
-
 #include "xprt_rdma.h"
 
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
@@ -41,30 +34,6 @@ enum {
                                          IB_ACCESS_REMOTE_READ,
 };
 
-static struct workqueue_struct *fmr_recovery_wq;
-
-#define FMR_RECOVERY_WQ_FLAGS          (WQ_UNBOUND)
-
-int
-fmr_alloc_recovery_wq(void)
-{
-       fmr_recovery_wq = alloc_workqueue("fmr_recovery", WQ_UNBOUND, 0);
-       return !fmr_recovery_wq ? -ENOMEM : 0;
-}
-
-void
-fmr_destroy_recovery_wq(void)
-{
-       struct workqueue_struct *wq;
-
-       if (!fmr_recovery_wq)
-               return;
-
-       wq = fmr_recovery_wq;
-       fmr_recovery_wq = NULL;
-       destroy_workqueue(wq);
-}
-
 static int
 __fmr_init(struct rpcrdma_mw *mw, struct ib_pd *pd)
 {
@@ -115,66 +84,56 @@ __fmr_unmap(struct rpcrdma_mw *mw)
        return rc;
 }
 
-static void
-__fmr_dma_unmap(struct rpcrdma_mw *mw)
-{
-       struct rpcrdma_xprt *r_xprt = mw->mw_xprt;
-
-       ib_dma_unmap_sg(r_xprt->rx_ia.ri_device,
-                       mw->mw_sg, mw->mw_nents, mw->mw_dir);
-       rpcrdma_put_mw(r_xprt, mw);
-}
-
-static void
-__fmr_reset_and_unmap(struct rpcrdma_mw *mw)
-{
-       int rc;
-
-       /* ORDER */
-       rc = __fmr_unmap(mw);
-       if (rc) {
-               pr_warn("rpcrdma: ib_unmap_fmr status %d, fmr %p orphaned\n",
-                       rc, mw);
-               return;
-       }
-       __fmr_dma_unmap(mw);
-}
-
 static void
 __fmr_release(struct rpcrdma_mw *r)
 {
+       LIST_HEAD(unmap_list);
        int rc;
 
        kfree(r->fmr.fm_physaddrs);
        kfree(r->mw_sg);
 
+       /* In case this one was left mapped, try to unmap it
+        * to prevent dealloc_fmr from failing with EBUSY
+        */
+       rc = __fmr_unmap(r);
+       if (rc)
+               pr_err("rpcrdma: final ib_unmap_fmr for %p failed %i\n",
+                      r, rc);
+
        rc = ib_dealloc_fmr(r->fmr.fm_mr);
        if (rc)
                pr_err("rpcrdma: final ib_dealloc_fmr for %p returned %i\n",
                       r, rc);
 }
 
-/* Deferred reset of a single FMR. Generate a fresh rkey by
- * replacing the MR. There's no recovery if this fails.
+/* Reset of a single FMR.
+ *
+ * There's no recovery if this fails. The FMR is abandoned, but
+ * remains in rb_all. It will be cleaned up when the transport is
+ * destroyed.
  */
 static void
-__fmr_recovery_worker(struct work_struct *work)
+fmr_op_recover_mr(struct rpcrdma_mw *mw)
 {
-       struct rpcrdma_mw *mw = container_of(work, struct rpcrdma_mw,
-                                            mw_work);
+       struct rpcrdma_xprt *r_xprt = mw->mw_xprt;
+       int rc;
 
-       __fmr_reset_and_unmap(mw);
-       return;
-}
+       /* ORDER: invalidate first */
+       rc = __fmr_unmap(mw);
 
-/* A broken MR was discovered in a context that can't sleep.
- * Defer recovery to the recovery worker.
- */
-static void
-__fmr_queue_recovery(struct rpcrdma_mw *mw)
-{
-       INIT_WORK(&mw->mw_work, __fmr_recovery_worker);
-       queue_work(fmr_recovery_wq, &mw->mw_work);
+       /* ORDER: then DMA unmap */
+       ib_dma_unmap_sg(r_xprt->rx_ia.ri_device,
+                       mw->mw_sg, mw->mw_nents, mw->mw_dir);
+       if (rc) {
+               pr_err("rpcrdma: FMR reset status %d, %p orphaned\n",
+                      rc, mw);
+               r_xprt->rx_stats.mrs_orphaned++;
+               return;
+       }
+
+       rpcrdma_put_mw(r_xprt, mw);
+       r_xprt->rx_stats.mrs_recovered++;
 }
 
 static int
@@ -245,16 +204,11 @@ fmr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
 
        mw = seg1->rl_mw;
        seg1->rl_mw = NULL;
-       if (!mw) {
-               mw = rpcrdma_get_mw(r_xprt);
-               if (!mw)
-                       return -ENOMEM;
-       } else {
-               /* this is a retransmit; generate a fresh rkey */
-               rc = __fmr_unmap(mw);
-               if (rc)
-                       return rc;
-       }
+       if (mw)
+               rpcrdma_defer_mr_recovery(mw);
+       mw = rpcrdma_get_mw(r_xprt);
+       if (!mw)
+               return -ENOMEM;
 
        pageoff = offset_in_page(seg1->mr_offset);
        seg1->mr_offset -= pageoff;     /* start of page */
@@ -309,7 +263,7 @@ out_maperr:
        pr_err("rpcrdma: ib_map_phys_fmr %u@0x%llx+%i (%d) status %i\n",
               len, (unsigned long long)dma_pages[0],
               pageoff, mw->mw_nents, rc);
-       __fmr_dma_unmap(mw);
+       rpcrdma_defer_mr_recovery(mw);
        return rc;
 }
 
@@ -332,7 +286,7 @@ fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
        /* ORDER: Invalidate all of the req's MRs first
         *
         * ib_unmap_fmr() is slow, so use a single call instead
-        * of one call per mapped MR.
+        * of one call per mapped FMR.
         */
        for (i = 0, nchunks = req->rl_nchunks; nchunks; nchunks--) {
                seg = &req->rl_segments[i];
@@ -344,7 +298,7 @@ fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
        }
        rc = ib_unmap_fmr(&unmap_list);
        if (rc)
-               pr_warn("%s: ib_unmap_fmr failed (%i)\n", __func__, rc);
+               goto out_reset;
 
        /* ORDER: Now DMA unmap all of the req's MRs, and return
         * them to the free MW list.
@@ -354,7 +308,9 @@ fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
                mw = seg->rl_mw;
 
                list_del_init(&mw->fmr.fm_mr->list);
-               __fmr_dma_unmap(mw);
+               ib_dma_unmap_sg(r_xprt->rx_ia.ri_device,
+                               mw->mw_sg, mw->mw_nents, mw->mw_dir);
+               rpcrdma_put_mw(r_xprt, mw);
 
                i += seg->mr_nsegs;
                seg->mr_nsegs = 0;
@@ -362,6 +318,20 @@ fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
        }
 
        req->rl_nchunks = 0;
+       return;
+
+out_reset:
+       pr_err("rpcrdma: ib_unmap_fmr failed (%i)\n", rc);
+
+       for (i = 0, nchunks = req->rl_nchunks; nchunks; nchunks--) {
+               seg = &req->rl_segments[i];
+               mw = seg->rl_mw;
+
+               list_del_init(&mw->fmr.fm_mr->list);
+               fmr_op_recover_mr(mw);
+
+               i += seg->mr_nsegs;
+       }
 }
 
 /* Use a slow, safe mechanism to invalidate all memory regions
@@ -380,9 +350,9 @@ fmr_op_unmap_safe(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
                mw = seg->rl_mw;
 
                if (sync)
-                       __fmr_reset_and_unmap(mw);
+                       fmr_op_recover_mr(mw);
                else
-                       __fmr_queue_recovery(mw);
+                       rpcrdma_defer_mr_recovery(mw);
 
                i += seg->mr_nsegs;
                seg->mr_nsegs = 0;
@@ -407,6 +377,7 @@ const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops = {
        .ro_map                         = fmr_op_map,
        .ro_unmap_sync                  = fmr_op_unmap_sync,
        .ro_unmap_safe                  = fmr_op_unmap_safe,
+       .ro_recover_mr                  = fmr_op_recover_mr,
        .ro_open                        = fmr_op_open,
        .ro_maxpages                    = fmr_op_maxpages,
        .ro_init                        = fmr_op_init,
This page took 0.028027 seconds and 5 git commands to generate.