[S390] cio: introduce cio_update_schib
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Thu, 25 Dec 2008 12:39:12 +0000 (13:39 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 25 Dec 2008 12:39:10 +0000 (13:39 +0100)
There is the chance that we get condition code 0 for a stsch but
the resulting schib is not vaild. In the current code there are
2 cases:
* we do a check for validity of the schib after stsch, but at this
  time we have already stored the invaild schib in the subchannel
  structure. This may lead to problems.
* we don't do a check for validity, which is not that good either.

The patch addresses both issues by introducing the stsch wrapper
cio_update_schib which performs stsch on a local schib. This schib
is only written back to the subchannel if it's valid.

side note: For some functions (chp_events) the return codes are
different now (-ENXIO vs -ENODEV) but this shouldn't do harm
since the caller doesn't check for _specific_ errors.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/chsc_sch.c
drivers/s390/cio/cio.c
drivers/s390/cio/cio.h
drivers/s390/cio/cmf.c
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_pgid.c
drivers/s390/cio/device_status.c

index f49f0e5..0a2f2ed 100644 (file)
@@ -61,7 +61,7 @@ static void chsc_subchannel_irq(struct subchannel *sch)
        }
        private->request = NULL;
        memcpy(&request->irb, irb, sizeof(*irb));
-       stsch(sch->schid, &sch->schib);
+       cio_update_schib(sch);
        complete(&request->completion);
        put_device(&sch->dev);
 }
index 8047800..9bdb463 100644 (file)
@@ -114,11 +114,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
        else
                sch->lpm = 0;
 
-       stsch (sch->schid, &sch->schib);
-
        CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
                      "subchannel 0.%x.%04x!\n", sch->schid.ssid,
                      sch->schid.sch_no);
+
+       if (cio_update_schib(sch))
+               return -ENODEV;
+
        sprintf(dbf_text, "no%s", dev_name(&sch->dev));
        CIO_TRACE_EVENT(0, dbf_text);
        CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -316,7 +318,8 @@ cio_cancel (struct subchannel *sch)
        switch (ccode) {
        case 0:         /* success */
                /* Update information in scsw. */
-               stsch (sch->schid, &sch->schib);
+               if (cio_update_schib(sch))
+                       return -ENODEV;
                return 0;
        case 1:         /* status pending */
                return -EBUSY;
@@ -358,6 +361,23 @@ cio_modify (struct subchannel *sch)
 }
 
 /**
+ * cio_update_schib - Perform stsch and update schib if subchannel is valid.
+ * @sch: subchannel on which to perform stsch
+ * Return zero on success, -ENODEV otherwise.
+ */
+int cio_update_schib(struct subchannel *sch)
+{
+       struct schib schib;
+
+       if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+               return -ENODEV;
+
+       memcpy(&sch->schib, &schib, sizeof(schib));
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cio_update_schib);
+
+/**
  * cio_enable_subchannel - enable a subchannel.
  * @sch: subchannel to be enabled
  * @intparm: interruption parameter to set
@@ -365,7 +385,6 @@ cio_modify (struct subchannel *sch)
 int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
 {
        char dbf_txt[15];
-       int ccode;
        int retry;
        int ret;
 
@@ -374,8 +393,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
 
        if (sch_is_pseudo_sch(sch))
                return -EINVAL;
-       ccode = stsch (sch->schid, &sch->schib);
-       if (ccode)
+       if (cio_update_schib(sch))
                return -ENODEV;
 
        for (retry = 5, ret = 0; retry > 0; retry--) {
@@ -392,7 +410,10 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
                         */
                        sch->schib.pmcw.csense = 0;
                if (ret == 0) {
-                       stsch (sch->schid, &sch->schib);
+                       if (cio_update_schib(sch)) {
+                               ret = -ENODEV;
+                               break;
+                       }
                        if (sch->schib.pmcw.ena)
                                break;
                }
@@ -415,7 +436,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
 int cio_disable_subchannel(struct subchannel *sch)
 {
        char dbf_txt[15];
-       int ccode;
        int retry;
        int ret;
 
@@ -424,8 +444,7 @@ int cio_disable_subchannel(struct subchannel *sch)
 
        if (sch_is_pseudo_sch(sch))
                return 0;
-       ccode = stsch (sch->schid, &sch->schib);
-       if (ccode == 3)         /* Not operational. */
+       if (cio_update_schib(sch))
                return -ENODEV;
 
        if (scsw_actl(&sch->schib.scsw) != 0)
@@ -448,7 +467,10 @@ int cio_disable_subchannel(struct subchannel *sch)
                         */
                        break;
                if (ret == 0) {
-                       stsch (sch->schid, &sch->schib);
+                       if (cio_update_schib(sch)) {
+                               ret = -ENODEV;
+                               break;
+                       }
                        if (!sch->schib.pmcw.ena)
                                break;
                }
@@ -851,7 +873,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
                cc = msch(schid, schib);
                if (cc)
                        return (cc==3?-ENODEV:-EBUSY);
-               stsch(schid, schib);
+               if (stsch(schid, schib) || !css_sch_is_valid(schib))
+                       return -ENODEV;
                if (!schib->pmcw.ena)
                        return 0;
        }
index 5db887e..fb125ef 100644 (file)
@@ -102,6 +102,7 @@ extern int cio_cancel (struct subchannel *);
 extern int cio_set_options (struct subchannel *, int);
 extern int cio_get_options (struct subchannel *);
 extern int cio_modify (struct subchannel *);
