From: Cornelia Huck <cohuck@de.ibm.com>

Common i/o layer changes:
 - If a device driver tries to start I/O while the common I/O layer wants
   to start path verification, don't reject the I/O with -EBUSY (which
   might prompt the driver to retry the next tick) but seemingly accept
   the I/O and deliver a fake irb with deferred cc 1 after path
   verification has finished (prompting the driver to retry the I/O).
   This prevents the device driver from doing useless retries while cio
   is still busy with path verification.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/s390/cio/chsc.c       |    3 +++
 25-akpm/drivers/s390/cio/css.h        |    2 ++
 25-akpm/drivers/s390/cio/device_fsm.c |   29 +++++++++++++++++++++++++++++
 25-akpm/drivers/s390/cio/device_ops.c |   10 ++++++++++
 4 files changed, 44 insertions(+)

diff -puN drivers/s390/cio/chsc.c~s390-irb-faking drivers/s390/cio/chsc.c
--- 25/drivers/s390/cio/chsc.c~s390-irb-faking	2005-03-02 17:55:23.000000000 -0800
+++ 25-akpm/drivers/s390/cio/chsc.c	2005-03-02 17:55:23.000000000 -0800
@@ -703,6 +703,9 @@ __check_for_io_and_kill(struct subchanne
 {
 	int cc;
 
+	if (!device_is_online(sch))
+		/* cio could be doing I/O. */
+		return 0;
 	cc = stsch(sch->irq, &sch->schib);
 	if (cc)
 		return 0;
diff -puN drivers/s390/cio/css.h~s390-irb-faking drivers/s390/cio/css.h
--- 25/drivers/s390/cio/css.h~s390-irb-faking	2005-03-02 17:55:23.000000000 -0800
+++ 25-akpm/drivers/s390/cio/css.h	2005-03-02 17:55:23.000000000 -0800
@@ -84,6 +84,7 @@ struct ccw_device_private {
 		unsigned int doverify:1;    /* delayed path verification */
 		unsigned int donotify:1;    /* call notify function */
 		unsigned int recog_done:1;  /* dev. recog. complete */
+		unsigned int fake_irb:1;    /* deliver faked irb */
 	} __attribute__((packed)) flags;
 	unsigned long intparm;	/* user interruption parameter */
 	struct qdio_irq *qdio_data;
@@ -136,6 +137,7 @@ void device_set_disconnected(struct subc
 void device_trigger_reprobe(struct subchannel *);
 
 /* Helper functions for vary on/off. */
+int device_is_online(struct subchannel *);
 void device_set_waiting(struct subchannel *);
 
 /* Machine check helper function. */
diff -puN drivers/s390/cio/device_fsm.c~s390-irb-faking drivers/s390/cio/device_fsm.c
--- 25/drivers/s390/cio/device_fsm.c~s390-irb-faking	2005-03-02 17:55:23.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device_fsm.c	2005-03-02 17:55:23.000000000 -0800
@@ -24,6 +24,17 @@
 #include "qdio.h"
 
 int
+device_is_online(struct subchannel *sch)
+{
+	struct ccw_device *cdev;
+
+	if (!sch->dev.driver_data)
+		return 0;
+	cdev = sch->dev.driver_data;
+	return (cdev->private->state == DEV_STATE_ONLINE);
+}
+
+int
 device_is_disconnected(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
@@ -44,6 +55,7 @@ device_set_disconnected(struct subchanne
 		return;
 	cdev = sch->dev.driver_data;
 	ccw_device_set_timeout(cdev, 0);
+	cdev->private->flags.fake_irb = 0;
 	cdev->private->state = DEV_STATE_DISCONNECTED;
 }
 
@@ -474,6 +486,7 @@ ccw_device_nopath_notify(void *data)
 	} else {
 		cio_disable_subchannel(sch);
 		ccw_device_set_timeout(cdev, 0);
+		cdev->private->flags.fake_irb = 0;
 		cdev->private->state = DEV_STATE_DISCONNECTED;
 		wake_up(&cdev->private->wait_q);
 	}
@@ -488,6 +501,21 @@ ccw_device_verify_done(struct ccw_device
 		cdev->private->options.pgroup = 0;
 	case 0:
 		ccw_device_done(cdev, DEV_STATE_ONLINE);
+		/* Deliver fake irb to device driver, if needed. */
+		if (cdev->private->flags.fake_irb) {
+			memset(&cdev->private->irb, 0, sizeof(struct irb));
+			cdev->private->irb.scsw = (struct scsw) {
+				.cc = 1,
+				.fctl = SCSW_FCTL_START_FUNC,
+				.actl = SCSW_ACTL_START_PEND,
+				.stctl = SCSW_STCTL_STATUS_PEND,
+			};
+			cdev->private->flags.fake_irb = 0;
+			if (cdev->handler)
+				cdev->handler(cdev, cdev->private->intparm,
+					      &cdev->private->irb);
+			memset(&cdev->private->irb, 0, sizeof(struct irb));
+		}
 		break;
 	case -ETIME:
 		ccw_device_done(cdev, DEV_STATE_BOXED);
@@ -639,6 +667,7 @@ ccw_device_online_notoper(struct ccw_dev
 	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;
diff -puN drivers/s390/cio/device_ops.c~s390-irb-faking drivers/s390/cio/device_ops.c
--- 25/drivers/s390/cio/device_ops.c~s390-irb-faking	2005-03-02 17:55:23.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device_ops.c	2005-03-02 17:55:23.000000000 -0800
@@ -81,6 +81,16 @@ ccw_device_start_key(struct ccw_device *
 		return -ENODEV;
 	if (cdev->private->state == DEV_STATE_NOT_OPER)
 		return -ENODEV;
+	if (cdev->private->state == DEV_STATE_VERIFY) {
+		/* Remember to fake irb when finished. */
+		if (!cdev->private->flags.fake_irb) {
+			cdev->private->flags.fake_irb = 1;
+			cdev->private->intparm = intparm;
+			return 0;
+		} else
+			/* There's already a fake I/O around. */
+			return -EBUSY;
+	}
 	if (cdev->private->state != DEV_STATE_ONLINE ||
 	    ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
 	     !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
_