瀏覽代碼

[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>
Cornelia Huck 18 年之前
父節點
當前提交
ee04bbccde
共有 1 個文件被更改,包括 47 次插入20 次删除
  1. 47 20
      drivers/s390/cio/device_fsm.c

+ 47 - 20
drivers/s390/cio/device_fsm.c

@@ -334,20 +334,29 @@ ccw_device_oper_notify(struct work_struct *work)
 	struct ccw_device *cdev;
 	struct ccw_device *cdev;
 	struct subchannel *sch;
 	struct subchannel *sch;
 	int ret;
 	int ret;
+	unsigned long flags;
 
 
 	priv = container_of(work, struct ccw_device_private, kick_work);
 	priv = container_of(work, struct ccw_device_private, kick_work);
 	cdev = priv->cdev;
 	cdev = priv->cdev;
+	spin_lock_irqsave(cdev->ccwlock, flags);
 	sch = to_subchannel(cdev->dev.parent);
 	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. */
 		/* Reenable channel measurements, if needed. */
+		spin_unlock_irqrestore(cdev->ccwlock, flags);
 		cmf_reenable(cdev);
 		cmf_reenable(cdev);
+		spin_lock_irqsave(cdev->ccwlock, flags);
 		wake_up(&cdev->private->wait_q);
 		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 ccw_device *cdev;
 	struct subchannel *sch;
 	struct subchannel *sch;
 	int ret;
 	int ret;
+	unsigned long flags;
 
 
 	priv = container_of(work, struct ccw_device_private, kick_work);
 	priv = container_of(work, struct ccw_device_private, kick_work);
 	cdev = priv->cdev;
 	cdev = priv->cdev;
+	spin_lock_irqsave(cdev->ccwlock, flags);
 	sch = to_subchannel(cdev->dev.parent);
 	sch = to_subchannel(cdev->dev.parent);
 	/* Extra sanity. */
 	/* Extra sanity. */
 	if (sch->lpm)
 	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 (!ret) {
 		if (get_device(&sch->dev)) {
 		if (get_device(&sch->dev)) {
 			/* Driver doesn't want to keep device. */
 			/* 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;
 		cdev->private->state = DEV_STATE_DISCONNECTED;
 		wake_up(&cdev->private->wait_q);
 		wake_up(&cdev->private->wait_q);
 	}
 	}
+out_unlock:
+	spin_unlock_irqrestore(cdev->ccwlock, flags);
 }
 }
 
 
 void
 void
@@ -607,10 +624,13 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
 	default:
 	default:
 		/* Reset oper notify indication after verify error. */
 		/* Reset oper notify indication after verify error. */
 		cdev->private->flags.donotify = 0;
 		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;
 		break;
 	}
 	}
 }
 }
@@ -756,15 +776,22 @@ static void
 ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
 ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
 {
 {
 	struct subchannel *sch;
 	struct subchannel *sch;
+	int ret;
 
 
 	sch = to_subchannel(cdev->dev.parent);
 	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;
 	cdev->private->state = DEV_STATE_NOT_OPER;
 	cio_disable_subchannel(sch);
 	cio_disable_subchannel(sch);