[S390] cio: always query all paths on path verification.
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Wed, 20 Sep 2006 13:59:59 +0000 (15:59 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 20 Sep 2006 13:59:59 +0000 (15:59 +0200)
Reappearing channel paths are sometimes not utilized by CCW devices
because path verification incorrectly relies on path-operational-mask
information which is not updated until a channel path has been used
again.
Modify path verification procedure to always query all available paths
to a device.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/chsc.c
drivers/s390/cio/cio.c
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_ops.c
drivers/s390/cio/device_pgid.c

index 9f9134b..3bb4e47 100644 (file)
@@ -256,7 +256,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
        /* trigger path verification. */
        if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
-       else if (sch->vpm == mask)
+       else if (sch->lpm == mask)
                goto out_unreg;
 out_unlock:
        spin_unlock_irq(&sch->lock);
index 54cce54..2e2882d 100644 (file)
@@ -569,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
        sch->opm = 0xff;
        if (!cio_is_console(sch->schid))
                chsc_validate_chpids(sch);
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
+       sch->lpm = sch->schib.pmcw.pam & sch->opm;
 
        CIO_DEBUG(KERN_INFO, 0,
                  "Detected device %04x on subchannel 0.%x.%04X"
index 7756f32..dace46f 100644 (file)
@@ -232,10 +232,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
         */
        old_lpm = sch->lpm;
        stsch(sch->schid, &sch->schib);
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
+       sch->lpm = sch->schib.pmcw.pam & sch->opm;
        /* Check since device may again have become not operational. */
        if (!sch->schib.pmcw.dnv)
                state = DEV_STATE_NOT_OPER;
@@ -455,8 +452,8 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
                return;
        }
        /* Start Path Group verification. */
-       sch->vpm = 0;   /* Start with no path groups set. */
        cdev->private->state = DEV_STATE_VERIFY;
+       cdev->private->flags.doverify = 0;
        ccw_device_verify_start(cdev);
 }
 
@@ -556,7 +553,19 @@ ccw_device_nopath_notify(void *data)
 void
 ccw_device_verify_done(struct ccw_device *cdev, int err)
 {
-       cdev->private->flags.doverify = 0;
+       struct subchannel *sch;
+
+       sch = to_subchannel(cdev->dev.parent);
+       /* Update schib - pom may have changed. */
+       stsch(sch->schid, &sch->schib);
+       /* Update lpm with verified path mask. */
+       sch->lpm = sch->vpm;
+       /* Repeat path verification? */
+       if (cdev->private->flags.doverify) {
+               cdev->private->flags.doverify = 0;
+               ccw_device_verify_start(cdev);
+               return;
+       }
        switch (err) {
        case -EOPNOTSUPP: /* path grouping not supported, just set online. */
                cdev->private->options.pgroup = 0;
@@ -614,6 +623,7 @@ ccw_device_online(struct ccw_device *cdev)
        if (!cdev->private->options.pgroup) {
                /* Start initial path verification. */
                cdev->private->state = DEV_STATE_VERIFY;
+               cdev->private->flags.doverify = 0;
                ccw_device_verify_start(cdev);
                return 0;
        }
@@ -660,7 +670,6 @@ ccw_device_offline(struct ccw_device *cdev)
        /* Are we doing path grouping? */
        if (!cdev->private->options.pgroup) {
                /* No, set state offline immediately. */
-               sch->vpm = 0;
                ccw_device_done(cdev, DEV_STATE_OFFLINE);
                return 0;
        }
@@ -781,6 +790,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
        }
        /* Device is idle, we can do the path verification. */
        cdev->private->state = DEV_STATE_VERIFY;
+       cdev->private->flags.doverify = 0;
        ccw_device_verify_start(cdev);
 }
 
@@ -1043,9 +1053,9 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
 }
 
 static void
