]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/sunrpc/xprtrdma/rpc_rdma.c
net: qrtr: fix len of skb_put_padto in qrtr_node_enqueue
[linux.git] / net / sunrpc / xprtrdma / rpc_rdma.c
index b86b5fd62d9f515d23f47e30cdbc31ad708408eb..aec3beb93b253477d2e0b5098ca46fffcf176f5f 100644 (file)
@@ -78,8 +78,6 @@ static unsigned int rpcrdma_max_call_header_size(unsigned int maxsegs)
        size += rpcrdma_segment_maxsz * sizeof(__be32);
        size += sizeof(__be32); /* list discriminator */
 
-       dprintk("RPC:       %s: max call header size = %u\n",
-               __func__, size);
        return size;
 }
 
@@ -100,8 +98,6 @@ static unsigned int rpcrdma_max_reply_header_size(unsigned int maxsegs)
        size += maxsegs * rpcrdma_segment_maxsz * sizeof(__be32);
        size += sizeof(__be32); /* list discriminator */
 
-       dprintk("RPC:       %s: max reply header size = %u\n",
-               __func__, size);
        return size;
 }
 
@@ -363,8 +359,7 @@ static struct rpcrdma_mr_seg *rpcrdma_mr_prepare(struct rpcrdma_xprt *r_xprt,
 out_getmr_err:
        trace_xprtrdma_nomrs(req);
        xprt_wait_for_buffer_space(&r_xprt->rx_xprt);
-       if (r_xprt->rx_ep.rep_connected != -ENODEV)
-               schedule_work(&r_xprt->rx_buf.rb_refresh_worker);
+       rpcrdma_mrs_refresh(r_xprt);
        return ERR_PTR(-EAGAIN);
 }
 
@@ -393,7 +388,7 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt,
        unsigned int pos;
        int nsegs;
 
-       if (rtype == rpcrdma_noch)
+       if (rtype == rpcrdma_noch_pullup || rtype == rpcrdma_noch_mapped)
                goto done;
 
        pos = rqst->rq_snd_buf.head[0].iov_len;
