diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2008-12-25 15:39:12 +0300 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 15:39:10 +0300 |
commit | cdb912a40df8b8507ab60b3d52f9980c0ba1f44d (patch) | |
tree | 52d693b3515b71b4f84b539d41571facb75498db /drivers/s390/cio | |
parent | d6a30761d8116b8de8a5b5e79e68cce81d36414b (diff) | |
download | linux-cdb912a40df8b8507ab60b3d52f9980c0ba1f44d.tar.xz |
[S390] cio: introduce cio_update_schib
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>
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/chsc_sch.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 47 | ||||
-rw-r--r-- | drivers/s390/cio/cio.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/cmf.c | 8 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 13 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 28 | ||||
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/device_status.c | 4 |
8 files changed, 67 insertions, 38 deletions
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index f49f0e502b8d..0a2f2edafc03 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -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); } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 8047800e9a0c..9bdb463765c7 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -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; } diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 5db887e8f032..fb125efd6891 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -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); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index a90b28c0be57..288482b21048 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -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) { diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 868aa1915380..51e94212bf66 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -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; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 01330cf5ae5e..e1a3786779ba 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -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 diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 86bc94eb607f..fc5ca1dd52b3 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -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; } diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 1b03c5423be2..5814dbee2410 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -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; } |