-ccw_device_wait4io_verify(struct ccw_device *cdev, enum dev_event dev_event)
+ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
 {
-       /* When the I/O has terminated, we have to start verification. */
+       /* Start verification after current task finished. */
        cdev->private->flags.doverify = 1;
 }
 
@@ -1111,10 +1121,7 @@ device_trigger_reprobe(struct subchannel *sch)
         * The pim, pam, pom values may not be accurate, but they are the best
         * we have before performing device selection :/
         */
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
+       sch->lpm = sch->schib.pmcw.pam & sch->opm;
        /* Re-set some bits in the pmcw that were lost. */
        sch->schib.pmcw.isc = 3;
        sch->schib.pmcw.csense = 1;
@@ -1238,7 +1245,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_online_notoper,
                [DEV_EVENT_INTERRUPT]   = ccw_device_verify_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_delay_verify,
        },
        [DEV_STATE_ONLINE] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_online_notoper,
@@ -1281,7 +1288,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_online_notoper,
                [DEV_EVENT_INTERRUPT]   = ccw_device_wait4io_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_wait4io_timeout,
-               [DEV_EVENT_VERIFY]      = ccw_device_wait4io_verify,
+               [DEV_EVENT_VERIFY]      = ccw_device_delay_verify,
        },
        [DEV_STATE_QUIESCE] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_quiesce_done,
@@ -1294,7 +1301,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_nop,
                [DEV_EVENT_INTERRUPT]   = ccw_device_start_id,
                [DEV_EVENT_TIMEOUT]     = ccw_device_bug,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_start_id,
        },
        [DEV_STATE_DISCONNECTED_SENSE_ID] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_recog_notoper,
index acad8f8..93a897e 100644 (file)
@@ -256,7 +256,7 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
        if (!sch)
                return 0;
        else
-               return sch->vpm;
+               return sch->lpm;
 }
 
 static void
index 1693a10..8ca2d07 100644 (file)
@@ -245,18 +245,17 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
        memset(&cdev->private->irb, 0, sizeof(struct irb));
 
        /* Try multiple times. */
-       ret = -ENODEV;
+       ret = -EACCES;
        if (cdev->private->iretry > 0) {
                cdev->private->iretry--;
                ret = cio_start (sch, cdev->private->iccws,
                                 cdev->private->imask);
-               /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-               if ((ret != -EACCES) && (ret != -ENODEV))
+               /* We expect an interrupt in case of success or busy
+                * indication. */
+               if ((ret == 0) || (ret == -EBUSY))
                        return ret;
        }
-       /* PGID command failed on this path. Switch it off. */
-       sch->lpm &= ~cdev->private->imask;
-       sch->vpm &= ~cdev->private->imask;
+       /* PGID command failed on this path. */
        CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
                      "0.%x.%04x, lpm %02X, became 'not operational'\n",
                      cdev->private->devno, sch->schid.ssid,
@@ -286,18 +285,17 @@ static int __ccw_device_do_nop(struct ccw_device *cdev)
        memset(&cdev->private->irb, 0, sizeof(struct irb));
 
        /* Try multiple times. */
-       ret = -ENODEV;
+       ret = -EACCES;
        if (cdev->private->iretry > 0) {
                cdev->private->iretry--;
                ret = cio_start (sch, cdev->private->iccws,
                                 cdev->private->imask);
-               /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-               if ((ret != -EACCES) && (ret != -ENODEV))
+               /* We expect an interrupt in case of success or busy
+                * indication. */
+               if ((ret == 0) || (ret == -EBUSY))
                        return ret;
        }
-       /* nop command failed on this path. Switch it off. */
-       sch->lpm &= ~cdev->private->imask;
-       sch->vpm &= ~cdev->private->imask;
+       /* nop command failed on this path. */
        CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
                      "0.%x.%04x, lpm %02X, became 'not operational'\n",
                      cdev->private->devno, sch->schid.ssid,
