Skip to content

Commit 302d3de

Browse files
chuckleveramschuma-ntap
authored andcommitted
xprtrdma: Prevent inline overflow
When deciding whether to send a Call inline, rpcrdma_marshal_req doesn't take into account header bytes consumed by chunk lists. This results in Call messages on the wire that are sometimes larger than the inline threshold. Likewise, when a Write list or Reply chunk is in play, the server's reply has to emit an RDMA Send that includes a larger-than-minimal RPC-over-RDMA header. The actual size of a Call message cannot be estimated until after the chunk lists have been registered. Thus the size of each RPC-over-RDMA header can be estimated only after chunks are registered; but the decision to register chunks is based on the size of that header. Chicken, meet egg. The best a client can do is estimate header size based on the largest header that might occur, and then ensure that inline content is always smaller than that. Signed-off-by: Chuck Lever <[email protected]> Tested-by: Steve Wise <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent 9493174 commit 302d3de

File tree

5 files changed

+90
-11
lines changed

5 files changed

+90
-11
lines changed

net/sunrpc/xprtrdma/fmr_ops.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ static int
3939
fmr_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
4040
struct rpcrdma_create_data_internal *cdata)
4141
{
42+
rpcrdma_set_max_header_sizes(ia, cdata, max_t(unsigned int, 1,
43+
RPCRDMA_MAX_DATA_SEGS /
44+
RPCRDMA_MAX_FMR_SGES));
4245
return 0;
4346
}
4447

net/sunrpc/xprtrdma/frwr_ops.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ frwr_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
231231
depth;
232232
}
233233

234+
rpcrdma_set_max_header_sizes(ia, cdata, max_t(unsigned int, 1,
235+
RPCRDMA_MAX_DATA_SEGS /
236+
ia->ri_max_frmr_depth));
234237
return 0;
235238
}
236239

net/sunrpc/xprtrdma/physical_ops.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ physical_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
3636
__func__, PTR_ERR(mr));
3737
return -ENOMEM;
3838
}
39-
4039
ia->ri_dma_mr = mr;
40+
41+
rpcrdma_set_max_header_sizes(ia, cdata, min_t(unsigned int,
42+
RPCRDMA_MAX_DATA_SEGS,
43+
RPCRDMA_MAX_HDR_SEGS));
4144
return 0;
4245
}
4346

net/sunrpc/xprtrdma/rpc_rdma.c

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,79 @@ enum rpcrdma_chunktype {
6161
rpcrdma_replych
6262
};
6363

64-
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
6564
static const char transfertypes[][12] = {
6665
"pure inline", /* no chunks */
6766
" read chunk", /* some argument via rdma read */
6867
"*read chunk", /* entire request via rdma read */
6968
"write chunk", /* some result via rdma write */
7069
"reply chunk" /* entire reply via rdma write */
7170
};
72-
#endif
71+
72+
/* Returns size of largest RPC-over-RDMA header in a Call message
73+
*
74+
* The client marshals only one chunk list per Call message.
75+
* The largest list is the Read list.
76+
*/
77+
static unsigned int rpcrdma_max_call_header_size(unsigned int maxsegs)
78+
{
79+
unsigned int size;
80+
81+
/* Fixed header fields and list discriminators */
82+
size = RPCRDMA_HDRLEN_MIN;
83+
84+
/* Maximum Read list size */
85+
maxsegs += 2; /* segment for head and tail buffers */
86+
size = maxsegs * sizeof(struct rpcrdma_read_chunk);
87+
88+
dprintk("RPC: %s: max call header size = %u\n",
89+
__func__, size);
90+
return size;
91+
}
92+
93+
/* Returns size of largest RPC-over-RDMA header in a Reply message
94+
*
95+
* There is only one Write list or one Reply chunk per Reply
96+
* message. The larger list is the Write list.
97+
*/
98+
static unsigned int rpcrdma_max_reply_header_size(unsigned int maxsegs)
99+
{
100+
unsigned int size;
101+
102+
/* Fixed header fields and list discriminators */
103+
size = RPCRDMA_HDRLEN_MIN;
104+
105+
/* Maximum Write list size */
106+
maxsegs += 2; /* segment for head and tail buffers */
107+
size = sizeof(__be32); /* segment count */
108+
size += maxsegs * sizeof(struct rpcrdma_segment);
109+
size += sizeof(__be32); /* list discriminator */
110+
111+
dprintk("RPC: %s: max reply header size = %u\n",
112+
__func__, size);
113+
return size;
114+
}
115+
116+
void rpcrdma_set_max_header_sizes(struct rpcrdma_ia *ia,
117+
struct rpcrdma_create_data_internal *cdata,
118+
unsigned int maxsegs)
119+
{
120+
ia->ri_max_inline_write = cdata->inline_wsize -
121+
rpcrdma_max_call_header_size(maxsegs);
122+
ia->ri_max_inline_read = cdata->inline_rsize -
123+
rpcrdma_max_reply_header_size(maxsegs);
124+
}
73125

