aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/cio/device_fsm.c
diff options
context:
space:
mode:
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>2008-08-21 19:46:39 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-08-21 19:46:41 +0200
commit91c36919a456589f4f073671474a1f899e0d3c2b (patch)
tree63cb2ee1afd9b00bf2ea4959482d58f402bb21f3 /drivers/s390/cio/device_fsm.c
parent49fd38bdaa96f093fcad3176a781a4d0de8f8602 (diff)
[S390] cio: call ccw driver notify function with lock held
Calling a ccw driver's notify function without the ccw device lock held opens up a race window between discovery and handling of a change in the device operational state. As a result, the device driver may encounter unexpected device malfunction, leading to out-of-retry situations or similar. Remove race by extending the ccw device lock from state change discovery to the calling of the notify function. Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device_fsm.c')
-rw-r--r--drivers/s390/cio/device_fsm.c31
1 files changed, 19 insertions, 12 deletions
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 8b5fe57fb2f..550508df952 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -337,26 +337,34 @@ int ccw_device_notify(struct ccw_device *cdev, int event)
return 0;
if (!cdev->online)
return 0;
+ CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
+ cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
+ event);
return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
}
-static void
-ccw_device_oper_notify(struct work_struct *work)
+static void cmf_reenable_delayed(struct work_struct *work)
{
struct ccw_device_private *priv;
struct ccw_device *cdev;
- int ret;
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
- ret = ccw_device_notify(cdev, CIO_OPER);
- if (ret) {
+ cmf_reenable(cdev);
+}
+
+static void ccw_device_oper_notify(struct ccw_device *cdev)
+{
+ if (ccw_device_notify(cdev, CIO_OPER)) {
/* Reenable channel measurements, if needed. */
- cmf_reenable(cdev);
- wake_up(&cdev->private->wait_q);
- } else
- /* Driver doesn't want device back. */
- ccw_device_do_unreg_rereg(work);
+ PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ return;
+ }
+ /* Driver doesn't want device back. */
+ ccw_device_set_notoper(cdev);
+ PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unreg_rereg);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
}
/*
@@ -386,8 +394,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
if (cdev->private->flags.donotify) {
cdev->private->flags.donotify = 0;
- PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify);
- queue_work(ccw_device_notify_work, &cdev->private->kick_work);
+ ccw_device_oper_notify(cdev);
}
wake_up(&cdev->private->wait_q);