From: Cornelia Huck 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 Signed-off-by: Andrew Morton --- 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)) || _