+extern int cio_update_schib(struct subchannel *sch);
 
 int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
 int cio_tm_intrg(struct subchannel *sch);
index a90b28c..288482b 100644 (file)
@@ -195,7 +195,8 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
        /* msch can silently fail, so do it again if necessary */
        for (retry = 0; retry < 3; retry++) {
                /* prepare schib */
-               stsch(sch->schid, schib);
+               if (cio_update_schib(sch))
+                       return -ENODEV;
                schib->pmcw.mme  = mme;
                schib->pmcw.mbfc = mbfc;
                /* address can be either a block address or a block index */
@@ -219,7 +220,8 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
                                ret = -EINVAL;
                                break;
                }
-               stsch(sch->schid, schib); /* restore the schib */
+               if (cio_update_schib(sch))
+                       return -ENODEV;
 
                if (ret)
                        break;
@@ -338,7 +340,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
 
        sch = to_subchannel(cdev->dev.parent);
 
-       if (stsch(sch->schid, &sch->schib))
+       if (cio_update_schib(sch))
                return -ENODEV;
 
        if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
index 868aa19..51e9421 100644 (file)
@@ -1350,10 +1350,7 @@ static void io_subchannel_verify(struct subchannel *sch)
 
 static int check_for_io_on_path(struct subchannel *sch, int mask)
 {
-       int cc;
-
-       cc = stsch(sch->schid, &sch->schib);
-       if (cc)
+       if (cio_update_schib(sch))
                return 0;
        if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
                return 1;
@@ -1422,15 +1419,13 @@ static int io_subchannel_chp_event(struct subchannel *sch,
                io_subchannel_verify(sch);
                break;
        case CHP_OFFLINE:
-               if (stsch(sch->schid, &sch->schib))
-                       return -ENXIO;
-               if (!css_sch_is_valid(&sch->schib))
+               if (cio_update_schib(sch))
                        return -ENODEV;
                io_subchannel_terminate_path(sch, mask);
                break;
        case CHP_ONLINE:
-               if (stsch(sch->schid, &sch->schib))
-                       return -ENXIO;
+               if (cio_update_schib(sch))
+                       return -ENODEV;
                sch->lpm |= mask & sch->opm;
                io_subchannel_verify(sch);
                break;
index 01330cf..e1a3786 100644 (file)
@@ -140,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
        int ret;
 
        sch = to_subchannel(cdev->dev.parent);
-       ret = stsch(sch->schid, &sch->schib);
-       if (ret || !sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                return -ENODEV; 
        if (!sch->schib.pmcw.ena)
                /* Not operational -> done. */
@@ -245,11 +244,13 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
         * through ssch() and the path information is up to date.
         */
        old_lpm = sch->lpm;
-       stsch(sch->schid, &sch->schib);
-       sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
        /* Check since device may again have become not operational. */
-       if (!sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                state = DEV_STATE_NOT_OPER;
+       else
+               sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
        if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
                /* Force reprobe on all chpids. */
                old_lpm = 0;
@@ -549,7 +550,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
 
        sch = to_subchannel(cdev->dev.parent);
        /* Update schib - pom may have changed. */
-       stsch(sch->schid, &sch->schib);
+       if (cio_update_schib(sch)) {
+               cdev->private->flags.donotify = 0;
+               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+               return;
+       }
        /* Update lpm with verified path mask. */
        sch->lpm = sch->vpm;
        /* Repeat path verification? */
@@ -667,7 +672,7 @@ ccw_device_offline(struct ccw_device *cdev)
                return 0;
        }
        sch = to_subchannel(cdev->dev.parent);
-       if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                return -ENODEV;
        if (scsw_actl(&sch->schib.scsw) != 0)
                return -EBUSY;
@@ -745,7 +750,10 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
         * Since we might not just be coming from an interrupt from the
         * subchannel we have to update the schib.
         */
-       stsch(sch->schid, &sch->schib);
+       if (cio_update_schib(sch)) {
+               ccw_device_verify_done(cdev, -ENODEV);
+               return;
+       }
 
        if (scsw_actl(&sch->schib.scsw) != 0 ||
            (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
@@ -1011,9 +1019,7 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
 
        sch = to_subchannel(cdev->dev.parent);
        /* Update some values. */
-       if (stsch(sch->schid, &sch->schib))
-               return;
-       if (!sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                return;
        /*
         * The pim, pam, pom values may not be accurate, but they are the best
index 86bc94e..fc5ca1d 100644 (file)
@@ -504,7 +504,7 @@ ccw_device_verify_start(struct ccw_device *cdev)
        sch->vpm = 0;
 
        /* Get current pam. */
-       if (stsch(sch->schid, &sch->schib)) {
+       if (cio_update_schib(sch)) {
                ccw_device_verify_done(cdev, -ENODEV);
                return;
        }
index 1b03c54..5814dbe 100644 (file)
@@ -56,7 +56,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
-       stsch (sch->schid, &sch->schib);
+       if (cio_update_schib(sch))
+               goto doverify;
 
        CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
                      "not operational \n", __func__,
@@ -64,6 +65,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
                      sch->schib.pmcw.pnom);
 
        sch->lpm &= ~sch->schib.pmcw.pnom;
+doverify:
        cdev->private->flags.doverify = 1;
 }