@@ -372,27 +370,32 @@ static void
 __ccw_device_verify_start(struct ccw_device *cdev)
 {
        struct subchannel *sch;
-       __u8 imask, func;
+       __u8 func;
        int ret;
 
        sch = to_subchannel(cdev->dev.parent);
-       while (sch->vpm != sch->lpm) {
-               /* Find first unequal bit in vpm vs. lpm */
-               for (imask = 0x80; imask != 0; imask >>= 1)
-                       if ((sch->vpm & imask) != (sch->lpm & imask))
-                               break;
-               cdev->private->imask = imask;
+       /* Repeat for all paths. */
+       for (; cdev->private->imask; cdev->private->imask >>= 1,
+                                    cdev->private->iretry = 5) {
+               if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
+                       /* Path not available, try next. */
+                       continue;
                if (cdev->private->options.pgroup) {
-                       func = (sch->vpm & imask) ?
-                               SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
+                       if (sch->opm & cdev->private->imask)
+                               func = SPID_FUNC_ESTABLISH;
+                       else
+                               func = SPID_FUNC_RESIGN;
                        ret = __ccw_device_do_pgid(cdev, func);
                } else
                        ret = __ccw_device_do_nop(cdev);
+               /* We expect an interrupt in case of success or busy
+                * indication. */
                if (ret == 0 || ret == -EBUSY)
                        return;
-               cdev->private->iretry = 5;
+               /* Permanent path failure, try next. */
        }
-       ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
+       /* Done with all paths. */
+       ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
 }
                
 /*
@@ -421,14 +424,14 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
        else
                ret = __ccw_device_check_nop(cdev);
        memset(&cdev->private->irb, 0, sizeof(struct irb));
+
        switch (ret) {
        /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
        case 0:
-               /* Establish or Resign Path Group done. Update vpm. */
-               if ((sch->lpm & cdev->private->imask) != 0)
-                       sch->vpm |= cdev->private->imask;
-               else
-                       sch->vpm &= ~cdev->private->imask;
+               /* Path verification ccw finished successfully, update lpm. */
+               sch->vpm |= sch->opm & cdev->private->imask;
+               /* Go on with next path. */
+               cdev->private->imask >>= 1;
                cdev->private->iretry = 5;
                __ccw_device_verify_start(cdev);
                break;
@@ -441,6 +444,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
                        cdev->private->options.pgroup = 0;
                else
                        cdev->private->flags.pgid_single = 1;
+               /* Retry */
+               sch->vpm = 0;
+               cdev->private->imask = 0x80;
+               cdev->private->iretry = 5;
                /* fall through. */
        case -EAGAIN:           /* Try again. */
                __ccw_device_verify_start(cdev);
@@ -449,8 +456,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
                ccw_device_verify_done(cdev, -ETIME);
                break;
        case -EACCES:           /* channel is not operational. */
-               sch->lpm &= ~cdev->private->imask;
-               sch->vpm &= ~cdev->private->imask;
+               cdev->private->imask >>= 1;
                cdev->private->iretry = 5;
                __ccw_device_verify_start(cdev);
                break;
@@ -463,19 +469,17 @@ ccw_device_verify_start(struct ccw_device *cdev)
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
 
        cdev->private->flags.pgid_single = 0;
+       cdev->private->imask = 0x80;
        cdev->private->iretry = 5;
-       /*
-        * Update sch->lpm with current values to catch paths becoming
-        * available again.
-        */
+
+       /* Start with empty vpm. */
+       sch->vpm = 0;
+
+       /* Get current pam. */
        if (stsch(sch->schid, &sch->schib)) {
                ccw_device_verify_done(cdev, -ENODEV);
                return;
        }
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
        __ccw_device_verify_start(cdev);
 }
 
@@ -524,7 +528,6 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
        switch (ret) {
        /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
        case 0:                 /* disband successful. */
-               sch->vpm = 0;
                ccw_device_disband_done(cdev, ret);
                break;
        case -EOPNOTSUPP: