Commit 2d8527f2 authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Alexander Gordeev
Browse files

s390/cio: fix race condition during online processing



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 2297791c ("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: 2297791c ("s390/cio: dont unregister subchannel from child-drivers")
Reviewed-by: default avatarAlexandra Winter <wintera@linux.ibm.com>
Reviewed-by: default avatarVineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
parent 607638fa
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -363,10 +363,8 @@ int ccw_device_set_online(struct ccw_device *cdev)

	spin_lock_irq(cdev->ccwlock);
	ret = ccw_device_online(cdev);
	if (ret) {
		spin_unlock_irq(cdev->ccwlock);
	if (ret == 0)
		wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
	else {
		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;
	}
	/* 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)) {