Commit ee04bbcc authored by Cornelia Huck's avatar Cornelia Huck Committed by Martin Schwidefsky

[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: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 482b05dd
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment