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)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Fri, 12 Apr 2024 14:13:02 +0000 (16:13 +0200)
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>
drivers/s390/cio/device.c

index f95d12345d98a6dbfd1efa850829586852d409ab..920f550bc313bf6c036ed11c539d4319852cb2dd 100644 (file)
@@ -363,10 +363,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,
@@ -375,7 +373,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)) {