forked from Minki/linux
[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>
This commit is contained in:
parent
482b05dd53
commit
ee04bbccde
@ -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 (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);
|
||||
else {
|
||||
/* Reenable channel measurements, if needed. */
|
||||
cmf_reenable(cdev);
|
||||
wake_up(&cdev->private->wait_q);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user