s390/cio: fix race condition during online processing
authorPeter Oberparleiter <oberpar@linux.ibm.com>
Wed, 10 Apr 2024 09:46:19 +0000 (11:46 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 27 Apr 2024 15:05:25 +0000 (17:05 +0200)
[ Upstream commit 2d8527f2f911fab84aec04df4788c0c23af3df48 ]

A race condition exists in ccw_device_set_online() that can cause the
online process to fail, leaving the affected device in an inconsistent
state. As a result, subsequent attempts to set that device online fail
with return code ENODEV.

The problem occurs when a path verification request arrives after
a wait for final device state completed, but before the result state
is evaluated.

Fix this by ensuring that the CCW-device lock is held between
determining final state and checking result state.

Note that since:

commit 2297791c92d0 ("s390/cio: dont unregister subchannel from child-drivers")

path verification requests are much more likely to occur during boot,
resulting in an increased chance of this race condition occurring.

Fixes: 2297791c92d0 ("s390/cio: dont unregister subchannel from child-drivers")
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/s390/cio/device.c

index a111154a90465c1e9283a72fcbedb012936e90b6..c16f18cfeed720447d60927971c1a9e994aced43 100644 (file)
@@ -360,10 +360,8 @@ int ccw_device_set_online(struct ccw_device *cdev)
 
        spin_lock_irq(cdev->ccwlock);
        ret = ccw_device_online(cdev);
-       spin_unlock_irq(cdev->ccwlock);
-       if (ret == 0)
-               wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
-       else {
+       if (ret) {
+               spin_unlock_irq(cdev->ccwlock);
                CIO_MSG_EVENT(0, "ccw_device_online returned %d, "
                              "device 0.%x.%04x\n",
                              ret, cdev->private->dev_id.ssid,
@@ -372,7 +370,12 @@ int ccw_device_set_online(struct ccw_device *cdev)
                put_device(&cdev->dev);
                return ret;
        }
-       spin_lock_irq(cdev->ccwlock);
+       /* Wait until a final state is reached */
+       while (!dev_fsm_final_state(cdev)) {
+               spin_unlock_irq(cdev->ccwlock);
+               wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
+               spin_lock_irq(cdev->ccwlock);
+       }
        /* Check if online processing was successful */
        if ((cdev->private->state != DEV_STATE_ONLINE) &&
            (cdev->private->state != DEV_STATE_W4SENSE)) {