summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2007-03-05 23:35:56 +0100
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-03-05 23:35:56 +0100
commitee04bbccdeb11bdbc54015be8dca30a0deeca5e4 (patch)
treedebc4b75f59aa188db3e7e3eedf7bcacb73a268b /drivers
parent482b05dd533da162fa8d04c61712fae297bea3e0 (diff)
[S390] cio: Fix locking when calling notify function.
Make sure we hold the device lock when we modify the ccw device structure but always call the notify function without the lock held. Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/cio/device_fsm.c67
1 files changed, 47 insertions, 20 deletions
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 51238e7555b..8baa9cd3794 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -334,20 +334,29 @@ ccw_device_oper_notify(struct work_struct *work)
struct ccw_device *cdev;
struct subchannel *sch;
int ret;
+ unsigned long flags;
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
+ spin_lock_irqsave(cdev->ccwlock, flags);
sch = to_subchannel(cdev->dev.parent);
- ret = (sch->driver && sch->driver->notify) ?
- sch->driver->notify(&sch->dev, CIO_OPER) : 0;
- if (!ret)
- /* Driver doesn't want device back. */
- ccw_device_do_unreg_rereg(work);
- else {
+ if (sch->driver && sch->driver->notify) {
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ ret = sch->driver->notify(&sch->dev, CIO_OPER);
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ } else
+ ret = 0;
+ if (ret) {
/* Reenable channel measurements, if needed. */
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
cmf_reenable(cdev);
+ spin_lock_irqsave(cdev->ccwlock, flags);
wake_up(&cdev->private->wait_q);
}
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (!ret)
+ /* Driver doesn't want device back. */
+ ccw_device_do_unreg_rereg(work);
}
/*
@@ -534,15 +543,21 @@ ccw_device_nopath_notify(struct work_struct *work)
struct ccw_device *cdev;
struct subchannel *sch;
int ret;
+ unsigned long flags;
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
+ spin_lock_irqsave(cdev->ccwlock, flags);
sch = to_subchannel(cdev->dev.parent);
/* Extra sanity. */
if (sch->lpm)
- return;
- ret = (sch->driver && sch->driver->notify) ?
- sch->driver->notify(&sch->dev, CIO_NO_PATH) : 0;
+ goto out_unlock;
+ if (sch->driver && sch->driver->notify) {
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ ret = sch->driver->notify(&sch->dev, CIO_NO_PATH);
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ } else
+ ret = 0;
if (!ret) {
if (get_device(&sch->dev)) {
/* Driver doesn't want to keep device. */
@@ -562,6 +577,8 @@ ccw_device_nopath_notify(struct work_struct *work)
cdev->private->state = DEV_STATE_DISCONNECTED;
wake_up(&cdev->private->wait_q);
}
+out_unlock:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
}
void
@@ -607,10 +624,13 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
default:
/* Reset oper notify indication after verify error. */
cdev->private->flags.donotify = 0;
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_nopath_notify);
- queue_work(ccw_device_notify_work, &cdev->private->kick_work);
- ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ if (cdev->online) {
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_nopath_notify);
+ queue_work(ccw_device_notify_work,
+ &cdev->private->kick_work);
+ } else
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break;
}
}
@@ -756,15 +776,22 @@ static void
ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
+ int ret;
sch = to_subchannel(cdev->dev.parent);
- if (sch->driver->notify &&
- sch->driver->notify(&sch->dev, sch->lpm ? CIO_GONE : CIO_NO_PATH)) {
- ccw_device_set_timeout(cdev, 0);
- cdev->private->flags.fake_irb = 0;
- cdev->private->state = DEV_STATE_DISCONNECTED;
- wake_up(&cdev->private->wait_q);
- return;
+ if (sch->driver->notify) {
+ spin_unlock_irq(cdev->ccwlock);
+ ret = sch->driver->notify(&sch->dev,
+ sch->lpm ? CIO_GONE : CIO_NO_PATH);
+ spin_lock_irq(cdev->ccwlock);
+ } else
+ ret = 0;
+ if (ret) {
+ ccw_device_set_timeout(cdev, 0);
+ cdev->private->flags.fake_irb = 0;
+ cdev->private->state = DEV_STATE_DISCONNECTED;
+ wake_up(&cdev->private->wait_q);
+ return;
}
cdev->private->state = DEV_STATE_NOT_OPER;
cio_disable_subchannel(sch);