FAILED: patch "[PATCH] sunrpc: defer rq_argp and rq_resp free until after RCU grace" failed to apply to 6.1-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x c479bde671cbe2f9e152834a8b0eb7c3c295bbaf
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090350-comment-projector-6988@gregkh' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c479bde671cbe2f9e152834a8b0eb7c3c295bbaf Mon Sep 17 00:00:00 2001
From: Jeff Layton <[email protected]>
Date: Thu, 11 Jun 2026 16:00:47 -0400
Subject: [PATCH] sunrpc: defer rq_argp and rq_resp free until after RCU grace
period
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
net/sunrpc/svc.c +10 -3
@@ -716,6 +716,15 @@ svc_release_buffer(struct svc_rqst *rqstp) } } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +static void svc_rqst_free (struct svc_rqst *rqstp) {
@@ -724,10 +733,8 @@ svc_rqst_free(struct svc_rqst *rqstp)svc_release_buffer (rqstp);if (rqstp->rq_scratch_folio)folio_put (rqstp->rq_scratch_folio); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst *
Sasha Levin
maintainerPATCH 1/5 SUNRPC: remove timeout arg from svc_recv()
From: NeilBrown <[email protected]>
[ Upstream commit c743b4259c3af2c0637c307f08a062d25fa3c99f ]
Most svc threads have no interest in a timeout.
nfsd sets it to 1 hour, but this is a wart of no significance.
lockd uses the timeout so that it can call nlmsvc_retry_blocked(). It also sometimes calls svc_wake_up() to ensure this is called.
So change lockd to be consistent and always use svc_wake_up() to trigger nlmsvc_retry_blocked() - using a timer instead of a timeout to svc_recv().
And change svc_recv() to not take a timeout arg.
This makes the sp_threads_timedout counter always zero.
[ Stable backport note: The svc_recv timeout cleanup is unrelated to the follow-up fix and does not fit the older lockd service loop. Drop those hunks. Instead, make the existing svc request allocation/free helpers private, move svc_rqst_free() before its first use, and convert the scratch page to a folio. This aligns the lifetime-management context so the follow-up fix applies cleanly. ]
[ sashal: Reduced backport -- upstream c743b4259c3af touches 9 file(s), this
backport carries 2. Not backported here:
fs/lockd/svc.c
fs/lockd/svclock.c
fs/nfs/callback.c
fs/nfsd/nfssvc.c
include/linux/lockd/lockd.h
include/linux/sunrpc/svcsock.h
net/sunrpc/svc_xprt.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 7 ++----- net/sunrpc/svc.c | 38 +++++++++++++++++--------------------- 2 files changed, 19 insertions(+), 26 deletions(-)
include/linux/sunrpc/svc.h +2 -5
@@ -247,7 +247,7 @@ struct svc_rqst {struct xdr_buf rq_arg;struct xdr_stream rq_arg_stream;struct xdr_stream rq_res_stream; -struct page *rq_scratch_page; +struct folio *rq_scratch_folio;struct xdr_buf rq_res;struct page *rq_pages[RPCSVC_MAXPAGES +1 ];struct page * *rq_respages;/* points into rq_pages */
@@ -473,11 +473,8 @@ void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);int svc_bind (struct svc_serv *serv,struct net *net);struct svc_serv *svc_create (struct svc_program *,unsigned int ,int (*threadfn)(void *data)); -struct svc_rqst *svc_rqst_alloc (struct svc_serv *serv, -struct svc_pool *pool,int node);void svc_rqst_replace_page (struct svc_rqst *rqstp,struct page *page); -void svc_rqst_free (struct svc_rqst *);void svc_exit_thread (struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *prog, struct svc_stat *stats,
@@ -554,7 +551,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp) buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;xdr_init_decode (xdr, buf, argv->iov_base,NULL ); -xdr_set_scratch_page (xdr, rqstp->rq_scratch_page); +xdr_set_scratch_page (xdr,folio_page (rqstp->rq_scratch_folio,0 )); }/**
net/sunrpc/svc.c +17 -21
@@ -630,7 +630,19 @@ svc_release_buffer(struct svc_rqst *rqstp)put_page (rqstp->rq_pages[i]); } -struct svc_rqst * +static void +svc_rqst_free (struct svc_rqst *rqstp) +{ +svc_release_buffer (rqstp); +if (rqstp->rq_scratch_folio) +folio_put (rqstp->rq_scratch_folio); +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp->rq_auth_data); +kfree_rcu (rqstp, rq_rcu_head); +} + +static struct svc_rqst *svc_rqst_alloc (struct svc_serv *serv,struct svc_pool *pool,int node) {struct svc_rqst *rqstp;
@@ -644,8 +656,10 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node) rqstp->rq_server = serv; rqstp->rq_pool = pool; - rqstp->rq_scratch_page =alloc_pages_node (node,GFP_KERNEL ,0 ); -if (!rqstp->rq_scratch_page) +if (node ==NUMA_NO_NODE ) + node =numa_mem_id (); + rqstp->rq_scratch_folio =__folio_alloc_node (GFP_KERNEL ,0 , node); +if (!rqstp->rq_scratch_folio) goto out_enomem; rqstp->rq_argp =kmalloc_node (serv->sv_xdrsize,GFP_KERNEL , node);
@@ -664,7 +678,6 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)svc_rqst_free (rqstp);return NULL ; } -EXPORT_SYMBOL_GPL (svc_rqst_alloc);static struct svc_rqst *svc_prepare_thread (struct svc_serv *serv,struct svc_pool *pool,int node)
@@ -847,23 +860,6 @@ void svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) }EXPORT_SYMBOL_GPL (svc_rqst_replace_page); -/* -* Called from a server thread as it's exiting. Caller must hold the "service -* mutex" for the service. -*/ -void -svc_rqst_free (struct svc_rqst *rqstp) -{ -svc_release_buffer (rqstp); -if (rqstp->rq_scratch_page) -put_page (rqstp->rq_scratch_page); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp); -kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); -} -EXPORT_SYMBOL_GPL (svc_rqst_free); -void svc_exit_thread (struct svc_rqst *rqstp) {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/5 SUNRPC: make various functions static, or not exported.
From: NeilBrown <[email protected]>
[ Upstream commit f2b27e1d72527f94f030b6356b3187576e60885b ]
Various functions are only used within the sunrpc module, and several are only use in the one file. So clean up:
These are marked static, and any EXPORT is removed.
svc_rcpb_setup()
svc_rqst_alloc()
svc_rqst_free() - also moved before first use svc_rpcbind_set_version()
svc_drop() - also moved to svc.c
These are now not EXPORTed, but are not static.
svc_authenticate()
svc_sock_update_bufs()
[ Stable backport note: svc_rqst_alloc() and svc_rqst_free() are already
static, and the latter already precedes its first use, in the preceding dependency. Keep that implementation and its scratch folio handling. Do not import the newer page-release helper or authentication interfaces.
Retain the visibility cleanup for svc_rpcb_setup(), svc_rpcbind_set_version(), svc_authenticate(), and svc_sock_update_bufs(). Declare svc_authenticate() as int in the private header to match 6.1. Keep svc_drop() in svc_xprt.c: it still calls the file-local svc_xprt_release(), which upstream no longer calls from svc_drop().
Add braces around the existing svc_release_buffer() page-release loop, without changing its behavior, to provide the exact context required by the first hunk of c479bde671cb. No functions are added. ]
[ sashal: Reduced backport -- upstream f2b27e1d72527 touches 8 file(s), this
backport carries 7. Not backported here:
net/sunrpc/svc_xprt.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 6 ------ include/linux/sunrpc/svcauth.h | 1 - include/linux/sunrpc/svcsock.h | 1 - net/sunrpc/sunrpc.h | 4 ++++ net/sunrpc/svc.c | 10 +++++----- net/sunrpc/svcauth.c | 1 - net/sunrpc/svcsock.c | 1 - 7 files changed, 9 insertions(+), 15 deletions(-)
include/linux/sunrpc/svc.h +0 -6
@@ -468,7 +468,6 @@ struct svc_procedure {/* * Function prototypes. */ -int svc_rpcb_setup (struct svc_serv *serv,struct net *net);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net);int svc_bind (struct svc_serv *serv,struct net *net); struct svc_serv *svc_create(struct svc_program *, unsigned int,
@@ -509,11 +508,6 @@ int svc_generic_rpcbind_set(struct net *net, u32 version, int family, unsigned short proto, unsigned short port); -int svc_rpcbind_set_version (struct net *net, -const struct svc_program *progp, -u32 version,int family, -unsigned short proto, -unsigned short port);#define RPC_MAX_ADDRBUFLEN (63U)
include/linux/sunrpc/svcauth.h +0 -1
@@ -149,7 +149,6 @@ struct auth_ops {struct svc_xprt ; -extern int svc_authenticate (struct svc_rqst *rqstp);extern int svc_authorise (struct svc_rqst *rqstp);extern int svc_set_client (struct svc_rqst *rqstp);extern int svc_auth_register (rpc_authflavor_t flavor,struct auth_ops *aops);
include/linux/sunrpc/svcsock.h +0 -1
@@ -58,7 +58,6 @@ void svc_close_net(struct svc_serv *, struct net *);int svc_recv (struct svc_rqst *,long );int svc_send (struct svc_rqst *);void svc_drop (struct svc_rqst *); -void svc_sock_update_bufs (struct svc_serv *serv);int svc_addsock (struct svc_serv *serv,struct net *net,const int fd,char *name_return,const size_t len,const struct cred *cred);
net/sunrpc/sunrpc.h +4 -0
@@ -36,7 +36,11 @@ static inline int sock_is_loopback(struct sock *sk) return loopback; } +struct svc_serv ; +struct svc_rqst ;int rpc_clients_notifier_register (void );void rpc_clients_notifier_unregister (void );void auth_domain_cleanup (void ); +void svc_sock_update_bufs (struct svc_serv *serv); +int svc_authenticate (struct svc_rqst *rqstp);#endif /* _NET_SUNRPC_SUNRPC_H */
net/sunrpc/svc.c +5 -5
@@ -32,6 +32,7 @@#include <trace/events/sunrpc.h> #include "fail.h" +#include "sunrpc.h" #define RPCDBG_FACILITY RPCDBG_SVCDSP
@@ -388,7 +389,7 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv)return &serv->sv_pools[pidx % serv->sv_nrpools]; } -int svc_rpcb_setup (struct svc_serv *serv,struct net *net) +static int svc_rpcb_setup (struct svc_serv *serv,struct net *net) {int err;
@@ -400,7 +401,6 @@ int svc_rpcb_setup(struct svc_serv *serv, struct net *net)svc_unregister (serv, net);return 0 ; } -EXPORT_SYMBOL_GPL (svc_rpcb_setup);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net) {
@@ -625,9 +625,10 @@ svc_release_buffer(struct svc_rqst *rqstp) {unsigned int i; -for (i =0 ; i <ARRAY_SIZE (rqstp->rq_pages); i++) +for (i =0 ; i <ARRAY_SIZE (rqstp->rq_pages); i++) {if (rqstp->rq_pages[i])put_page (rqstp->rq_pages[i]); + } } static void
@@ -1009,7 +1010,7 @@ static int __svc_register(struct net *net, const char *progname,return error; } -int svc_rpcbind_set_version(struct net *net, +static int svc_rpcbind_set_version(struct net *net, const struct svc_program *progp, u32 version, int family, unsigned short proto,
@@ -1019,7 +1020,6 @@ int svc_rpcbind_set_version(struct net *net, version, family, proto, port); } -EXPORT_SYMBOL_GPL (svc_rpcbind_set_version); int svc_generic_rpcbind_set(struct net *net, const struct svc_program *progp,
net/sunrpc/svcauth.c +0 -1
@@ -84,7 +84,6 @@ svc_authenticate(struct svc_rqst *rqstp) rqstp->rq_authop = aops;return aops->accept (rqstp); } -EXPORT_SYMBOL_GPL (svc_authenticate);int svc_set_client (struct svc_rqst *rqstp) {
net/sunrpc/svcsock.c +0 -1
@@ -1283,7 +1283,6 @@ void svc_sock_update_bufs(struct svc_serv *serv)set_bit (XPT_CHNGBUF , &svsk->sk_xprt.xpt_flags);spin_unlock_bh (&serv->sv_lock); } -EXPORT_SYMBOL_GPL (svc_sock_update_bufs);/* * Initialize socket for RPC use and create svc_sock struct
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/5 SUNRPC: Introduce xdr_set_scratch_folio()
In reply to Sasha LevinFrom: Anna Schumaker <[email protected]>
[ Upstream commit c8a127596edc026e5364a7a609986dcd3999914c ]
This will replace xdr_set_scratch_page() when we switch pages to folios.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/xdr.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
include/linux/sunrpc/xdr.h +13 -0
@@ -299,6 +299,19 @@ xdr_set_scratch_page(struct xdr_stream *xdr, struct page *page)xdr_set_scratch_buffer (xdr,page_address (page),PAGE_SIZE ); } +/** +* xdr_set_scratch_folio - Attach a scratch buffer for decoding data +* @xdr: pointer to xdr_stream struct +* @page: an anonymous folio +* +* See xdr_set_scratch_buffer(). +*/ +static inline void +xdr_set_scratch_folio (struct xdr_stream *xdr,struct folio *folio) +{ +xdr_set_scratch_buffer (xdr,folio_address (folio),folio_size (folio)); +} +/** * xdr_reset_scratch_buffer - Clear scratch buffer information * @xdr: pointer to xdr_stream struct
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/5 SUNRPC: Update svcxdr_init_decode() to call xdr_set_scratch_folio()
In reply to Sasha LevinFrom: Anna Schumaker <[email protected]>
[ Upstream commit d57e43b72bf2071caac90da323849c3983a695f0 ]
The only snag here is that __folio_alloc_node() doesn't handle NUMA_NO_NODE, so I also need to update svc_pool_map_get_node() to return numa_mem_id() instead. I arrived at this approach by looking at what other users of __folio_alloc_node() do for this case.
[ Stable backport note: Earlier dependencies already converted rq_scratch_page
to rq_scratch_folio, including allocation and release. Keep those changes and switch svcxdr_init_decode() to the xdr_set_scratch_folio() helper added by the immediately preceding dependency.
Drop the svc_pool_map_get_node() change: svc_rqst_alloc() already converts NUMA_NO_NODE to numa_mem_id() before __folio_alloc_node(). Preserve that fallback and the existing pool/thread placement policy.
The resulting dependency changes only the existing decoder helper call; no functions are added. The scratch-folio release and surrounding context required by c479bde671cb are retained, and its patch applies cleanly. ]
[ sashal: Reduced backport -- upstream d57e43b72bf20 touches 2 file(s), this
backport carries 1. Not backported here:
net/sunrpc/svc.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/sunrpc/svc.h +1 -1
@@ -545,7 +545,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp) buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;xdr_init_decode (xdr, buf, argv->iov_base,NULL ); -xdr_set_scratch_page (xdr,folio_page (rqstp->rq_scratch_folio,0 )); +xdr_set_scratch_folio (xdr, rqstp->rq_scratch_folio); }/**
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/5 sunrpc: defer rq_argp and rq_resp free until after RCU grace period
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit c479bde671cbe2f9e152834a8b0eb7c3c295bbaf ]
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
--- net/sunrpc/svc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/svc.c +10 -3
@@ -631,16 +631,23 @@ svc_release_buffer(struct svc_rqst *rqstp) } } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +static void svc_rqst_free (struct svc_rqst *rqstp) {svc_release_buffer (rqstp);if (rqstp->rq_scratch_folio)folio_put (rqstp->rq_scratch_folio); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst *
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 1/5 SUNRPC: remove timeout arg from svc_recv()
In reply to [email protected]From: NeilBrown <[email protected]>
[ Upstream commit c743b4259c3af2c0637c307f08a062d25fa3c99f ]
Most svc threads have no interest in a timeout.
nfsd sets it to 1 hour, but this is a wart of no significance.
lockd uses the timeout so that it can call nlmsvc_retry_blocked(). It also sometimes calls svc_wake_up() to ensure this is called.
So change lockd to be consistent and always use svc_wake_up() to trigger nlmsvc_retry_blocked() - using a timer instead of a timeout to svc_recv().
And change svc_recv() to not take a timeout arg.
This makes the sp_threads_timedout counter always zero.
[ Stable backport note: The svc_recv timeout cleanup is unrelated to the follow-up fix and does not fit the older lockd service loop. Drop those hunks. Instead, make the existing svc request allocation/free helpers private, move svc_rqst_free() before its first use, and convert the scratch page to a folio. This aligns the lifetime-management context so the follow-up fix applies cleanly. ]
[ sashal: Reduced backport -- upstream c743b4259c3af touches 9 file(s), this
backport carries 2. Not backported here:
fs/lockd/svc.c
fs/lockd/svclock.c
fs/nfs/callback.c
fs/nfsd/nfssvc.c
include/linux/lockd/lockd.h
include/linux/sunrpc/svcsock.h
net/sunrpc/svc_xprt.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 7 ++----- net/sunrpc/svc.c | 38 +++++++++++++++++--------------------- 2 files changed, 19 insertions(+), 26 deletions(-)
include/linux/sunrpc/svc.h +2 -5
@@ -247,7 +247,7 @@ struct svc_rqst {struct xdr_buf rq_arg;struct xdr_stream rq_arg_stream;struct xdr_stream rq_res_stream; -struct page *rq_scratch_page; +struct folio *rq_scratch_folio;struct xdr_buf rq_res;struct page *rq_pages[RPCSVC_MAXPAGES +1 ];struct page * *rq_respages;/* points into rq_pages */
@@ -473,11 +473,8 @@ void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);int svc_bind (struct svc_serv *serv,struct net *net);struct svc_serv *svc_create (struct svc_program *,unsigned int ,int (*threadfn)(void *data)); -struct svc_rqst *svc_rqst_alloc (struct svc_serv *serv, -struct svc_pool *pool,int node);void svc_rqst_replace_page (struct svc_rqst *rqstp,struct page *page); -void svc_rqst_free (struct svc_rqst *);void svc_exit_thread (struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *prog, struct svc_stat *stats,
@@ -554,7 +551,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp) buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;xdr_init_decode (xdr, buf, argv->iov_base,NULL ); -xdr_set_scratch_page (xdr, rqstp->rq_scratch_page); +xdr_set_scratch_page (xdr,folio_page (rqstp->rq_scratch_folio,0 )); }/**
net/sunrpc/svc.c +17 -21
@@ -630,7 +630,19 @@ svc_release_buffer(struct svc_rqst *rqstp)put_page (rqstp->rq_pages[i]); } -struct svc_rqst * +static void +svc_rqst_free (struct svc_rqst *rqstp) +{ +svc_release_buffer (rqstp); +if (rqstp->rq_scratch_folio) +folio_put (rqstp->rq_scratch_folio); +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp->rq_auth_data); +kfree_rcu (rqstp, rq_rcu_head); +} + +static struct svc_rqst *svc_rqst_alloc (struct svc_serv *serv,struct svc_pool *pool,int node) {struct svc_rqst *rqstp;
@@ -644,8 +656,10 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node) rqstp->rq_server = serv; rqstp->rq_pool = pool; - rqstp->rq_scratch_page =alloc_pages_node (node,GFP_KERNEL ,0 ); -if (!rqstp->rq_scratch_page) +if (node ==NUMA_NO_NODE ) + node =numa_mem_id (); + rqstp->rq_scratch_folio =__folio_alloc_node (GFP_KERNEL ,0 , node); +if (!rqstp->rq_scratch_folio) goto out_enomem; rqstp->rq_argp =kmalloc_node (serv->sv_xdrsize,GFP_KERNEL , node);
@@ -664,7 +678,6 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)svc_rqst_free (rqstp);return NULL ; } -EXPORT_SYMBOL_GPL (svc_rqst_alloc);static struct svc_rqst *svc_prepare_thread (struct svc_serv *serv,struct svc_pool *pool,int node)
@@ -847,23 +860,6 @@ void svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) }EXPORT_SYMBOL_GPL (svc_rqst_replace_page); -/* -* Called from a server thread as it's exiting. Caller must hold the "service -* mutex" for the service. -*/ -void -svc_rqst_free (struct svc_rqst *rqstp) -{ -svc_release_buffer (rqstp); -if (rqstp->rq_scratch_page) -put_page (rqstp->rq_scratch_page); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp); -kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); -} -EXPORT_SYMBOL_GPL (svc_rqst_free); -void svc_exit_thread (struct svc_rqst *rqstp) {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/5 SUNRPC: make various functions static, or not exported.
From: NeilBrown <[email protected]>
[ Upstream commit f2b27e1d72527f94f030b6356b3187576e60885b ]
Various functions are only used within the sunrpc module, and several are only use in the one file. So clean up:
These are marked static, and any EXPORT is removed.
svc_rcpb_setup()
svc_rqst_alloc()
svc_rqst_free() - also moved before first use svc_rpcbind_set_version()
svc_drop() - also moved to svc.c
These are now not EXPORTed, but are not static.
svc_authenticate()
svc_sock_update_bufs()
[ Stable backport note: svc_rqst_alloc() and svc_rqst_free() are already
static, and the latter already precedes its first use, in the preceding dependency. Keep that implementation and its scratch folio handling. Do not import the newer page-release helper or authentication interfaces.
Retain the visibility cleanup for svc_rpcb_setup(), svc_rpcbind_set_version(), svc_authenticate(), and svc_sock_update_bufs(). Declare svc_authenticate() as int in the private header to match 6.1. Keep svc_drop() in svc_xprt.c: it still calls the file-local svc_xprt_release(), which upstream no longer calls from svc_drop().
Add braces around the existing svc_release_buffer() page-release loop, without changing its behavior, to provide the exact context required by the first hunk of c479bde671cb. No functions are added. ]
[ sashal: Reduced backport -- upstream f2b27e1d72527 touches 8 file(s), this
backport carries 7. Not backported here:
net/sunrpc/svc_xprt.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 6 ------ include/linux/sunrpc/svcauth.h | 1 - include/linux/sunrpc/svcsock.h | 1 - net/sunrpc/sunrpc.h | 4 ++++ net/sunrpc/svc.c | 10 +++++----- net/sunrpc/svcauth.c | 1 - net/sunrpc/svcsock.c | 1 - 7 files changed, 9 insertions(+), 15 deletions(-)
include/linux/sunrpc/svc.h +0 -6
@@ -468,7 +468,6 @@ struct svc_procedure {/* * Function prototypes. */ -int svc_rpcb_setup (struct svc_serv *serv,struct net *net);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net);int svc_bind (struct svc_serv *serv,struct net *net); struct svc_serv *svc_create(struct svc_program *, unsigned int,
@@ -509,11 +508,6 @@ int svc_generic_rpcbind_set(struct net *net, u32 version, int family, unsigned short proto, unsigned short port); -int svc_rpcbind_set_version (struct net *net, -const struct svc_program *progp, -u32 version,int family, -unsigned short proto, -unsigned short port);#define RPC_MAX_ADDRBUFLEN (63U)
include/linux/sunrpc/svcauth.h +0 -1
@@ -149,7 +149,6 @@ struct auth_ops {struct svc_xprt ; -extern int svc_authenticate (struct svc_rqst *rqstp);extern int svc_authorise (struct svc_rqst *rqstp);extern int svc_set_client (struct svc_rqst *rqstp);extern int svc_auth_register (rpc_authflavor_t flavor,struct auth_ops *aops);
include/linux/sunrpc/svcsock.h +0 -1
@@ -58,7 +58,6 @@ void svc_close_net(struct svc_serv *, struct net *);int svc_recv (struct svc_rqst *,long );int svc_send (struct svc_rqst *);void svc_drop (struct svc_rqst *); -void svc_sock_update_bufs (struct svc_serv *serv);int svc_addsock (struct svc_serv *serv,struct net *net,const int fd,char *name_return,const size_t len,const struct cred *cred);
net/sunrpc/sunrpc.h +4 -0
@@ -36,7 +36,11 @@ static inline int sock_is_loopback(struct sock *sk) return loopback; } +struct svc_serv ; +struct svc_rqst ;int rpc_clients_notifier_register (void );void rpc_clients_notifier_unregister (void );void auth_domain_cleanup (void ); +void svc_sock_update_bufs (struct svc_serv *serv); +int svc_authenticate (struct svc_rqst *rqstp);#endif /* _NET_SUNRPC_SUNRPC_H */
net/sunrpc/svc.c +5 -5
@@ -32,6 +32,7 @@#include <trace/events/sunrpc.h> #include "fail.h" +#include "sunrpc.h" #define RPCDBG_FACILITY RPCDBG_SVCDSP
@@ -388,7 +389,7 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv)return &serv->sv_pools[pidx % serv->sv_nrpools]; } -int svc_rpcb_setup (struct svc_serv *serv,struct net *net) +static int svc_rpcb_setup (struct svc_serv *serv,struct net *net) {int err;
@@ -400,7 +401,6 @@ int svc_rpcb_setup(struct svc_serv *serv, struct net *net)svc_unregister (serv, net);return 0 ; } -EXPORT_SYMBOL_GPL (svc_rpcb_setup);void svc_rpcb_cleanup (struct svc_serv *serv,struct net *net) {
@@ -625,9 +625,10 @@ svc_release_buffer(struct svc_rqst *rqstp) {unsigned int i; -for (i =0 ; i <ARRAY_SIZE (rqstp->rq_pages); i++) +for (i =0 ; i <ARRAY_SIZE (rqstp->rq_pages); i++) {if (rqstp->rq_pages[i])put_page (rqstp->rq_pages[i]); + } } static void
@@ -1009,7 +1010,7 @@ static int __svc_register(struct net *net, const char *progname,return error; } -int svc_rpcbind_set_version(struct net *net, +static int svc_rpcbind_set_version(struct net *net, const struct svc_program *progp, u32 version, int family, unsigned short proto,
@@ -1019,7 +1020,6 @@ int svc_rpcbind_set_version(struct net *net, version, family, proto, port); } -EXPORT_SYMBOL_GPL (svc_rpcbind_set_version); int svc_generic_rpcbind_set(struct net *net, const struct svc_program *progp,
net/sunrpc/svcauth.c +0 -1
@@ -84,7 +84,6 @@ svc_authenticate(struct svc_rqst *rqstp) rqstp->rq_authop = aops;return aops->accept (rqstp); } -EXPORT_SYMBOL_GPL (svc_authenticate);int svc_set_client (struct svc_rqst *rqstp) {
net/sunrpc/svcsock.c +0 -1
@@ -1283,7 +1283,6 @@ void svc_sock_update_bufs(struct svc_serv *serv)set_bit (XPT_CHNGBUF , &svsk->sk_xprt.xpt_flags);spin_unlock_bh (&serv->sv_lock); } -EXPORT_SYMBOL_GPL (svc_sock_update_bufs);/* * Initialize socket for RPC use and create svc_sock struct
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/5 SUNRPC: Introduce xdr_set_scratch_folio()
In reply to Sasha LevinFrom: Anna Schumaker <[email protected]>
[ Upstream commit c8a127596edc026e5364a7a609986dcd3999914c ]
This will replace xdr_set_scratch_page() when we switch pages to folios.
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/xdr.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
include/linux/sunrpc/xdr.h +13 -0
@@ -299,6 +299,19 @@ xdr_set_scratch_page(struct xdr_stream *xdr, struct page *page)xdr_set_scratch_buffer (xdr,page_address (page),PAGE_SIZE ); } +/** +* xdr_set_scratch_folio - Attach a scratch buffer for decoding data +* @xdr: pointer to xdr_stream struct +* @page: an anonymous folio +* +* See xdr_set_scratch_buffer(). +*/ +static inline void +xdr_set_scratch_folio (struct xdr_stream *xdr,struct folio *folio) +{ +xdr_set_scratch_buffer (xdr,folio_address (folio),folio_size (folio)); +} +/** * xdr_reset_scratch_buffer - Clear scratch buffer information * @xdr: pointer to xdr_stream struct
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/5 SUNRPC: Update svcxdr_init_decode() to call xdr_set_scratch_folio()
In reply to Sasha LevinFrom: Anna Schumaker <[email protected]>
[ Upstream commit d57e43b72bf2071caac90da323849c3983a695f0 ]
The only snag here is that __folio_alloc_node() doesn't handle NUMA_NO_NODE, so I also need to update svc_pool_map_get_node() to return numa_mem_id() instead. I arrived at this approach by looking at what other users of __folio_alloc_node() do for this case.
[ Stable backport note: Earlier dependencies already converted rq_scratch_page
to rq_scratch_folio, including allocation and release. Keep those changes and switch svcxdr_init_decode() to the xdr_set_scratch_folio() helper added by the immediately preceding dependency.
Drop the svc_pool_map_get_node() change: svc_rqst_alloc() already converts NUMA_NO_NODE to numa_mem_id() before __folio_alloc_node(). Preserve that fallback and the existing pool/thread placement policy.
The resulting dependency changes only the existing decoder helper call; no functions are added. The scratch-folio release and surrounding context required by c479bde671cb are retained, and its patch applies cleanly. ]
[ sashal: Reduced backport -- upstream d57e43b72bf20 touches 2 file(s), this
backport carries 1. Not backported here:
net/sunrpc/svc.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: c479bde671cb ("sunrpc: defer rq_argp and rq_resp free until after RCU grace period")
--- include/linux/sunrpc/svc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/sunrpc/svc.h +1 -1
@@ -545,7 +545,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp) buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;xdr_init_decode (xdr, buf, argv->iov_base,NULL ); -xdr_set_scratch_page (xdr,folio_page (rqstp->rq_scratch_folio,0 )); +xdr_set_scratch_folio (xdr, rqstp->rq_scratch_folio); }/**
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/5 sunrpc: defer rq_argp and rq_resp free until after RCU grace period
In reply to Sasha LevinFrom: Jeff Layton <[email protected]>
[ Upstream commit c479bde671cbe2f9e152834a8b0eb7c3c295bbaf ]
svc_rqst_free() frees rqstp->rq_argp and rqstp->rq_resp synchronously via kfree(), but defers the rqstp struct free via kfree_rcu(). After svc_exit_thread() calls list_del_rcu() and svc_rqst_free(), there is a window where RCU readers that started before list_del_rcu() can still traverse the thread list and find the rqstp. These readers (e.g. nfsd_nl_rpc_status_get_dumpit()) dereference rqstp->rq_argp, which has already been freed — a use-after-free.
Fix this by moving the kfree of rq_argp and rq_resp into an explicit call_rcu() callback alongside the struct free. Resources not accessed by RCU readers (bvec, buffer pages, scratch folio, auth_data) remain synchronously freed.
--- net/sunrpc/svc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/sunrpc/svc.c +10 -3
@@ -631,16 +631,23 @@ svc_release_buffer(struct svc_rqst *rqstp) } } +static void svc_rqst_free_rcu (struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + +kfree (rqstp->rq_resp); +kfree (rqstp->rq_argp); +kfree (rqstp); +} +static void svc_rqst_free (struct svc_rqst *rqstp) {svc_release_buffer (rqstp);if (rqstp->rq_scratch_folio)folio_put (rqstp->rq_scratch_folio); -kfree (rqstp->rq_resp); -kfree (rqstp->rq_argp);kfree (rqstp->rq_auth_data); -kfree_rcu (rqstp, rq_rcu_head); +call_rcu (&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst *
Signature · 1 line
2.53.0