svcrdma: Use rdma_rw API in RPC reply path
authorChuck Lever <chuck.lever@oracle.com>
Sun, 9 Apr 2017 17:06:25 +0000 (13:06 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Tue, 25 Apr 2017 21:25:55 +0000 (17:25 -0400)
The current svcrdma sendto code path posts one RDMA Write WR at a
time. Each of these Writes typically carries a small number of pages
(for instance, up to 30 pages for mlx4 devices). That means a 1MB
NFS READ reply requires 9 ib_post_send() calls for the Write WRs,
and one for the Send WR carrying the actual RPC Reply message.

Instead, use the new rdma_rw API. The details of Write WR chain
construction and memory registration are taken care of in the RDMA
core. svcrdma can focus on the details of the RPC-over-RDMA
protocol. This gives three main benefits:

1. All Write WRs for one RDMA segment are posted in a single chain.
As few as one ib_post_send() for each Write chunk.

2. The Write path can now use FRWR to register the Write buffers.
If the device's maximum page list depth is large, this means a
single Write WR is needed for each RPC's Write chunk data.

3. The new code introduces support for RPCs that carry both a Write
list and a Reply chunk. This combination can be used for an NFSv4
READ where the data payload is large, and thus is removed from the
Payload Stream, but the Payload Stream is still larger than the
inline threshold.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
include/linux/sunrpc/svc_rdma.h
net/sunrpc/xprtrdma/svc_rdma_backchannel.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c

index ca08671fb7e2b37448621ba9945910a11b79e2aa..599ee03ee3fb13bbc0bff92879526084c2358935 100644 (file)
@@ -212,7 +212,6 @@ extern int svc_rdma_xdr_decode_req(struct xdr_buf *);
 extern int svc_rdma_xdr_encode_error(struct svcxprt_rdma *,
                                     struct rpcrdma_msg *,
                                     enum rpcrdma_errcode, __be32 *);
-extern void svc_rdma_xdr_encode_write_list(struct rpcrdma_msg *, int);
 extern void svc_rdma_xdr_encode_reply_array(struct rpcrdma_write_array *, int);
 extern void svc_rdma_xdr_encode_array_chunk(struct rpcrdma_write_array *, int,
                                            __be32, __be64, u32);
index 0305b33d482fc054f8f731fb3f26f1a0f082af05..bf185b79c98fb502bced8d5fcda7868222fee78f 100644 (file)
@@ -90,9 +90,9 @@ out_notfound:
  * Caller holds the connection's mutex and has already marshaled
  * the RPC/RDMA request.
  *
- * This is similar to svc_rdma_reply, but takes an rpc_rqst
- * instead, does not support chunks, and avoids blocking memory
- * allocation.
+ * This is similar to svc_rdma_send_reply_msg, but takes a struct
+ * rpc_rqst instead, does not support chunks, and avoids blocking
+ * memory allocation.
  *
  * XXX: There is still an opportunity to block in svc_rdma_send()
  * if there are no SQ entries to post the Send. This may occur if
index 2eb3df698e11b880fc715a6d712b9e187655fb90..ce62b78e5bc92048c3717aeb98c1b5e329ceeff6 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2016 Oracle. All rights reserved.
  * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved.
  * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved.
  *
  * Author: Tom Tucker <tom@opengridcomputing.com>
  */
 
+/* Operation
+ *
+ * The main entry point is svc_rdma_sendto. This is called by the
+ * RPC server when an RPC Reply is ready to be transmitted to a client.
+ *
+ * The passed-in svc_rqst contains a struct xdr_buf which holds an
+ * XDR-encoded RPC Reply message. sendto must construct the RPC-over-RDMA
+ * transport header, post all Write WRs needed for this Reply, then post
+ * a Send WR conveying the transport header and the RPC message itself to
+ * the client.
+ *
+ * svc_rdma_sendto must fully transmit the Reply before returning, as
+ * the svc_rqst will be recycled as soon as sendto returns. Remaining
+ * resources referred to by the svc_rqst are also recycled at that time.
+ * Therefore any resources that must remain longer must be detached
+ * from the svc_rqst and released later.
+ *
+ * Page Management
+ *
+ * The I/O that performs Reply transmission is asynchronous, and may
+ * complete well after sendto returns. Thus pages under I/O must be
+ * removed from the svc_rqst before sendto returns.
+ *
+ * The logic here depends on Send Queue and completion ordering. Since
+ * the Send WR is always posted last, it will always complete last. Thus
+ * when it completes, it is guaranteed that all previous Write WRs have
+ * also completed.
+ *
+ * Write WRs are constructed and posted. Each Write segment gets its own
+ * svc_rdma_rw_ctxt, allowing the Write completion handler to find and
+ * DMA-unmap the pages under I/O for that Write segment. The Write
+ * completion handler does not release any pages.
+ *
+ * When the Send WR is constructed, it also gets its own svc_rdma_op_ctxt.
+ * The ownership of all of the Reply's pages are transferred into that
+ * ctxt, the Send WR is posted, and sendto returns.
+ *
+ * The svc_rdma_op_ctxt is presented when the Send WR completes. The
+ * Send completion handler finally releases the Reply's pages.
+ *
+ * This mechanism also assumes that completions on the transport's Send
+ * Completion Queue do not run in parallel. Otherwise a Write completion
+ * and Send completion running at the same time could release pages that
+ * are still DMA-mapped.
+ *
+ * Error Handling
+ *
+ * - If the Send WR is posted successfully, it will either complete
+ *   successfully, or get flushed. Either way, the Send completion
+ *   handler releases the Reply's pages.
+ * - If the Send WR cannot be not posted, the forward path releases
+ *   the Reply's pages.
+ *
+ * This handles the case, without the use of page reference counting,
+ * where two different Write segments send portions of the same page.
+ */
+
 #include <linux/sunrpc/debug.h>
 #include <linux/sunrpc/rpc_rdma.h>
 #include <linux/spinlock.h>
@@ -55,6 +113,133 @@ static u32 xdr_padsize(u32 len)
        return (len & 3) ? (4 - (len & 3)) : 0;
 }
 