@@ -565,6 +560,7 @@ static void rpcrdma_sendctx_done(struct kref *kref)
  */
 void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
 {
+       struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf;
        struct ib_sge *sge;
 
        if (!sc->sc_unmap_count)
@@ -576,7 +572,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
         */
        for (sge = &sc->sc_sges[2]; sc->sc_unmap_count;
             ++sge, --sc->sc_unmap_count)
-               ib_dma_unmap_page(sc->sc_device, sge->addr, sge->length,
+               ib_dma_unmap_page(rdmab_device(rb), sge->addr, sge->length,
                                  DMA_TO_DEVICE);
 
        kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done);
@@ -589,149 +585,228 @@ static bool rpcrdma_prepare_hdr_sge(struct rpcrdma_xprt *r_xprt,
 {
        struct rpcrdma_sendctx *sc = req->rl_sendctx;
        struct rpcrdma_regbuf *rb = req->rl_rdmabuf;
-       struct ib_sge *sge = sc->sc_sges;
+       struct ib_sge *sge = &sc->sc_sges[req->rl_wr.num_sge++];
 
        if (!rpcrdma_regbuf_dma_map(r_xprt, rb))
-               goto out_regbuf;
+               return false;
        sge->addr = rdmab_addr(rb);
        sge->length = len;
        sge->lkey = rdmab_lkey(rb);
 
        ib_dma_sync_single_for_device(rdmab_device(rb), sge->addr, sge->length,
                                      DMA_TO_DEVICE);
-       sc->sc_wr.num_sge++;
        return true;
-
-out_regbuf:
-       pr_err("rpcrdma: failed to DMA map a Send buffer\n");
-       return false;
 }
 
-/* Prepare the Send SGEs. The head and tail iovec, and each entry
- * in the page list, gets its own SGE.
+/* The head iovec is straightforward, as it is usually already
+ * DMA-mapped. Sync the content that has changed.
  */
-static bool rpcrdma_prepare_msg_sges(struct rpcrdma_xprt *r_xprt,
-                                    struct rpcrdma_req *req,
-                                    struct xdr_buf *xdr,
-                                    enum rpcrdma_chunktype rtype)
+static bool rpcrdma_prepare_head_iov(struct rpcrdma_xprt *r_xprt,
+                                    struct rpcrdma_req *req, unsigned int len)
 {
        struct rpcrdma_sendctx *sc = req->rl_sendctx;
-       unsigned int sge_no, page_base, len, remaining;
+       struct ib_sge *sge = &sc->sc_sges[req->rl_wr.num_sge++];
        struct rpcrdma_regbuf *rb = req->rl_sendbuf;
-       struct ib_sge *sge = sc->sc_sges;
-       struct page *page, **ppages;
 
-       /* The head iovec is straightforward, as it is already
-        * DMA-mapped. Sync the content that has changed.
-        */
        if (!rpcrdma_regbuf_dma_map(r_xprt, rb))
-               goto out_regbuf;
-       sc->sc_device = rdmab_device(rb);
-       sge_no = 1;
-       sge[sge_no].addr = rdmab_addr(rb);
-       sge[sge_no].length = xdr->head[0].iov_len;
-       sge[sge_no].lkey = rdmab_lkey(rb);
-       ib_dma_sync_single_for_device(rdmab_device(rb), sge[sge_no].addr,
-                                     sge[sge_no].length, DMA_TO_DEVICE);
-
-       /* If there is a Read chunk, the page list is being handled
-        * via explicit RDMA, and thus is skipped here. However, the
-        * tail iovec may include an XDR pad for the page list, as
-        * well as additional content, and may not reside in the
-        * same page as the head iovec.
-        */
-       if (rtype == rpcrdma_readch) {
-               len = xdr->tail[0].iov_len;
+               return false;
 
-               /* Do not include the tail if it is only an XDR pad */
-               if (len < 4)
-                       goto out;
+       sge->addr = rdmab_addr(rb);
+       sge->length = len;
+       sge->lkey = rdmab_lkey(rb);
 
-               page = virt_to_page(xdr->tail[0].iov_base);
-               page_base = offset_in_page(xdr->tail[0].iov_base);
+       ib_dma_sync_single_for_device(rdmab_device(rb), sge->addr, sge->length,
+                                     DMA_TO_DEVICE);
+       return true;
+}
 
-               /* If the content in the page list is an odd length,
-                * xdr_write_pages() has added a pad at the beginning
-                * of the tail iovec. Force the tail's non-pad content
-                * to land at the next XDR position in the Send message.
-                */
-               page_base += len & 3;
-               len -= len & 3;
-               goto map_tail;
-       }
+/* If there is a page list present, DMA map and prepare an
+ * SGE for each page to be sent.
+ */
+static bool rpcrdma_prepare_pagelist(struct rpcrdma_req *req,
+                                    struct xdr_buf *xdr)
+{
+       struct rpcrdma_sendctx *sc = req->rl_sendctx;
+       struct rpcrdma_regbuf *rb = req->rl_sendbuf;
+       unsigned int page_base, len, remaining;
+       struct page **ppages;
+       struct ib_sge *sge;
 
-       /* If there is a page list present, temporarily DMA map
-        * and prepare an SGE for each page to be sent.
-        */
-       if (xdr->page_len) {
-               ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
-               page_base = offset_in_page(xdr->page_base);
-               remaining = xdr->page_len;
-               while (remaining) {
-                       sge_no++;
-                       if (sge_no > RPCRDMA_MAX_SEND_SGES - 2)
-                               goto out_mapping_overflow;
-
-                       len = min_t(u32, PAGE_SIZE - page_base, remaining);
-                       sge[sge_no].addr =
-                               ib_dma_map_page(rdmab_device(rb), *ppages,
-                                               page_base, len, DMA_TO_DEVICE);
-                       if (ib_dma_mapping_error(rdmab_device(rb),
-                                                sge[sge_no].addr))
-                               goto out_mapping_err;
-                       sge[sge_no].length = len;
-                       sge[sge_no].lkey = rdmab_lkey(rb);
-
-                       sc->sc_unmap_count++;
-                       ppages++;
-                       remaining -= len;
-                       page_base = 0;
-               }
-       }
+       ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
+       page_base = offset_in_page(xdr->page_base);
+       remaining = xdr->page_len;
+       while (remaining) {
+               sge = &sc->sc_sges[req->rl_wr.num_sge++];
+               len = min_t(unsigned int, PAGE_SIZE - page_base, remaining);
+               sge->addr = ib_dma_map_page(rdmab_device(rb), *ppages,
+                                           page_base, len, DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(rdmab_device(rb), sge->addr))
+                       goto out_mapping_err;
 
-       /* The tail iovec is not always constructed in the same
-        * page where the head iovec resides (see, for example,
-        * gss_wrap_req_priv). To neatly accommodate that case,
-        * DMA map it separately.
-        */
-       if (xdr->tail[0].iov_len) {
-               page = virt_to_page(xdr->tail[0].iov_base);
-               page_base = offset_in_page(xdr->tail[0].iov_base);
-               len = xdr->tail[0].iov_len;
+               sge->length = len;
+               sge->lkey = rdmab_lkey(rb);
 
-map_tail:
-               sge_no++;
-               sge[sge_no].addr =
-                       ib_dma_map_page(rdmab_device(rb), page, page_base, len,
-                                       DMA_TO_DEVICE);
-               if (ib_dma_mapping_error(rdmab_device(rb), sge[sge_no].addr))
-                       goto out_mapping_err;
-               sge[sge_no].length = len;
-               sge[sge_no].lkey = rdmab_lkey(rb);
                sc->sc_unmap_count++;
+               ppages++;
+               remaining -= len;
+               page_base = 0;
        }
 
-out:
-       sc->sc_wr.num_sge += sge_no;
-       if (sc->sc_unmap_count)
-               kref_get(&req->rl_kref);
        return true;
 
-out_regbuf:
-       pr_err("rpcrdma: failed to DMA map a Send buffer\n");
+out_mapping_err:
+       trace_xprtrdma_dma_maperr(sge->addr);
        return false;
+}
 
-out_mapping_overflow:
-       rpcrdma_sendctx_unmap(sc);
-       pr_err("rpcrdma: too many Send SGEs (%u)\n", sge_no);
-       return false;
+/* The tail iovec may include an XDR pad for the page list,
+ * as well as additional content, and may not reside in the
+ * same page as the head iovec.
+ */
+static bool rpcrdma_prepare_tail_iov(struct rpcrdma_req *req,
+                                    struct xdr_buf *xdr,
+                                    unsigned int page_base, unsigned int len)
+{
+       struct rpcrdma_sendctx *sc = req->rl_sendctx;
+       struct ib_sge *sge = &sc->sc_sges[req->rl_wr.num_sge++];
+       struct rpcrdma_regbuf *rb = req->rl_sendbuf;
+       struct page *page = virt_to_page(xdr->tail[0].iov_base);
+
+       sge->addr = ib_dma_map_page(rdmab_device(rb), page, page_base, len,
+                                   DMA_TO_DEVICE);
+       if (ib_dma_mapping_error(rdmab_device(rb), sge->addr))
+               goto out_mapping_err;
+
+       sge->length = len;
+       sge->lkey = rdmab_lkey(rb);
+       ++sc->sc_unmap_count;
+       return true;
 
 out_mapping_err:
-       rpcrdma_sendctx_unmap(sc);
-       trace_xprtrdma_dma_maperr(sge[sge_no].addr);
+       trace_xprtrdma_dma_maperr(sge->addr);
        return false;
 }
 
+/* Copy the tail to the end of the head buffer.
+ */
+static void rpcrdma_pullup_tail_iov(struct rpcrdma_xprt *r_xprt,
+                                   struct rpcrdma_req *req,
+                                   struct xdr_buf *xdr)
+{
+       unsigned char *dst;
+
+       dst = (unsigned char *)xdr->head[0].iov_base;
+       dst += xdr->head[0].iov_len + xdr->page_len;
+       memmove(dst, xdr->tail[0].iov_base, xdr->tail[0].iov_len);
+       r_xprt->rx_stats.pullup_copy_count += xdr->tail[0].iov_len;
+}
+
+/* Copy pagelist content into the head buffer.
+ */
+static void rpcrdma_pullup_pagelist(struct rpcrdma_xprt *r_xprt,
+                                   struct rpcrdma_req *req,
+                                   struct xdr_buf *xdr)
+{
+       unsigned int len, page_base, remaining;
+       struct page **ppages;
+       unsigned char *src, *dst;
+
+       dst = (unsigned char *)xdr->head[0].iov_base;
+       dst += xdr->head[0].iov_len;
+       ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
+       page_base = offset_in_page(xdr->page_base);
+       remaining = xdr->page_len;
+       while (remaining) {
+               src = page_address(*ppages);
+               src += page_base;
+               len = min_t(unsigned int, PAGE_SIZE - page_base, remaining);
+               memcpy(dst, src, len);
+               r_xprt->rx_stats.pullup_copy_count += len;
+
+               ppages++;
+               dst += len;
+               remaining -= len;
+               page_base = 0;
+       }
+}
+
+/* Copy the contents of @xdr into @rl_sendbuf and DMA sync it.
+ * When the head, pagelist, and tail are small, a pull-up copy
+ * is considerably less costly than DMA mapping the components
+ * of @xdr.
+ *
+ * Assumptions:
+ *  - the caller has already verified that the total length
+ *    of the RPC Call body will fit into @rl_sendbuf.
+ */
+static bool rpcrdma_prepare_noch_pullup(struct rpcrdma_xprt *r_xprt,
+                                       struct rpcrdma_req *req,
+                                       struct xdr_buf *xdr)
+{
+       if (unlikely(xdr->tail[0].iov_len))
+               rpcrdma_pullup_tail_iov(r_xprt, req, xdr);
+
+       if (unlikely(xdr->page_len))
+               rpcrdma_pullup_pagelist(r_xprt, req, xdr);
+
+       /* The whole RPC message resides in the head iovec now */
+       return rpcrdma_prepare_head_iov(r_xprt, req, xdr->len);
+}
+
+static bool rpcrdma_prepare_noch_mapped(struct rpcrdma_xprt *r_xprt,
+                                       struct rpcrdma_req *req,
+                                       struct xdr_buf *xdr)
+{
+       struct kvec *tail = &xdr->tail[0];
+
+       if (!rpcrdma_prepare_head_iov(r_xprt, req, xdr->head[0].iov_len))
+               return false;
+       if (xdr->page_len)
+               if (!rpcrdma_prepare_pagelist(req, xdr))
+                       return false;
+       if (tail->iov_len)
+               if (!rpcrdma_prepare_tail_iov(req, xdr,
+                                             offset_in_page(tail->iov_base),
+                                             tail->iov_len))
+                       return false;
+
+       if (req->rl_sendctx->sc_unmap_count)
+               kref_get(&req->rl_kref);
+       return true;
+}
+
+static bool rpcrdma_prepare_readch(struct rpcrdma_xprt *r_xprt,
+                                  struct rpcrdma_req *req,
+                                  struct xdr_buf *xdr)
+{
+       if (!rpcrdma_prepare_head_iov(r_xprt, req, xdr->head[0].iov_len))
+               return false;
+
+       /* If there is a Read chunk, the page list is being handled
+        * via explicit RDMA, and thus is skipped here.
+        */
+
+       /* Do not include the tail if it is only an XDR pad */
+       if (xdr->tail[0].iov_len > 3) {
+               unsigned int page_base, len;
+
+               /* If the content in the page list is an odd length,
+                * xdr_write_pages() adds a pad at the beginning of
+                * the tail iovec. Force the tail's non-pad content to
+                * land at the next XDR position in the Send message.
+                */
+               page_base = offset_in_page(xdr->tail[0].iov_base);
+               len = xdr->tail[0].iov_len;
+               page_base += len & 3;
+               len -= len & 3;
+               if (!rpcrdma_prepare_tail_iov(req, xdr, page_base, len))
+                       return false;
+               kref_get(&req->rl_kref);
+       }
+
+       return true;
+}
+
 /**
  * rpcrdma_prepare_send_sges - Construct SGEs for a Send WR
  * @r_xprt: controlling transport
@@ -742,31 +817,53 @@ static bool rpcrdma_prepare_msg_sges(struct rpcrdma_xprt *r_xprt,
  *
  * Returns 0 on success; otherwise a negative errno is returned.
  */
-int
-rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
-                         struct rpcrdma_req *req, u32 hdrlen,
-                         struct xdr_buf *xdr, enum rpcrdma_chunktype rtype)
+inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
+                                    struct rpcrdma_req *req, u32 hdrlen,
+                                    struct xdr_buf *xdr,
+                                    enum rpcrdma_chunktype rtype)
 {
        int ret;
 
        ret = -EAGAIN;
        req->rl_sendctx = rpcrdma_sendctx_get_locked(r_xprt);
        if (!req->rl_sendctx)
-               goto err;
-       req->rl_sendctx->sc_wr.num_sge = 0;
+               goto out_nosc;
        req->rl_sendctx->sc_unmap_count = 0;
        req->rl_sendctx->sc_req = req;
        kref_init(&req->rl_kref);
+       req->rl_wr.wr_cqe = &req->rl_sendctx->sc_cqe;
+       req->rl_wr.sg_list = req->rl_sendctx->sc_sges;
+       req->rl_wr.num_sge = 0;
+       req->rl_wr.opcode = IB_WR_SEND;
 
        ret = -EIO;
        if (!rpcrdma_prepare_hdr_sge(r_xprt, req, hdrlen))
-               goto err;
-       if (rtype != rpcrdma_areadch)
-               if (!rpcrdma_prepare_msg_sges(r_xprt, req, xdr, rtype))
-                       goto err;
+               goto out_unmap;
+
+       switch (rtype) {
+       case rpcrdma_noch_pullup:
+               if (!rpcrdma_prepare_noch_pullup(r_xprt, req, xdr))
+                       goto out_unmap;
+               break;
+       case rpcrdma_noch_mapped:
+               if (!rpcrdma_prepare_noch_mapped(r_xprt, req, xdr))
+                       goto out_unmap;
+               break;
+       case rpcrdma_readch:
+               if (!rpcrdma_prepare_readch(r_xprt, req, xdr))
+                       goto out_unmap;
+               break;
+       case rpcrdma_areadch:
+               break;
+       default:
+               goto out_unmap;
+       }
+
        return 0;
 
-err:
+out_unmap:
+       rpcrdma_sendctx_unmap(req->rl_sendctx);
+out_nosc:
        trace_xprtrdma_prepsend_failed(&req->rl_slot, ret);
        return ret;
 }
@@ -796,6 +893,7 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
        struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
        struct xdr_stream *xdr = &req->rl_stream;
        enum rpcrdma_chunktype rtype, wtype;
+       struct xdr_buf *buf = &rqst->rq_snd_buf;
        bool ddp_allowed;
        __be32 *p;
        int ret;
@@ -853,8 +951,9 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
         */
        if (rpcrdma_args_inline(r_xprt, rqst)) {
                *p++ = rdma_msg;
-               rtype = rpcrdma_noch;
-       } else if (ddp_allowed && rqst->rq_snd_buf.flags & XDRBUF_WRITE) {
+               rtype = buf->len < rdmab_length(req->rl_sendbuf) ?
+                       rpcrdma_noch_pullup : rpcrdma_noch_mapped;
+       } else if (ddp_allowed && buf->flags & XDRBUF_WRITE) {
                *p++ = rdma_msg;
                rtype = rpcrdma_readch;
        } else {
@@ -863,12 +962,6 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
                rtype = rpcrdma_areadch;
        }
 
-       /* If this is a retransmit, discard previously registered
-        * chunks. Very likely the connection has been replaced,
-        * so these registrations are invalid and unusable.
-        */
-       frwr_recycle(req);
-
        /* This implementation supports the following combinations
         * of chunk lists in one RPC-over-RDMA Call message:
         *
@@ -902,7 +995,7 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
                goto out_err;
 
        ret = rpcrdma_prepare_send_sges(r_xprt, req, req->rl_hdrbuf.len,
-                                       &rqst->rq_snd_buf, rtype);
+                                       buf, rtype);
        if (ret)
                goto out_err;
 
@@ -916,6 +1009,40 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
        return ret;
 }
 
+static void __rpcrdma_update_cwnd_locked(struct rpc_xprt *xprt,
+                                        struct rpcrdma_buffer *buf,
+                                        u32 grant)
+{
+       buf->rb_credits = grant;
+       xprt->cwnd = grant << RPC_CWNDSHIFT;
+}
+
+static void rpcrdma_update_cwnd(struct rpcrdma_xprt *r_xprt, u32 grant)
+{
+       struct rpc_xprt *xprt = &r_xprt->rx_xprt;
+
+       spin_lock(&xprt->transport_lock);
+       __rpcrdma_update_cwnd_locked(xprt, &r_xprt->rx_buf, grant);
+       spin_unlock(&xprt->transport_lock);
+}
+
+/**
+ * rpcrdma_reset_cwnd - Reset the xprt's congestion window
+ * @r_xprt: controlling transport instance
+ *
+ * Prepare @r_xprt for the next connection by reinitializing
+ * its credit grant to one (see RFC 8166, Section 3.3.3).
+ */
+void rpcrdma_reset_cwnd(struct rpcrdma_xprt *r_xprt)
+{
+       struct rpc_xprt *xprt = &r_xprt->rx_xprt;
+
+       spin_lock(&xprt->transport_lock);
+       xprt->cong = 0;
+       __rpcrdma_update_cwnd_locked(xprt, &r_xprt->rx_buf, 1);
+       spin_unlock(&xprt->transport_lock);
+}
+
 /**
  * rpcrdma_inline_fixup - Scatter inline received data into rqst's iovecs
  * @rqst: controlling RPC request
@@ -955,7 +1082,6 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
        curlen = rqst->rq_rcv_buf.head[0].iov_len;
        if (curlen > copy_len)
                curlen = copy_len;
-       trace_xprtrdma_fixup(rqst, copy_len, curlen);
        srcp += curlen;
        copy_len -= curlen;
 
@@ -975,8 +1101,6 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
                        if (curlen > pagelist_len)
                                curlen = pagelist_len;
 
-                       trace_xprtrdma_fixup_pg(rqst, i, srcp,
-                                               copy_len, curlen);
                        destp = kmap_atomic(ppages[i]);
                        memcpy(destp + page_base, srcp, curlen);
                        flush_dcache_page(ppages[i]);
@@ -1008,6 +1132,8 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
                rqst->rq_private_buf.tail[0].iov_base = srcp;
        }
 
+       if (fixup_copy_count)
+               trace_xprtrdma_fixup(rqst, fixup_copy_count);
        return fixup_copy_count;
 }
 
@@ -1356,12 +1482,9 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
                credits = 1;    /* don't deadlock */
        else if (credits > buf->rb_max_requests)
                credits = buf->rb_max_requests;
-       if (buf->rb_credits != credits) {
-               spin_lock(&xprt->transport_lock);
-               buf->rb_credits = credits;
-               xprt->cwnd = credits << RPC_CWNDSHIFT;
-               spin_unlock(&xprt->transport_lock);
-       }
+       if (buf->rb_credits != credits)
+               rpcrdma_update_cwnd(r_xprt, credits);
+       rpcrdma_post_recvs(r_xprt, false);
 
        req = rpcr_to_rdmar(rqst);
        if (req->rl_reply) {