FAILED: patch "[PATCH] rpcrdma: arm rn_done before publishing the notification" failed to apply to 6.12-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 6.12-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.12.y
git checkout FETCH_HEAD
git cherry-pick -x 5b06f706374c37375bdff9d21cc10e61df925a92
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090359-consensus-citizen-be20@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 5b06f706374c37375bdff9d21cc10e61df925a92 Mon Sep 17 00:00:00 2001
From: Chuck Lever <[email protected]>
Date: Mon, 1 Jun 2026 16:17:03 -0400
Subject: [PATCH] rpcrdma: arm rn_done before publishing the notification
rpcrdma_rn_register() inserts @rn into rd_xa with xa_alloc() before storing the caller's callback in rn->rn_done. The xarray makes @rn reachable to rpcrdma_remove_one(), which walks rd_xa and invokes rn->rn_done(rn) for every registered notification. A device removal that races a fresh registration can therefore observe @rn with rn_done still NULL, because the notification objects are zero allocated by their owners, and call through a NULL function pointer.
Store rn->rn_done before xa_alloc() publishes @rn. The xarray's store-side and load-side ordering then guarantees that any CPU which finds @rn in rd_xa also observes the armed callback.
rpcrdma_rn_unregister() treats a non-NULL rn_done as the sentinel for a completed registration, so the early store must not survive a failed registration. Clear rn_done again when xa_alloc() fails. Were it left set, the failed-accept cleanup path would call rpcrdma_rn_unregister() on an @rn that was never inserted, erasing an unrelated rd_xa slot and underflowing rd_kref.
net/sunrpc/xprtrdma/ib_client.c +19 -7
@@ -52,8 +52,8 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device)* is unregistered first. * * On failure, a negative errno is returned. rn->rn_done is left -* NULL on every failure path (it is assigned only after xa_alloc -* and kref_get have both succeeded), so the @rn may safely be +* NULL on every failure path (it is armed before xa_alloc but +* cleared again if xa_alloc fails), so the @rn may safely be * passed to rpcrdma_rn_unregister() without a separate * registered/unregistered flag in the caller. */
@@ -66,10 +66,21 @@ int rpcrdma_rn_register(struct ib_device *device,if (!rd ||test_bit (RPCRDMA_RD_F_REMOVING , &rd->rd_flags))return -ENETUNREACH ; -if (xa_alloc (&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b,GFP_KERNEL ) <0 ) -return -ENOMEM ; -kref_get (&rd->rd_kref); +/* +* Arm rn_done before xa_alloc() publishes @rn: once @rn is +* visible in rd_xa, a concurrent rpcrdma_remove_one() can +* call rn->rn_done(), so the pointer must already be set. +* +* Restore NULL if xa_alloc() fails. rn_done doubles as the +* registration sentinel for rpcrdma_rn_unregister(); a stale +* value would unregister an @rn that was never inserted. +*/ rn->rn_done = done; +if (xa_alloc (&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b,GFP_KERNEL ) <0 ) { + rn->rn_done =NULL ; +return -ENOMEM ; + } +kref_get (&rd->rd_kref);trace_rpcrdma_client_register (device, rn);return 0 ; }
@@ -102,8 +113,9 @@ void rpcrdma_rn_unregister(struct ib_device *device,/* * rn_done is the registration sentinel: rpcrdma_rn_register -* assigns it last, after xa_alloc and kref_get have both -* succeeded. A NULL rn_done means this notification was +* leaves it NULL on every failure path, clearing it again if +* xa_alloc fails, so a non-NULL rn_done marks a completed +* registration. A NULL rn_done means this notification was * never registered (or its registration failed) or has * already been unregistered, and the call is a no-op. * Without this guard, rn_index == 0 from a kzalloc'd
Sasha Levin
maintainerPATCH 1/1 rpcrdma: arm rn_done before publishing the notification
From: Chuck Lever <[email protected]>
[ Upstream commit 5b06f706374c37375bdff9d21cc10e61df925a92 ]
rpcrdma_rn_register() inserts @rn into rd_xa with xa_alloc() before storing the caller's callback in rn->rn_done. The xarray makes @rn reachable to rpcrdma_remove_one(), which walks rd_xa and invokes rn->rn_done(rn) for every registered notification. A device removal that races a fresh registration can therefore observe @rn with rn_done still NULL, because the notification objects are zero allocated by their owners, and call through a NULL function pointer.
Store rn->rn_done before xa_alloc() publishes @rn. The xarray's store-side and load-side ordering then guarantees that any CPU which finds @rn in rd_xa also observes the armed callback.
rpcrdma_rn_unregister() treats a non-NULL rn_done as the sentinel for a completed registration, so the early store must not survive a failed registration. Clear rn_done again when xa_alloc() fails. Were it left set, the failed-accept cleanup path would call rpcrdma_rn_unregister() on an @rn that was never inserted, erasing an unrelated rd_xa slot and underflowing rd_kref.
--- net/sunrpc/xprtrdma/ib_client.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
net/sunrpc/xprtrdma/ib_client.c +18 -6
@@ -52,8 +52,8 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device)* is unregistered first. * * On failure, a negative errno is returned. rn->rn_done is left -* NULL on every failure path (it is assigned only after xa_alloc -* and kref_get have both succeeded), so the @rn may safely be +* NULL on every failure path (it is armed before xa_alloc but +* cleared again if xa_alloc fails), so the @rn may safely be * passed to rpcrdma_rn_unregister() without a separate * registered/unregistered flag in the caller. */
@@ -66,10 +66,21 @@ int rpcrdma_rn_register(struct ib_device *device,if (!rd ||test_bit (RPCRDMA_RD_F_REMOVING , &rd->rd_flags))return -ENETUNREACH ; -if (xa_alloc (&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b,GFP_KERNEL ) <0 ) +/* +* Arm rn_done before xa_alloc() publishes @rn: once @rn is +* visible in rd_xa, a concurrent rpcrdma_remove_one() can +* call rn->rn_done(), so the pointer must already be set. +* +* Restore NULL if xa_alloc() fails. rn_done doubles as the +* registration sentinel for rpcrdma_rn_unregister(); a stale +* value would unregister an @rn that was never inserted. +*/ + rn->rn_done = done; +if (xa_alloc (&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b,GFP_KERNEL ) <0 ) { + rn->rn_done =NULL ;return -ENOMEM ; + }kref_get (&rd->rd_kref); - rn->rn_done = done;trace_rpcrdma_client_register (device, rn);return 0 ; }
@@ -102,8 +113,9 @@ void rpcrdma_rn_unregister(struct ib_device *device,/* * rn_done is the registration sentinel: rpcrdma_rn_register -* assigns it last, after xa_alloc and kref_get have both -* succeeded. A NULL rn_done means this notification was +* leaves it NULL on every failure path, clearing it again if +* xa_alloc fails, so a non-NULL rn_done marks a completed +* registration. A NULL rn_done means this notification was * never registered (or its registration failed) or has * already been unregistered, and the call is a no-op. * Without this guard, rn_index == 0 from a kzalloc'd
Signature · 1 line
2.53.0