svcrdma: Make RDMA_ERROR messages work
[deliverable/linux.git] / net / sunrpc / xprtrdma / svc_rdma_recvfrom.c
index ff4f01e527ecc08a1480ecba8f00d41a90a76571..0f09052110a7621a125eb800cf68dfe728e45f9f 100644 (file)
@@ -144,6 +144,7 @@ int rdma_read_chunk_lcl(struct svcxprt_rdma *xprt,
 
                head->arg.pages[pg_no] = rqstp->rq_arg.pages[pg_no];
                head->arg.page_len += len;
+
                head->arg.len += len;
                if (!pg_off)
                        head->count++;
@@ -160,8 +161,7 @@ int rdma_read_chunk_lcl(struct svcxprt_rdma *xprt,
                        goto err;
                atomic_inc(&xprt->sc_dma_used);
 
-               /* The lkey here is either a local dma lkey or a dma_mr lkey */
-               ctxt->sge[pno].lkey = xprt->sc_dma_lkey;
+               ctxt->sge[pno].lkey = xprt->sc_pd->local_dma_lkey;
                ctxt->sge[pno].length = len;
                ctxt->count++;
 
@@ -567,6 +567,38 @@ static int rdma_read_complete(struct svc_rqst *rqstp,
        return ret;
 }
 
+/* By convention, backchannel calls arrive via rdma_msg type
+ * messages, and never populate the chunk lists. This makes
+ * the RPC/RDMA header small and fixed in size, so it is
+ * straightforward to check the RPC header's direction field.
+ */
+static bool
+svc_rdma_is_backchannel_reply(struct svc_xprt *xprt, struct rpcrdma_msg *rmsgp)
+{
+       __be32 *p = (__be32 *)rmsgp;
+
+       if (!xprt->xpt_bc_xprt)
+               return false;
+
+       if (rmsgp->rm_type != rdma_msg)
+               return false;
+       if (rmsgp->rm_body.rm_chunks[0] != xdr_zero)
+               return false;
+       if (rmsgp->rm_body.rm_chunks[1] != xdr_zero)
+               return false;
+       if (rmsgp->rm_body.rm_chunks[2] != xdr_zero)
+               return false;
+
+       /* sanity */
+       if (p[7] != rmsgp->rm_xid)
+               return false;
+       /* call direction */
+       if (p[8] == cpu_to_be32(RPC_CALL))
+               return false;
+
+       return true;
+}
+
 /*
  * Set up the rqstp thread context to point to the RQ buffer. If
  * necessary, pull additional data from the client with an RDMA_READ
@@ -580,7 +612,6 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
        struct svc_rdma_op_ctxt *ctxt = NULL;
        struct rpcrdma_msg *rmsgp;
        int ret = 0;
-       int len;
 
        dprintk("svcrdma: rqstp=%p\n", rqstp);
 
@@ -622,14 +653,18 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
        rdma_build_arg_xdr(rqstp, ctxt, ctxt->byte_len);
 
        /* Decode the RDMA header. */
-       len = svc_rdma_xdr_decode_req(&rmsgp, rqstp);
-       rqstp->rq_xprt_hlen = len;
-
-       /* If the request is invalid, reply with an error */
-       if (len < 0) {
-               if (len == -ENOSYS)
-                       svc_rdma_send_error(rdma_xprt, rmsgp, ERR_VERS);
-               goto close_out;
+       ret = svc_rdma_xdr_decode_req(&rmsgp, rqstp);
+       if (ret < 0)
+               goto out_err;
+       rqstp->rq_xprt_hlen = ret;
+
+       if (svc_rdma_is_backchannel_reply(xprt, rmsgp)) {
+               ret = svc_rdma_handle_bc_reply(xprt->xpt_bc_xprt, rmsgp,
+                                              &rqstp->rq_arg);
+               svc_rdma_put_context(ctxt, 0);
+               if (ret)
+                       goto repost;
+               return ret;
        }
 
        /* Read read-list data. */
@@ -657,6 +692,11 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
        svc_xprt_copy_addrs(rqstp, xprt);
        return ret;
 
+out_err:
+       svc_rdma_send_error(rdma_xprt, rmsgp, ret);
+       svc_rdma_put_context(ctxt, 0);
+       return 0;
+
  close_out:
        if (ctxt)
                svc_rdma_put_context(ctxt, 1);
@@ -668,4 +708,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
        set_bit(XPT_CLOSE, &xprt->xpt_flags);
 defer:
        return 0;
+
+repost:
+       return svc_rdma_repost_recv(rdma_xprt, GFP_KERNEL);
 }
This page took 0.026556 seconds and 5 git commands to generate.