Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+47 −20
Original line number Diff line number Diff line
@@ -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,9 +624,12 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
	default:
		/* Reset oper notify indication after verify error. */
		cdev->private->flags.donotify = 0;
		if (cdev->online) {
			PREPARE_WORK(&cdev->private->kick_work,
				     ccw_device_nopath_notify);
		queue_work(ccw_device_notify_work, &cdev->private->kick_work);
			queue_work(ccw_device_notify_work,
				   &cdev->private->kick_work);
		} else
			ccw_device_done(cdev, DEV_STATE_NOT_OPER);
		break;
	}
@@ -756,10 +776,17 @@ 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)) {
	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;