[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>
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 8047800..9bdb463 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -114,11 +114,13 @@
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 @@
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_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 @@
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
char dbf_txt[15];
- int ccode;
int retry;
int ret;
@@ -374,8 +393,7 @@
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 @@
*/
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 @@
int cio_disable_subchannel(struct subchannel *sch)
{
char dbf_txt[15];
- int ccode;
int retry;
int ret;
@@ -424,8 +444,7 @@
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 @@
*/
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 @@
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;
}