[S390] cio: Retry internal operations after vary off.
authorCornelia Huck <cornelia.huck@de.ibm.com>
Mon, 4 Dec 2006 14:41:04 +0000 (15:41 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 4 Dec 2006 14:41:04 +0000 (15:41 +0100)
If I/O was running on a just varied off chpid, it will be terminated.
If this was a common I/O layer internal I/O, it needs to be retried.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/chsc.c
drivers/s390/cio/css.h
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_id.c
drivers/s390/cio/device_pgid.c
drivers/s390/cio/device_status.c

index 11900de..dbfb77b 100644 (file)
@@ -251,6 +251,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
                cc = cio_clear(sch);
                if (cc == -ENODEV)
                        goto out_unreg;
+               /* Request retry of internal operation. */
+               device_set_intretry(sch);
                /* Call handler. */
                if (sch->driver && sch->driver->termination)
                        sch->driver->termination(&sch->dev);
@@ -711,9 +713,6 @@ static inline int check_for_io_on_path(struct subchannel *sch, int index)
 {
        int cc;
 
-       if (!device_is_online(sch))
-               /* cio could be doing I/O. */
-               return 0;
        cc = stsch(sch->schid, &sch->schib);
        if (cc)
                return 0;
@@ -722,6 +721,26 @@ static inline int check_for_io_on_path(struct subchannel *sch, int index)
        return 0;
 }
 
+static void terminate_internal_io(struct subchannel *sch)
+{
+       if (cio_clear(sch)) {
+               /* Recheck device in case clear failed. */
+               sch->lpm = 0;
+               if (device_trigger_verify(sch) != 0) {
+                       if(css_enqueue_subchannel_slow(sch->schid)) {
+                               css_clear_subchannel_slow_list();
+                               need_rescan = 1;
+                       }
+               }
+               return;
+       }
+       /* Request retry of internal operation. */
+       device_set_intretry(sch);
+       /* Call handler. */
+       if (sch->driver && sch->driver->termination)
+               sch->driver->termination(&sch->dev);
+}
+
 static inline void
 __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
 {
@@ -748,10 +767,14 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
                }
                sch->opm &= ~(0x80 >> chp);
                sch->lpm &= ~(0x80 >> chp);
-               if (check_for_io_on_path(sch, chp))
-                       /* Path verification is done after killing. */
-                       device_kill_io(sch);
-               else if (!sch->lpm) {
+               if (check_for_io_on_path(sch, chp)) {
+                       if (device_is_online(sch))
+                               /* Path verification is done after killing. */
+                               device_kill_io(sch);
+                       else
+                               /* Kill and retry internal I/O. */
+                               terminate_internal_io(sch);
+               } else if (!sch->lpm) {
                        if (device_trigger_verify(sch) != 0) {
                                if (css_enqueue_subchannel_slow(sch->schid)) {
                                        css_clear_subchannel_slow_list();
index da73453..9ff064e 100644 (file)
@@ -94,6 +94,7 @@ struct ccw_device_private {
                unsigned int donotify:1;    /* call notify function */
                unsigned int recog_done:1;  /* dev. recog. complete */
                unsigned int fake_irb:1;    /* deliver faked irb */
+               unsigned int intretry:1;    /* retry internal operation */
        } __attribute__((packed)) flags;
        unsigned long intparm;  /* user interruption parameter */
        struct qdio_irq *qdio_data;
@@ -171,6 +172,7 @@ void device_trigger_reprobe(struct subchannel *);
 /* Helper functions for vary on/off. */
 int device_is_online(struct subchannel *);
 void device_kill_io(struct subchannel *);
+void device_set_intretry(struct subchannel *sch);
 int device_trigger_verify(struct subchannel *sch);
 
 /* Machine check helper function. */
index 39c98f9..359b46c 100644 (file)
@@ -948,6 +948,9 @@ io_subchannel_ioterm(struct device *dev)
        cdev = dev->driver_data;
        if (!cdev)
                return;
+       /* Internal I/O will be retried by the interrupt handler. */
+       if (cdev->private->flags.intretry)
+               return;
        cdev->private->state = DEV_STATE_CLEAR_VERIFY;
        if (cdev->handler)
                cdev->handler(cdev, cdev->private->intparm,
index 7665000..09c7672 100644 (file)
@@ -59,6 +59,16 @@ device_set_disconnected(struct subchannel *sch)
        cdev->private->state = DEV_STATE_DISCONNECTED;
 }
 
+void device_set_intretry(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+
+       cdev = sch->dev.driver_data;
+       if (!cdev)
+               return;
+       cdev->private->flags.intretry = 1;
+}
+
 int device_trigger_verify(struct subchannel *sch)
 {
        struct ccw_device *cdev;
@@ -904,6 +914,12 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
         * had killed the original request.
         */
        if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+               /* Retry Basic Sense if requested. */
+               if (cdev->private->flags.intretry) {
+                       cdev->private->flags.intretry = 0;
+                       ccw_device_do_sense(cdev, irb);
+                       return;
+               }
                cdev->private->flags.dosense = 0;
                memset(&cdev->private->irb, 0, sizeof(struct irb));
                ccw_device_accumulate_irb(cdev, irb);
index a74785b..f172759 100644 (file)
@@ -191,6 +191,8 @@ __ccw_device_sense_id_start(struct ccw_device *cdev)
                if ((sch->opm & cdev->private->imask) != 0 &&
                    cdev->private->iretry > 0) {
                        cdev->private->iretry--;
+                       /* Reset internal retry indication. */
+                       cdev->private->flags.intretry = 0;
                        ret = cio_start (sch, cdev->private->iccws,
                                         cdev->private->imask);
                        /* ret is 0, -EBUSY, -EACCES or -ENODEV */
@@ -237,8 +239,14 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
                return 0; /* Success */
        }
        /* Check the error cases. */
-       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+               /* Retry Sense ID if requested. */
+               if (cdev->private->flags.intretry) {
+                       cdev->private->flags.intretry = 0;
+                       return -EAGAIN;
+               }
                return -ETIME;
+       }
        if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
                /*
                 * if the device doesn't support the SenseID
index 2975ce8..cb1879a 100644 (file)
@@ -71,6 +71,8 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
                ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
                if (cdev->private->iretry > 0) {
                        cdev->private->iretry--;
+                       /* Reset internal retry indication. */
+                       cdev->private->flags.intretry = 0;
                        ret = cio_start (sch, cdev->private->iccws, 
                                         cdev->private->imask);
                        /* ret is 0, -EBUSY, -EACCES or -ENODEV */
@@ -122,8 +124,14 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
 
        sch = to_subchannel(cdev->dev.parent);
        irb = &cdev->private->irb;
-       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+               /* Retry Sense PGID if requested. */
+               if (cdev->private->flags.intretry) {
+                       cdev->private->flags.intretry = 0;
+                       return -EAGAIN;
+               }
                return -ETIME;
+       }
        if (irb->esw.esw0.erw.cons &&
            (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
                /*
@@ -253,6 +261,8 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
        ret = -EACCES;
        if (cdev->private->iretry > 0) {
                cdev->private->iretry--;
+               /* Reset internal retry indication. */
+               cdev->private->flags.intretry = 0;
                ret = cio_start (sch, cdev->private->iccws,
                                 cdev->private->imask);
                /* We expect an interrupt in case of success or busy
@@ -293,6 +303,8 @@ static int __ccw_device_do_nop(struct ccw_device *cdev)
        ret = -EACCES;
        if (cdev->private->iretry > 0) {
                cdev->private->iretry--;
+               /* Reset internal retry indication. */
+               cdev->private->flags.intretry = 0;
                ret = cio_start (sch, cdev->private->iccws,
                                 cdev->private->imask);
                /* We expect an interrupt in case of success or busy
@@ -321,8 +333,14 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
 
        sch = to_subchannel(cdev->dev.parent);
        irb = &cdev->private->irb;
-       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+               /* Retry Set PGID if requested. */
+               if (cdev->private->flags.intretry) {
+                       cdev->private->flags.intretry = 0;
+                       return -EAGAIN;
+               }
                return -ETIME;
+       }
        if (irb->esw.esw0.erw.cons) {
                if (irb->ecw[0] & SNS0_CMD_REJECT)
                        return -EOPNOTSUPP;
@@ -360,8 +378,14 @@ static int __ccw_device_check_nop(struct ccw_device *cdev)
 
        sch = to_subchannel(cdev->dev.parent);
        irb = &cdev->private->irb;
-       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+               /* Retry NOP if requested. */
+               if (cdev->private->flags.intretry) {
+                       cdev->private->flags.intretry = 0;
+                       return -EAGAIN;
+               }
                return -ETIME;
+       }
        if (irb->scsw.cc == 3) {
                CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
                              " lpm %02X, became 'not operational'\n",
index 3f7cbce..bdcf930 100644 (file)
@@ -319,6 +319,9 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
        sch->sense_ccw.count = SENSE_MAX_COUNT;
        sch->sense_ccw.flags = CCW_FLAG_SLI;
 
+       /* Reset internal retry indication. */
+       cdev->private->flags.intretry = 0;
+
        return cio_start (sch, &sch->sense_ccw, 0xff);
 }