| From ba3b7bb441f1c3b1e1263b799db5a72db490cf52 Mon Sep 17 00:00:00 2001 |
| From: Chuck Lever <chuck.lever@oracle.com> |
| Date: Tue, 31 Mar 2020 17:02:33 -0400 |
| Subject: [PATCH] svcrdma: Fix leak of svc_rdma_recv_ctxt objects |
| |
| commit 23cf1ee1f1869966b75518c59b5cbda4c6c92450 upstream. |
| |
| Utilize the xpo_release_rqst transport method to ensure that each |
| rqstp's svc_rdma_recv_ctxt object is released even when the server |
| cannot return a Reply for that rqstp. |
| |
| Without this fix, each RPC whose Reply cannot be sent leaks one |
| svc_rdma_recv_ctxt. This is a 2.5KB structure, a 4KB DMA-mapped |
| Receive buffer, and any pages that might be part of the Reply |
| message. |
| |
| The leak is infrequent unless the network fabric is unreliable or |
| Kerberos is in use, as GSS sequence window overruns, which result |
| in connection loss, are more common on fast transports. |
| |
| Fixes: 3a88092ee319 ("svcrdma: Preserve Receive buffer until svc_rdma_sendto") |
| Signed-off-by: Chuck Lever <chuck.lever@oracle.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h |
| index 981f0d726ad4..7b0ab2c86148 100644 |
| --- a/include/linux/sunrpc/svc_rdma.h |
| +++ b/include/linux/sunrpc/svc_rdma.h |
| @@ -161,6 +161,7 @@ extern bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma); |
| extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, |
| struct svc_rdma_recv_ctxt *ctxt); |
| extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma); |
| +extern void svc_rdma_release_rqst(struct svc_rqst *rqstp); |
| extern int svc_rdma_recvfrom(struct svc_rqst *); |
| |
| /* svc_rdma_rw.c */ |
| diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c |
| index a58c5030d03a..26384a36237e 100644 |
| --- a/net/sunrpc/svc_xprt.c |
| +++ b/net/sunrpc/svc_xprt.c |
| @@ -897,9 +897,6 @@ int svc_send(struct svc_rqst *rqstp) |
| if (!xprt) |
| goto out; |
| |
| - /* release the receive skb before sending the reply */ |
| - xprt->xpt_ops->xpo_release_rqst(rqstp); |
| - |
| /* calculate over-all length */ |
| xb = &rqstp->rq_res; |
| xb->len = xb->head[0].iov_len + |
| diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c |
| index 2934dd711715..4260924ad9db 100644 |
| --- a/net/sunrpc/svcsock.c |
| +++ b/net/sunrpc/svcsock.c |
| @@ -605,6 +605,8 @@ svc_udp_sendto(struct svc_rqst *rqstp) |
| { |
| int error; |
| |
| + svc_release_udp_skb(rqstp); |
| + |
| error = svc_sendto(rqstp, &rqstp->rq_res); |
| if (error == -ECONNREFUSED) |
| /* ICMP error on earlier request. */ |
| @@ -1137,6 +1139,8 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp) |
| int sent; |
| __be32 reclen; |
| |
| + svc_release_skb(rqstp); |
| + |
| /* Set up the first element of the reply kvec. |
| * Any other kvecs that may be in use have been taken |
| * care of by the server implementation itself. |
| diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c |
| index 65e2fb9aac65..b5b77c7ecee7 100644 |
| --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c |
| +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c |
| @@ -226,6 +226,26 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, |
| svc_rdma_recv_ctxt_destroy(rdma, ctxt); |
| } |
| |
| +/** |
| + * svc_rdma_release_rqst - Release transport-specific per-rqst resources |
| + * @rqstp: svc_rqst being released |
| + * |
| + * Ensure that the recv_ctxt is released whether or not a Reply |
| + * was sent. For example, the client could close the connection, |
| + * or svc_process could drop an RPC, before the Reply is sent. |
| + */ |
| +void svc_rdma_release_rqst(struct svc_rqst *rqstp) |
| +{ |
| + struct svc_rdma_recv_ctxt *ctxt = rqstp->rq_xprt_ctxt; |
| + struct svc_xprt *xprt = rqstp->rq_xprt; |
| + struct svcxprt_rdma *rdma = |
| + container_of(xprt, struct svcxprt_rdma, sc_xprt); |
| + |
| + rqstp->rq_xprt_ctxt = NULL; |
| + if (ctxt) |
| + svc_rdma_recv_ctxt_put(rdma, ctxt); |
| +} |
| + |
| static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma, |
| struct svc_rdma_recv_ctxt *ctxt) |
| { |
| @@ -760,6 +780,8 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) |
| __be32 *p; |
| int ret; |
| |
| + rqstp->rq_xprt_ctxt = NULL; |
| + |
| spin_lock(&rdma_xprt->sc_rq_dto_lock); |
| ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_read_complete_q); |
| if (ctxt) { |
| diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c |
| index d4d9844ae85b..93ff7967389a 100644 |
| --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c |
| +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c |
| @@ -873,12 +873,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) |
| wr_lst, rp_ch); |
| if (ret < 0) |
| goto err1; |
| - ret = 0; |
| - |
| -out: |
| - rqstp->rq_xprt_ctxt = NULL; |
| - svc_rdma_recv_ctxt_put(rdma, rctxt); |
| - return ret; |
| + return 0; |
| |
| err2: |
| if (ret != -E2BIG && ret != -EINVAL) |
| @@ -887,14 +882,12 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) |
| ret = svc_rdma_send_error_msg(rdma, sctxt, rqstp); |
| if (ret < 0) |
| goto err1; |
| - ret = 0; |
| - goto out; |
| + return 0; |
| |
| err1: |
| svc_rdma_send_ctxt_put(rdma, sctxt); |
| err0: |
| trace_svcrdma_send_failed(rqstp, ret); |
| set_bit(XPT_CLOSE, &xprt->xpt_flags); |
| - ret = -ENOTCONN; |
| - goto out; |
| + return -ENOTCONN; |
| } |
| diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c |
| index 0004535c0188..fb96283745d1 100644 |
| --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c |
| +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c |
| @@ -71,7 +71,6 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, |
| struct sockaddr *sa, int salen, |
| int flags); |
| static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt); |
| -static void svc_rdma_release_rqst(struct svc_rqst *); |
| static void svc_rdma_detach(struct svc_xprt *xprt); |
| static void svc_rdma_free(struct svc_xprt *xprt); |
| static int svc_rdma_has_wspace(struct svc_xprt *xprt); |
| @@ -559,10 +558,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) |
| return NULL; |
| } |
| |
| -static void svc_rdma_release_rqst(struct svc_rqst *rqstp) |
| -{ |
| -} |
| - |
| /* |
| * When connected, an svc_xprt has at least two references: |
| * |
| -- |
| 2.7.4 |
| |