sunrpc: Fix potential race conditions in rpc_sysfs_xprt_state_change()

[ 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>
This commit is contained in:
Anna Schumaker
2021-11-15 11:54:25 -05:00
committed by Greg Kroah-Hartman
parent 4b22aa42bd
commit 4403233b87

View 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;