74126
/* The client can send a request inline as long as the RPCRDMA header
75127
* plus the RPC call fit under the transport's inline limit. If the
76128
* combined call message size exceeds that limit, the client must use
77129
* the read chunk list for this operation.
78130
*/
79-
static bool rpcrdma_args_inline(struct rpc_rqst *rqst)
131+
static bool rpcrdma_args_inline(struct rpcrdma_xprt *r_xprt,
132+
struct rpc_rqst *rqst)
80133
{
81-
unsigned int callsize = RPCRDMA_HDRLEN_MIN + rqst->rq_snd_buf.len;
134+
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
82135

83-
return callsize <= RPCRDMA_INLINE_WRITE_THRESHOLD(rqst);
136+
return rqst->rq_snd_buf.len <= ia->ri_max_inline_write;
84137
}
85138

86139
/* The client can't know how large the actual reply will be. Thus it
@@ -89,11 +142,12 @@ static bool rpcrdma_args_inline(struct rpc_rqst *rqst)
89142
* limit, the client must provide a write list or a reply chunk for
90143
* this request.
91144
*/
92-
static bool rpcrdma_results_inline(struct rpc_rqst *rqst)
145+
static bool rpcrdma_results_inline(struct rpcrdma_xprt *r_xprt,
146+
struct rpc_rqst *rqst)
93147
{
94-
unsigned int repsize = RPCRDMA_HDRLEN_MIN + rqst->rq_rcv_buf.buflen;
148+
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
95149

96-
return repsize <= RPCRDMA_INLINE_READ_THRESHOLD(rqst);
150+
return rqst->rq_rcv_buf.buflen <= ia->ri_max_inline_read;
97151
}
98152

99153
static int
@@ -492,7 +546,7 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
492546
*/
493547
if (rqst->rq_rcv_buf.flags & XDRBUF_READ)
494548
wtype = rpcrdma_writech;
495-
else if (rpcrdma_results_inline(rqst))
549+
else if (rpcrdma_results_inline(r_xprt, rqst))
496550
wtype = rpcrdma_noch;
497551
else
498552
wtype = rpcrdma_replych;
@@ -511,7 +565,7 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
511565
* that both has a data payload, and whose non-data arguments
512566
* by themselves are larger than the inline threshold.
513567
*/
514-
if (rpcrdma_args_inline(rqst)) {
568+
if (rpcrdma_args_inline(r_xprt, rqst)) {
515569
rtype = rpcrdma_noch;
516570
} else if (rqst->rq_snd_buf.flags & XDRBUF_WRITE) {
517571
rtype = rpcrdma_readch;
@@ -561,6 +615,9 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
561615
if (hdrlen < 0)
562616
return hdrlen;
563617

618+
if (hdrlen + rpclen > RPCRDMA_INLINE_WRITE_THRESHOLD(rqst))
619+
goto out_overflow;
620+
564621
dprintk("RPC: %s: %s: hdrlen %zd rpclen %zd"
565622
" headerp 0x%p base 0x%p lkey 0x%x\n",
566623
__func__, transfertypes[wtype], hdrlen, rpclen,
@@ -587,6 +644,14 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
587644

588645
req->rl_niovs = 2;
589646
return 0;
647+
648+
out_overflow:
649+
pr_err("rpcrdma: send overflow: hdrlen %zd rpclen %zu %s\n",
650+
hdrlen, rpclen, transfertypes[wtype]);
651+
/* Terminate this RPC. Chunks registered above will be
652+
* released by xprt_release -> xprt_rmda_free .
653+
*/
654+
return -EIO;
590655
}
591656

592657
/*

net/sunrpc/xprtrdma/xprt_rdma.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ struct rpcrdma_ia {
7373
struct completion ri_done;
7474
int ri_async_rc;
7575
unsigned int ri_max_frmr_depth;
76+
unsigned int ri_max_inline_write;
77+
unsigned int ri_max_inline_read;
7678
struct ib_qp_attr ri_qp_attr;
7779
struct ib_qp_init_attr ri_qp_init_attr;
7880
};
@@ -538,6 +540,9 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *);
538540
* RPC/RDMA protocol calls - xprtrdma/rpc_rdma.c
539541
*/
540542
int rpcrdma_marshal_req(struct rpc_rqst *);
543+
void rpcrdma_set_max_header_sizes(struct rpcrdma_ia *,
544+
struct rpcrdma_create_data_internal *,
545+
unsigned int);
541546

542547
/* RPC/RDMA module init - xprtrdma/transport.c
543548
*/

0 commit comments

Comments
 (0)