+/* Returns length of transport header, in bytes.
+ */
+static unsigned int svc_rdma_reply_hdr_len(__be32 *rdma_resp)
+{
+       unsigned int nsegs;
+       __be32 *p;
+
+       p = rdma_resp;
+
+       /* RPC-over-RDMA V1 replies never have a Read list. */
+       p += rpcrdma_fixed_maxsz + 1;
+
+       /* Skip Write list. */
+       while (*p++ != xdr_zero) {
+               nsegs = be32_to_cpup(p++);
+               p += nsegs * rpcrdma_segment_maxsz;
+       }
+
+       /* Skip Reply chunk. */
+       if (*p++ != xdr_zero) {
+               nsegs = be32_to_cpup(p++);
+               p += nsegs * rpcrdma_segment_maxsz;
+       }
+
+       return (unsigned long)p - (unsigned long)rdma_resp;
+}
+
+/* One Write chunk is copied from Call transport header to Reply
+ * transport header. Each segment's length field is updated to
+ * reflect number of bytes consumed in the segment.
+ *
+ * Returns number of segments in this chunk.
+ */
+static unsigned int xdr_encode_write_chunk(__be32 *dst, __be32 *src,
+                                          unsigned int remaining)
+{
+       unsigned int i, nsegs;
+       u32 seg_len;
+
+       /* Write list discriminator */
+       *dst++ = *src++;
+
+       /* number of segments in this chunk */
+       nsegs = be32_to_cpup(src);
+       *dst++ = *src++;
+
+       for (i = nsegs; i; i--) {
+               /* segment's RDMA handle */
+               *dst++ = *src++;
+
+               /* bytes returned in this segment */
+               seg_len = be32_to_cpu(*src);
+               if (remaining >= seg_len) {
+                       /* entire segment was consumed */
+                       *dst = *src;
+                       remaining -= seg_len;
+               } else {
+                       /* segment only partly filled */
+                       *dst = cpu_to_be32(remaining);
+                       remaining = 0;
+               }
+               dst++; src++;
+
+               /* segment's RDMA offset */
+               *dst++ = *src++;
+               *dst++ = *src++;
+       }
+
+       return nsegs;
+}
+
+/* The client provided a Write list in the Call message. Fill in
+ * the segments in the first Write chunk in the Reply's transport
+ * header with the number of bytes consumed in each segment.
+ * Remaining chunks are returned unused.
+ *
+ * Assumptions:
+ *  - Client has provided only one Write chunk
+ */
+static void svc_rdma_xdr_encode_write_list(__be32 *rdma_resp, __be32 *wr_ch,
+                                          unsigned int consumed)
+{
+       unsigned int nsegs;
+       __be32 *p, *q;
+
+       /* RPC-over-RDMA V1 replies never have a Read list. */
+       p = rdma_resp + rpcrdma_fixed_maxsz + 1;
+
+       q = wr_ch;
+       while (*q != xdr_zero) {
+               nsegs = xdr_encode_write_chunk(p, q, consumed);
+               q += 2 + nsegs * rpcrdma_segment_maxsz;
+               p += 2 + nsegs * rpcrdma_segment_maxsz;
+               consumed = 0;
+       }
+
+       /* Terminate Write list */
+       *p++ = xdr_zero;
+
+       /* Reply chunk discriminator; may be replaced later */
+       *p = xdr_zero;
+}
+
+/* The client provided a Reply chunk in the Call message. Fill in
+ * the segments in the Reply chunk in the Reply message with the
+ * number of bytes consumed in each segment.
+ *
+ * Assumptions:
+ * - Reply can always fit in the provided Reply chunk
+ */
+static void svc_rdma_xdr_encode_reply_chunk(__be32 *rdma_resp, __be32 *rp_ch,
+                                           unsigned int consumed)
+{
+       __be32 *p;
+
+       /* Find the Reply chunk in the Reply's xprt header.
+        * RPC-over-RDMA V1 replies never have a Read list.
+        */
+       p = rdma_resp + rpcrdma_fixed_maxsz + 1;
+
+       /* Skip past Write list */
+       while (*p++ != xdr_zero)
+               p += 1 + be32_to_cpup(p) * rpcrdma_segment_maxsz;
+
+       xdr_encode_write_chunk(p, rp_ch, consumed);
+}
+
 int svc_rdma_map_xdr(struct svcxprt_rdma *xprt,
                     struct xdr_buf *xdr,
                     struct svc_rdma_req_map *vec,
@@ -123,45 +308,14 @@ int svc_rdma_map_xdr(struct svcxprt_rdma *xprt,
        return 0;
 }
 
-static dma_addr_t dma_map_xdr(struct svcxprt_rdma *xprt,
-                             struct xdr_buf *xdr,
-                             u32 xdr_off, size_t len, int dir)
-{
-       struct page *page;
-       dma_addr_t dma_addr;
-       if (xdr_off < xdr->head[0].iov_len) {
-               /* This offset is in the head */
-               xdr_off += (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
-               page = virt_to_page(xdr->head[0].iov_base);
-       } else {
-               xdr_off -= xdr->head[0].iov_len;
-               if (xdr_off < xdr->page_len) {
-                       /* This offset is in the page list */
-                       xdr_off += xdr->page_base;
-                       page = xdr->pages[xdr_off >> PAGE_SHIFT];
-                       xdr_off &= ~PAGE_MASK;
-               } else {
-                       /* This offset is in the tail */
-                       xdr_off -= xdr->page_len;
-                       xdr_off += (unsigned long)
-                               xdr->tail[0].iov_base & ~PAGE_MASK;
-                       page = virt_to_page(xdr->tail[0].iov_base);
-               }
-       }
-       dma_addr = ib_dma_map_page(xprt->sc_cm_id->device, page, xdr_off,
-                                  min_t(size_t, PAGE_SIZE, len), dir);
-       return dma_addr;
-}
-
 /* Parse the RPC Call's transport header.
  */
-static void svc_rdma_get_write_arrays(struct rpcrdma_msg *rmsgp,
-                                     struct rpcrdma_write_array **write,
-                                     struct rpcrdma_write_array **reply)
+static void svc_rdma_get_write_arrays(__be32 *rdma_argp,
+                                     __be32 **write, __be32 **reply)
 {
        __be32 *p;
 
-       p = (__be32 *)&rmsgp->rm_body.rm_chunks[0];
+       p = rdma_argp + rpcrdma_fixed_maxsz;
 
        /* Read list */
        while (*p++ != xdr_zero)
@@ -169,7 +323,7 @@ static void svc_rdma_get_write_arrays(struct rpcrdma_msg *rmsgp,
 
        /* Write list */
        if (*p != xdr_zero) {
-               *write = (struct rpcrdma_write_array *)p;
+               *write = p;
                while (*p++ != xdr_zero)
                        p += 1 + be32_to_cpu(*p) * 4;
        } else {
@@ -179,7 +333,7 @@ static void svc_rdma_get_write_arrays(struct rpcrdma_msg *rmsgp,
 
        /* Reply chunk */
        if (*p != xdr_zero)
-               *reply = (struct rpcrdma_write_array *)p;
+               *reply = p;
        else
                *reply = NULL;
 }
@@ -210,6 +364,32 @@ static u32 svc_rdma_get_inv_rkey(__be32 *rdma_argp,
        return be32_to_cpup(p);
 }
 
+/* ib_dma_map_page() is used here because svc_rdma_dma_unmap()
+ * is used during completion to DMA-unmap this memory, and
+ * it uses ib_dma_unmap_page() exclusively.
+ */
+static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma,
+                               struct svc_rdma_op_ctxt *ctxt,
+                               unsigned int sge_no,
+                               unsigned char *base,
+                               unsigned int len)
+{
+       unsigned long offset = (unsigned long)base & ~PAGE_MASK;
+       struct ib_device *dev = rdma->sc_cm_id->device;
+       dma_addr_t dma_addr;
+
+       dma_addr = ib_dma_map_page(dev, virt_to_page(base),
+                                  offset, len, DMA_TO_DEVICE);
+       if (ib_dma_mapping_error(dev, dma_addr))
+               return -EIO;
+
+       ctxt->sge[sge_no].addr = dma_addr;
+       ctxt->sge[sge_no].length = len;
+       ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey;
+       svc_rdma_count_mappings(rdma, ctxt);
+       return 0;
+}
+
 static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma,
                                 struct svc_rdma_op_ctxt *ctxt,
                                 unsigned int sge_no,
@@ -253,222 +433,73 @@ int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma,
        return svc_rdma_dma_map_page(rdma, ctxt, 0, ctxt->pages[0], 0, len);
 }
 
-/* Assumptions:
- * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
+/* Load the xdr_buf into the ctxt's sge array, and DMA map each
+ * element as it is added.
+ *
+ * Returns the number of sge elements loaded on success, or
+ * a negative errno on failure.
  */
-static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
-                     u32 rmr, u64 to,
-                     u32 xdr_off, int write_len,
-                     struct svc_rdma_req_map *vec)
+static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
+                                 struct svc_rdma_op_ctxt *ctxt,
+                                 struct xdr_buf *xdr, __be32 *wr_lst)
 {
-       struct ib_rdma_wr write_wr;
-       struct ib_sge *sge;
-       int xdr_sge_no;
-       int sge_no;
-       int sge_bytes;
-       int sge_off;
-       int bc;
-       struct svc_rdma_op_ctxt *ctxt;
+       unsigned int len, sge_no, remaining, page_off;
+       struct page **ppages;
+       unsigned char *base;
+       u32 xdr_pad;
+       int ret;
 
-       if (vec->count > RPCSVC_MAXPAGES) {
-               pr_err("svcrdma: Too many pages (%lu)\n", vec->count);
-               return -EIO;
-       }
+       sge_no = 1;
 
-       dprintk("svcrdma: RDMA_WRITE rmr=%x, to=%llx, xdr_off=%d, "
-               "write_len=%d, vec->sge=%p, vec->count=%lu\n",
-               rmr, (unsigned long long)to, xdr_off,
-               write_len, vec->sge, vec->count);
+       ret = svc_rdma_dma_map_buf(rdma, ctxt, sge_no++,
+                                  xdr->head[0].iov_base,
+                                  xdr->head[0].iov_len);
+       if (ret < 0)
+               return ret;
 
-       ctxt = svc_rdma_get_context(xprt);
-       ctxt->direction = DMA_TO_DEVICE;
-       sge = ctxt->sge;
-
-       /* Find the SGE associated with xdr_off */
-       for (bc = xdr_off, xdr_sge_no = 1; bc && xdr_sge_no < vec->count;
-            xdr_sge_no++) {
-               if (vec->sge[xdr_sge_no].iov_len > bc)
-                       break;
-               bc -= vec->sge[xdr_sge_no].iov_len;
-       }
+       /* If a Write chunk is present, the xdr_buf's page list
+        * is not included inline. However the Upper Layer may
+        * have added XDR padding in the tail buffer, and that
+        * should not be included inline.
+        */
+       if (wr_lst) {
+               base = xdr->tail[0].iov_base;
+               len = xdr->tail[0].iov_len;
+               xdr_pad = xdr_padsize(xdr->page_len);
 
-       sge_off = bc;
-       bc = write_len;
-       sge_no = 0;
-
-       /* Copy the remaining SGE */
-       while (bc != 0) {
-               sge_bytes = min_t(size_t,
-                         bc, vec->sge[xdr_sge_no].iov_len-sge_off);
-               sge[sge_no].length = sge_bytes;
-               sge[sge_no].addr =
-                       dma_map_xdr(xprt, &rqstp->rq_res, xdr_off,
-                                   sge_bytes, DMA_TO_DEVICE);
-               xdr_off += sge_bytes;
-               if (ib_dma_mapping_error(xprt->sc_cm_id->device,
-                                        sge[sge_no].addr))
-                       goto err;
-               svc_rdma_count_mappings(xprt, ctxt);
-               sge[sge_no].lkey = xprt->sc_pd->local_dma_lkey;
-               ctxt->count++;
-               sge_off = 0;
-               sge_no++;
-               xdr_sge_no++;
-               if (xdr_sge_no > vec->count) {
-                       pr_err("svcrdma: Too many sges (%d)\n", xdr_sge_no);
-                       goto err;
+               if (len && xdr_pad) {
+                       base += xdr_pad;
+                       len -= xdr_pad;
                }
-               bc -= sge_bytes;
-               if (sge_no == xprt->sc_max_sge)
-                       break;
-       }
-
-       /* Prepare WRITE WR */
-       memset(&write_wr, 0, sizeof write_wr);
-       ctxt->cqe.done = svc_rdma_wc_write;
-       write_wr.wr.wr_cqe = &ctxt->cqe;
-       write_wr.wr.sg_list = &sge[0];
-       write_wr.wr.num_sge = sge_no;
-       write_wr.wr.opcode = IB_WR_RDMA_WRITE;
-       write_wr.wr.send_flags = IB_SEND_SIGNALED;
-       write_wr.rkey = rmr;
-       write_wr.remote_addr = to;
-
-       /* Post It */
-       atomic_inc(&rdma_stat_write);
-       if (svc_rdma_send(xprt, &write_wr.wr))
-               goto err;
-       return write_len - bc;
- err:
-       svc_rdma_unmap_dma(ctxt);
-       svc_rdma_put_context(ctxt, 0);
-       return -EIO;
-}
 
-noinline
-static int send_write_chunks(struct svcxprt_rdma *xprt,
-                            struct rpcrdma_write_array *wr_ary,
-                            struct rpcrdma_msg *rdma_resp,
-                            struct svc_rqst *rqstp,
-                            struct svc_rdma_req_map *vec)
-{
-       u32 xfer_len = rqstp->rq_res.page_len;
-       int write_len;
-       u32 xdr_off;
-       int chunk_off;
-       int chunk_no;
-       int nchunks;
-       struct rpcrdma_write_array *res_ary;
-       int ret;
-
-       res_ary = (struct rpcrdma_write_array *)
-               &rdma_resp->rm_body.rm_chunks[1];
-
-       /* Write chunks start at the pagelist */
-       nchunks = be32_to_cpu(wr_ary->wc_nchunks);
-       for (xdr_off = rqstp->rq_res.head[0].iov_len, chunk_no = 0;
-            xfer_len && chunk_no < nchunks;
-            chunk_no++) {
-               struct rpcrdma_segment *arg_ch;
-               u64 rs_offset;
-
-               arg_ch = &wr_ary->wc_array[chunk_no].wc_target;
-               write_len = min(xfer_len, be32_to_cpu(arg_ch->rs_length));
-
-               /* Prepare the response chunk given the length actually
-                * written */
-               xdr_decode_hyper((__be32 *)&arg_ch->rs_offset, &rs_offset);
-               svc_rdma_xdr_encode_array_chunk(res_ary, chunk_no,
-                                               arg_ch->rs_handle,
-                                               arg_ch->rs_offset,
-                                               write_len);
-               chunk_off = 0;
-               while (write_len) {
-                       ret = send_write(xprt, rqstp,
-                                        be32_to_cpu(arg_ch->rs_handle),
-                                        rs_offset + chunk_off,
-                                        xdr_off,
-                                        write_len,
-                                        vec);
-                       if (ret <= 0)
-                               goto out_err;
-                       chunk_off += ret;
-                       xdr_off += ret;
-                       xfer_len -= ret;
-                       write_len -= ret;
-               }
+               goto tail;
        }
-       /* Update the req with the number of chunks actually used */
-       svc_rdma_xdr_encode_write_list(rdma_resp, chunk_no);
 
-       return rqstp->rq_res.page_len;
+       ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
+       page_off = xdr->page_base & ~PAGE_MASK;
+       remaining = xdr->page_len;
+       while (remaining) {
+               len = min_t(u32, PAGE_SIZE - page_off, remaining);
 
-out_err:
-       pr_err("svcrdma: failed to send write chunks, rc=%d\n", ret);
-       return -EIO;
-}
-
-noinline
-static int send_reply_chunks(struct svcxprt_rdma *xprt,
-                            struct rpcrdma_write_array *rp_ary,
-                            struct rpcrdma_msg *rdma_resp,
-                            struct svc_rqst *rqstp,
-                            struct svc_rdma_req_map *vec)
-{
-       u32 xfer_len = rqstp->rq_res.len;
-       int write_len;
-       u32 xdr_off;
-       int chunk_no;
-       int chunk_off;
-       int nchunks;
-       struct rpcrdma_segment *ch;
-       struct rpcrdma_write_array *res_ary;
-       int ret;
+               ret = svc_rdma_dma_map_page(rdma, ctxt, sge_no++,
+                                           *ppages++, page_off, len);
+               if (ret < 0)
+                       return ret;
 
-       /* XXX: need to fix when reply lists occur with read-list and or
-        * write-list */
-       res_ary = (struct rpcrdma_write_array *)
-               &rdma_resp->rm_body.rm_chunks[2];
-
-       /* xdr offset starts at RPC message */
-       nchunks = be32_to_cpu(rp_ary->wc_nchunks);
-       for (xdr_off = 0, chunk_no = 0;
-            xfer_len && chunk_no < nchunks;
-            chunk_no++) {
-               u64 rs_offset;
-               ch = &rp_ary->wc_array[chunk_no].wc_target;
-               write_len = min(xfer_len, be32_to_cpu(ch->rs_length));
-
-               /* Prepare the reply chunk given the length actually
-                * written */
-               xdr_decode_hyper((__be32 *)&ch->rs_offset, &rs_offset);
-               svc_rdma_xdr_encode_array_chunk(res_ary, chunk_no,
-                                               ch->rs_handle, ch->rs_offset,
-                                               write_len);
-               chunk_off = 0;
-               while (write_len) {
-                       ret = send_write(xprt, rqstp,
-                                        be32_to_cpu(ch->rs_handle),
-                                        rs_offset + chunk_off,
-                                        xdr_off,
-                                        write_len,
-                                        vec);
-                       if (ret <= 0)
-                               goto out_err;
-                       chunk_off += ret;
-                       xdr_off += ret;
-                       xfer_len -= ret;
-                       write_len -= ret;
-               }
+               remaining -= len;
+               page_off = 0;
        }
-       /* Update the req with the number of chunks actually used */
-       svc_rdma_xdr_encode_reply_array(res_ary, chunk_no);
 
-       return rqstp->rq_res.len;
+       base = xdr->tail[0].iov_base;
+       len = xdr->tail[0].iov_len;
+tail:
+       if (len) {
+               ret = svc_rdma_dma_map_buf(rdma, ctxt, sge_no++, base, len);
+               if (ret < 0)
+                       return ret;
+       }
 
-out_err:
-       pr_err("svcrdma: failed to send reply chunks, rc=%d\n", ret);
-       return -EIO;
+       return sge_no - 1;
 }
 
 /* The svc_rqst and all resources it owns are released as soon as
@@ -525,90 +556,66 @@ int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma,
        return svc_rdma_send(rdma, send_wr);
 }
 
-/* This function prepares the portion of the RPCRDMA message to be
- * sent in the RDMA_SEND. This function is called after data sent via
- * RDMA has already been transmitted. There are three cases:
- * - The RPCRDMA header, RPC header, and payload are all sent in a
- *   single RDMA_SEND. This is the "inline" case.
- * - The RPCRDMA header and some portion of the RPC header and data
- *   are sent via this RDMA_SEND and another portion of the data is
- *   sent via RDMA.
- * - The RPCRDMA header [NOMSG] is sent in this RDMA_SEND and the RPC
- *   header and data are all transmitted via RDMA.
- * In all three cases, this function prepares the RPCRDMA header in
- * sge[0], the 'type' parameter indicates the type to place in the
- * RPCRDMA header, and the 'byte_count' field indicates how much of
- * the XDR to include in this RDMA_SEND. NB: The offset of the payload
- * to send is zero in the XDR.
+/* Prepare the portion of the RPC Reply that will be transmitted
+ * via RDMA Send. The RPC-over-RDMA transport header is prepared
+ * in sge[0], and the RPC xdr_buf is prepared in following sges.
+ *
+ * Depending on whether a Write list or Reply chunk is present,
+ * the server may send all, a portion of, or none of the xdr_buf.
+ * In the latter case, only the transport header (sge[0]) is
+ * transmitted.
+ *
+ * RDMA Send is the last step of transmitting an RPC reply. Pages
+ * involved in the earlier RDMA Writes are here transferred out
+ * of the rqstp and into the ctxt's page array. These pages are
+ * DMA unmapped by each Write completion, but the subsequent Send
+ * completion finally releases these pages.
+ *
+ * Assumptions:
+ * - The Reply's transport header will never be larger than a page.
  */
-static int send_reply(struct svcxprt_rdma *rdma,
-                     struct svc_rqst *rqstp,
-                     struct page *page,
-                     struct rpcrdma_msg *rdma_resp,
-                     struct svc_rdma_req_map *vec,
-                     int byte_count,
-                     u32 inv_rkey)
+static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma,
+                                  __be32 *rdma_argp, __be32 *rdma_resp,
+                                  struct svc_rqst *rqstp,
+                                  __be32 *wr_lst, __be32 *rp_ch)
 {
        struct svc_rdma_op_ctxt *ctxt;
-       u32 xdr_off;
-       int sge_no;
-       int sge_bytes;
-       int ret = -EIO;
+       u32 inv_rkey;
+       int ret;
+
+       dprintk("svcrdma: sending %s reply: head=%zu, pagelen=%u, tail=%zu\n",
+               (rp_ch ? "RDMA_NOMSG" : "RDMA_MSG"),
+               rqstp->rq_res.head[0].iov_len,
+               rqstp->rq_res.page_len,
+               rqstp->rq_res.tail[0].iov_len);
 
-       /* Prepare the context */
        ctxt = svc_rdma_get_context(rdma);
-       ctxt->direction = DMA_TO_DEVICE;
-       ctxt->pages[0] = page;
-       ctxt->count = 1;
 
-       /* Prepare the SGE for the RPCRDMA Header */
-       ctxt->sge[0].lkey = rdma->sc_pd->local_dma_lkey;
-       ctxt->sge[0].length =
-           svc_rdma_xdr_get_reply_hdr_len((__be32 *)rdma_resp);
-       ctxt->sge[0].addr =
-           ib_dma_map_page(rdma->sc_cm_id->device, page, 0,
-                           ctxt->sge[0].length, DMA_TO_DEVICE);
-       if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
+       ret = svc_rdma_map_reply_hdr(rdma, ctxt, rdma_resp,
+                                    svc_rdma_reply_hdr_len(rdma_resp));
+       if (ret < 0)
                goto err;
-       svc_rdma_count_mappings(rdma, ctxt);
-
-       ctxt->direction = DMA_TO_DEVICE;
 
-       /* Map the payload indicated by 'byte_count' */
-       xdr_off = 0;
-       for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
-               sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
-               byte_count -= sge_bytes;
-               ctxt->sge[sge_no].addr =
-                       dma_map_xdr(rdma, &rqstp->rq_res, xdr_off,
-                                   sge_bytes, DMA_TO_DEVICE);
-               xdr_off += sge_bytes;
-               if (ib_dma_mapping_error(rdma->sc_cm_id->device,
-                                        ctxt->sge[sge_no].addr))
+       if (!rp_ch) {
+               ret = svc_rdma_map_reply_msg(rdma, ctxt,
+                                            &rqstp->rq_res, wr_lst);
+               if (ret < 0)
                        goto err;
-               svc_rdma_count_mappings(rdma, ctxt);
-               ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey;
-               ctxt->sge[sge_no].length = sge_bytes;
-       }
-       if (byte_count != 0) {
-               pr_err("svcrdma: Could not map %d bytes\n", byte_count);
-               goto err;
        }
 
        svc_rdma_save_io_pages(rqstp, ctxt);
 
-       if (sge_no > rdma->sc_max_sge) {
-               pr_err("svcrdma: Too many sges (%d)\n", sge_no);
-               goto err;
-       }
-
-       ret = svc_rdma_post_send_wr(rdma, ctxt, sge_no, inv_rkey);
+       inv_rkey = 0;
+       if (rdma->sc_snd_w_inv)
+               inv_rkey = svc_rdma_get_inv_rkey(rdma_argp, wr_lst, rp_ch);
+       ret = svc_rdma_post_send_wr(rdma, ctxt, 1 + ret, inv_rkey);
        if (ret)
                goto err;
 
        return 0;
 
- err:
+err:
+       pr_err("svcrdma: failed to post Send WR (%d)\n", ret);
        svc_rdma_unmap_dma(ctxt);
        svc_rdma_put_context(ctxt, 1);
        return ret;
@@ -618,41 +625,36 @@ void svc_rdma_prep_reply_hdr(struct svc_rqst *rqstp)
 {
 }
 
+/**
+ * svc_rdma_sendto - Transmit an RPC reply
+ * @rqstp: processed RPC request, reply XDR already in ::rq_res
+ *
+ * Any resources still associated with @rqstp are released upon return.
+ * If no reply message was possible, the connection is closed.
+ *
+ * Returns:
+ *     %0 if an RPC reply has been successfully posted,
+ *     %-ENOMEM if a resource shortage occurred (connection is lost),
+ *     %-ENOTCONN if posting failed (connection is lost).
+ */
 int svc_rdma_sendto(struct svc_rqst *rqstp)
 {
        struct svc_xprt *xprt = rqstp->rq_xprt;
        struct svcxprt_rdma *rdma =
                container_of(xprt, struct svcxprt_rdma, sc_xprt);
-       struct rpcrdma_msg *rdma_argp;
-       struct rpcrdma_msg *rdma_resp;
-       struct rpcrdma_write_array *wr_ary, *rp_ary;
-       int ret;
-       int inline_bytes;
+       __be32 *p, *rdma_argp, *rdma_resp, *wr_lst, *rp_ch;
+       struct xdr_buf *xdr = &rqstp->rq_res;
        struct page *res_page;
-       struct svc_rdma_req_map *vec;
-       u32 inv_rkey;
-       __be32 *p;
-
-       dprintk("svcrdma: sending response for rqstp=%p\n", rqstp);
+       int ret;
 
-       /* Get the RDMA request header. The receive logic always
-        * places this at the start of page 0.
+       /* Find the call's chunk lists to decide how to send the reply.
+        * Receive places the Call's xprt header at the start of page 0.
         */
        rdma_argp = page_address(rqstp->rq_pages[0]);
-       svc_rdma_get_write_arrays(rdma_argp, &wr_ary, &rp_ary);
+       svc_rdma_get_write_arrays(rdma_argp, &wr_lst, &rp_ch);
 
-       inv_rkey = 0;
-       if (rdma->sc_snd_w_inv)
-               inv_rkey = svc_rdma_get_inv_rkey(&rdma_argp->rm_xid,
-                                                (__be32 *)wr_ary,
-                                                (__be32 *)rp_ary);
-
-       /* Build an req vec for the XDR */
-       vec = svc_rdma_get_req_map(rdma);
-       ret = svc_rdma_map_xdr(rdma, &rqstp->rq_res, vec, wr_ary != NULL);
-       if (ret)
-               goto err0;
-       inline_bytes = rqstp->rq_res.len;
+       dprintk("svcrdma: preparing response for XID 0x%08x\n",
+               be32_to_cpup(rdma_argp));
 
        /* Create the RDMA response header. xprt->xpt_mutex,
         * acquired in svc_send(), serializes RPC replies. The
@@ -666,54 +668,46 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
                goto err0;
        rdma_resp = page_address(res_page);
 
-       p = &rdma_resp->rm_xid;
-       *p++ = rdma_argp->rm_xid;
-       *p++ = rdma_argp->rm_vers;
+       p = rdma_resp;
+       *p++ = *rdma_argp;
+       *p++ = *(rdma_argp + 1);
        *p++ = rdma->sc_fc_credits;
-       *p++ = rp_ary ? rdma_nomsg : rdma_msg;
+       *p++ = rp_ch ? rdma_nomsg : rdma_msg;
 
        /* Start with empty chunks */
        *p++ = xdr_zero;
        *p++ = xdr_zero;
        *p   = xdr_zero;
 
-       /* Send any write-chunk data and build resp write-list */
-       if (wr_ary) {
-               ret = send_write_chunks(rdma, wr_ary, rdma_resp, rqstp, vec);
+       if (wr_lst) {
+               /* XXX: Presume the client sent only one Write chunk */
+               ret = svc_rdma_send_write_chunk(rdma, wr_lst, xdr);
                if (ret < 0)
                        goto err1;
-               inline_bytes -= ret + xdr_padsize(ret);
+               svc_rdma_xdr_encode_write_list(rdma_resp, wr_lst, ret);
        }
-
-       /* Send any reply-list data and update resp reply-list */
-       if (rp_ary) {
-               ret = send_reply_chunks(rdma, rp_ary, rdma_resp, rqstp, vec);
+       if (rp_ch) {
+               ret = svc_rdma_send_reply_chunk(rdma, rp_ch, wr_lst, xdr);
                if (ret < 0)
                        goto err1;
-               inline_bytes -= ret;
+               svc_rdma_xdr_encode_reply_chunk(rdma_resp, rp_ch, ret);
        }
 
-       /* Post a fresh Receive buffer _before_ sending the reply */
        ret = svc_rdma_post_recv(rdma, GFP_KERNEL);
        if (ret)
                goto err1;
-
-       ret = send_reply(rdma, rqstp, res_page, rdma_resp, vec,
-                        inline_bytes, inv_rkey);
+       ret = svc_rdma_send_reply_msg(rdma, rdma_argp, rdma_resp, rqstp,
+                                     wr_lst, rp_ch);
        if (ret < 0)
                goto err0;
-
-       svc_rdma_put_req_map(rdma, vec);
-       dprintk("svcrdma: send_reply returns %d\n", ret);
-       return ret;
+       return 0;
 
  err1:
        put_page(res_page);
  err0:
-       svc_rdma_put_req_map(rdma, vec);
        pr_err("svcrdma: Could not send reply, err=%d. Closing transport.\n",
               ret);
-       set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
+       set_bit(XPT_CLOSE, &xprt->xpt_flags);
        return -ENOTCONN;
 }
 
index b25c50992a95c163e97951292a4b34bdf0101feb..237c377c1e06b365d5db960edef1dfa190d844ca 100644 (file)
@@ -1053,6 +1053,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        memset(&qp_attr, 0, sizeof qp_attr);
        qp_attr.event_handler = qp_event_handler;
        qp_attr.qp_context = &newxprt->sc_xprt;
+       qp_attr.port_num = newxprt->sc_cm_id->port_num;
+       qp_attr.cap.max_rdma_ctxs = newxprt->sc_max_requests;
        qp_attr.cap.max_send_wr = newxprt->sc_sq_depth;
        qp_attr.cap.max_recv_wr = newxprt->sc_rq_depth;
        qp_attr.cap.max_send_sge = newxprt->sc_max_sge;