Skip to content

Commit df881eb

Browse files
chucklevergregkh
authored andcommitted
xprtrdma: Resize reply buffers before reposting receives
[ Upstream commit 234c0ff ] Commit 0e13dd9 ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") made rpcrdma_rep objects survive disconnects. That is normally fine, but it also means their receive regbufs keep the size they had when they were first allocated. Each rep's receive buffer is sized to ep->re_inline_recv when the rep is created. rpcrdma_ep_create() resets that threshold to the rdma_max_inline_read ceiling for every new endpoint, and the connect handshake then shrinks it to the peer's advertised inline send size. A rep allocated under a smaller negotiated threshold keeps that size: on disconnect, rpcrdma_xprt_disconnect() drains and DMA-unmaps the surviving reps but does not free or resize them. The threshold can come back larger on the next connection. The first peer may supply no RPC-over-RDMA CM private data, defaulting its send size to 1024, while the reconnect target is an ordinary server offering 4096; or, with rdma_max_inline_read raised above its default, the reconnect target may advertise a larger svcrdma_max_req_size than the first. rpcrdma_post_recvs() then reposts a surviving rep whose SGE length is still the old, smaller value, and a larger inline Reply hits a receive length error and forces another disconnect. The undersized rep returns to the free list when its failed Receive flushes, so the following reconnect reposts the same rep and fails the same way. The transport flaps without making forward progress for as long as the peer keeps advertising the larger inline size. This is local/admin-triggerable rather than remote-triggerable: a local administrator must create and maintain the NFS/RDMA mount, while the server or reconnect target has to advertise a larger inline send size and return a reply that uses it. Fix this by checking each rep before it is reposted. If the receive regbuf is smaller than the current endpoint's inline receive size, reallocate it on the current RDMA device's NUMA node and reinitialize the rep's xdr_buf before DMA-mapping and posting the Receive WR. Fixes: 0e13dd9 ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 3b04e15 commit df881eb

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

net/sunrpc/xprtrdma/verbs.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ rpcrdma_regbuf_alloc_node(size_t size, enum dma_data_direction direction,
8181
int node);
8282
static struct rpcrdma_regbuf *
8383
rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction);
84+
static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb,
85+
size_t size, gfp_t flags, int node);
8486
static void rpcrdma_regbuf_dma_unmap(struct rpcrdma_regbuf *rb);
8587
static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb);
8688

@@ -1306,10 +1308,16 @@ rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction)
13061308
* returned, @rb is left untouched.
13071309
*/
13081310
bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags)
1311+
{
1312+
return rpcrdma_regbuf_realloc_node(rb, size, flags, NUMA_NO_NODE);
1313+
}
1314+
1315+
static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb,
1316+
size_t size, gfp_t flags, int node)
13091317
{
13101318
void *buf;
13111319

1312-
buf = kmalloc(size, flags);
1320+
buf = kmalloc_node(size, flags, node);
13131321
if (!buf)
13141322
return false;
13151323

@@ -1321,6 +1329,23 @@ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags)
13211329
return true;
13221330
}
13231331

1332+
static bool rpcrdma_rep_resize(struct rpcrdma_xprt *r_xprt,
1333+
struct rpcrdma_rep *rep)
1334+
{
1335+
struct rpcrdma_regbuf *rb = rep->rr_rdmabuf;
1336+
struct rpcrdma_ep *ep = r_xprt->rx_ep;
1337+
size_t size = ep->re_inline_recv;
1338+
1339+
if (likely(rdmab_length(rb) >= size))
1340+
return true;
1341+
if (!rpcrdma_regbuf_realloc_node(rb, size, XPRTRDMA_GFP_FLAGS,
1342+
ibdev_to_node(ep->re_id->device)))
1343+
return false;
1344+
1345+
xdr_buf_init(&rep->rr_hdrbuf, rdmab_data(rb), rdmab_length(rb));
1346+
return true;
1347+
}
1348+
13241349
/**
13251350
* __rpcrdma_regbuf_dma_map - DMA-map a regbuf
13261351
* @r_xprt: controlling transport instance
@@ -1404,6 +1429,10 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed)
14041429
break;
14051430
/* I1: a rep on rb_free_reps must carry no rqst pointer. */
14061431
WARN_ON_ONCE(rep->rr_rqst);
1432+
if (!rpcrdma_rep_resize(r_xprt, rep)) {
1433+
rpcrdma_rep_put(buf, rep);
1434+
break;
1435+
}
14071436
if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) {
14081437
rpcrdma_rep_put(buf, rep);
14091438
break;

0 commit comments

Comments
 (0)