summaryrefslogtreecommitdiff
path: root/net/sunrpc
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-11 11:51:26 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-11 11:51:26 +0200
commitffb45b46184f54bf84d95e82df46932294b2031a (patch)
treecc199e0ec572fb2bf65fa4eb144f0e562bb34687 /net/sunrpc
parent5eccd39d8efa3bc8d557be50f202bbf023837eed (diff)
parenta300e35c0a4b4a38fb53742ea6e2a203c98ee523 (diff)
downloadlinux-stable-linux-rolling-stable.tar.gz
linux-stable-linux-rolling-stable.zip
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net/sunrpc')
-rw-r--r--net/sunrpc/xprtrdma/ib_client.c24
1 files changed, 18 insertions, 6 deletions
diff --git a/net/sunrpc/xprtrdma/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c
index 69166d5d9987..188f7a13397f 100644
--- a/net/sunrpc/xprtrdma/ib_client.c
+++ b/net/sunrpc/xprtrdma/ib_client.c
@@ -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