sunrpc: Fix potential race conditions in rpc_sysfs_xprt_state_change()
authorAnna Schumaker <Anna.Schumaker@Netapp.com>
Mon, 15 Nov 2021 16:54:25 +0000 (11:54 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 16 Feb 2022 11:56:10 +0000 (12:56 +0100)
[ Upstream commit 1a48db3fef499f615b56093947ec4b0d3d8e3021 ]

We need to use test_and_set_bit() when changing xprt state flags to
avoid potentially getting xps->xps_nactive out of sync.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/sunrpc/sysfs.c

index 379cf0e..326a314 100644 (file)
@@ -305,25 +305,28 @@ static ssize_t rpc_sysfs_xprt_state_change(struct kobject *kobj,
                goto release_tasks;
        }
        if (offline) {
-               set_bit(XPRT_OFFLINE, &xprt->state);
-               spin_lock(&xps->xps_lock);
-               xps->xps_nactive--;
-               spin_unlock(&xps->xps_lock);
+               if (!test_and_set_bit(XPRT_OFFLINE, &xprt->state)) {
+                       spin_lock(&xps->xps_lock);
+                       xps->xps_nactive--;
+                       spin_unlock(&xps->xps_lock);
+               }
        } else if (online) {
-               clear_bit(XPRT_OFFLINE, &xprt->state);
-               spin_lock(&xps->xps_lock);
-               xps->xps_nactive++;
-               spin_unlock(&xps->xps_lock);
+               if (test_and_clear_bit(XPRT_OFFLINE, &xprt->state)) {
+                       spin_lock(&xps->xps_lock);
+                       xps->xps_nactive++;
+                       spin_unlock(&xps->xps_lock);
+               }
        } else if (remove) {
                if (test_bit(XPRT_OFFLINE, &xprt->state)) {
-                       set_bit(XPRT_REMOVE, &xprt->state);
-                       xprt_force_disconnect(xprt);
-                       if (test_bit(XPRT_CONNECTED, &xprt->state)) {
-                               if (!xprt->sending.qlen &&
-                                   !xprt->pending.qlen &&
-                                   !xprt->backlog.qlen &&
-                                   !atomic_long_read(&xprt->queuelen))
-                                       rpc_xprt_switch_remove_xprt(xps, xprt);
+                       if (!test_and_set_bit(XPRT_REMOVE, &xprt->state)) {
+                               xprt_force_disconnect(xprt);
+                               if (test_bit(XPRT_CONNECTED, &xprt->state)) {
+                                       if (!xprt->sending.qlen &&
+                                           !xprt->pending.qlen &&
+                                           !xprt->backlog.qlen &&
+                                           !atomic_long_read(&xprt->queuelen))
+                                               rpc_xprt_switch_remove_xprt(xps, xprt);
+                               }
                        }
                } else {
                        count = -EINVAL;