Commit cdb912a4 authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

[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: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent d6a30761
...@@ -61,7 +61,7 @@ static void chsc_subchannel_irq(struct subchannel *sch) ...@@ -61,7 +61,7 @@ static void chsc_subchannel_irq(struct subchannel *sch)
} }
private->request = NULL; private->request = NULL;
memcpy(&request->irb, irb, sizeof(*irb)); memcpy(&request->irb, irb, sizeof(*irb));
stsch(sch->schid, &sch->schib); cio_update_schib(sch);
complete(&request->completion); complete(&request->completion);
put_device(&sch->dev); put_device(&sch->dev);
} }
......
...@@ -114,11 +114,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) ...@@ -114,11 +114,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
else else
sch->lpm = 0; sch->lpm = 0;
stsch (sch->schid, &sch->schib);
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for " CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid, "subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no); sch->schid.sch_no);
if (cio_update_schib(sch))
return -ENODEV;
sprintf(dbf_text, "no%s", dev_name(&sch->dev)); sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text); CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
...@@ -316,7 +318,8 @@ cio_cancel (struct subchannel *sch) ...@@ -316,7 +318,8 @@ cio_cancel (struct subchannel *sch)
switch (ccode) { switch (ccode) {
case 0: /* success */ case 0: /* success */
/* Update information in scsw. */ /* Update information in scsw. */
stsch (sch->schid, &sch->schib); if (cio_update_schib(sch))
return -ENODEV;
return 0; return 0;
case 1: /* status pending */ case 1: /* status pending */
return -EBUSY; return -EBUSY;
...@@ -357,6 +360,23 @@ cio_modify (struct subchannel *sch) ...@@ -357,6 +360,23 @@ cio_modify (struct subchannel *sch)
return ret; return ret;
} }
/**
* 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. * cio_enable_subchannel - enable a subchannel.
* @sch: subchannel to be enabled * @sch: subchannel to be enabled
...@@ -365,7 +385,6 @@ cio_modify (struct subchannel *sch) ...@@ -365,7 +385,6 @@ cio_modify (struct subchannel *sch)
int cio_enable_subchannel(struct subchannel *sch, u32 intparm) int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{ {
char dbf_txt[15]; char dbf_txt[15];
int ccode;
int retry; int retry;
int ret; int ret;
...@@ -374,8 +393,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) ...@@ -374,8 +393,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
if (sch_is_pseudo_sch(sch)) if (sch_is_pseudo_sch(sch))
return -EINVAL; return -EINVAL;
ccode = stsch (sch->schid, &sch->schib); if (cio_update_schib(sch))
if (ccode)
return -ENODEV; return -ENODEV;
for (retry = 5, ret = 0; retry > 0; retry--) { for (retry = 5, ret = 0; retry > 0; retry--) {
...@@ -392,7 +410,10 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) ...@@ -392,7 +410,10 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
*/ */
sch->schib.pmcw.csense = 0; sch->schib.pmcw.csense = 0;
if (ret == 0) { if (ret == 0) {
stsch (sch->schid, &sch->schib); if (cio_update_schib(sch)) {
ret = -ENODEV;
break;
}
if (sch->schib.pmcw.ena) if (sch->schib.pmcw.ena)
break; break;
} }
...@@ -415,7 +436,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel); ...@@ -415,7 +436,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
int cio_disable_subchannel(struct subchannel *sch) int cio_disable_subchannel(struct subchannel *sch)
{ {
char dbf_txt[15]; char dbf_txt[15];
int ccode;
int retry; int retry;
int ret; int ret;
...@@ -424,8 +444,7 @@ int cio_disable_subchannel(struct subchannel *sch) ...@@ -424,8 +444,7 @@ int cio_disable_subchannel(struct subchannel *sch)
if (sch_is_pseudo_sch(sch)) if (sch_is_pseudo_sch(sch))
return 0; return 0;
ccode = stsch (sch->schid, &sch->schib); if (cio_update_schib(sch))
if (ccode == 3) /* Not operational. */
return -ENODEV; return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0) if (scsw_actl(&sch->schib.scsw) != 0)
...@@ -448,7 +467,10 @@ int cio_disable_subchannel(struct subchannel *sch) ...@@ -448,7 +467,10 @@ int cio_disable_subchannel(struct subchannel *sch)
*/ */
break; break;
if (ret == 0) { if (ret == 0) {
stsch (sch->schid, &sch->schib); if (cio_update_schib(sch)) {
ret = -ENODEV;
break;
}
if (!sch->schib.pmcw.ena) if (!sch->schib.pmcw.ena)
break; break;
} }
...@@ -851,7 +873,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib) ...@@ -851,7 +873,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
cc = msch(schid, schib); cc = msch(schid, schib);
if (cc) if (cc)
return (cc==3?-ENODEV:-EBUSY); return (cc==3?-ENODEV:-EBUSY);
stsch(schid, schib); if (stsch(schid, schib) || !css_sch_is_valid(schib))
return -ENODEV;
if (!schib->pmcw.ena) if (!schib->pmcw.ena)
return 0; return 0;
} }
......
...@@ -102,6 +102,7 @@ extern int cio_cancel (struct subchannel *); ...@@ -102,6 +102,7 @@ extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int); extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *); extern int cio_get_options (struct subchannel *);
extern int cio_modify (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_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
int cio_tm_intrg(struct subchannel *sch); int cio_tm_intrg(struct subchannel *sch);
......
...@@ -195,7 +195,8 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc, ...@@ -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 */ /* msch can silently fail, so do it again if necessary */
for (retry = 0; retry < 3; retry++) { for (retry = 0; retry < 3; retry++) {
/* prepare schib */ /* prepare schib */
stsch(sch->schid, schib); if (cio_update_schib(sch))
return -ENODEV;
schib->pmcw.mme = mme; schib->pmcw.mme = mme;
schib->pmcw.mbfc = mbfc; schib->pmcw.mbfc = mbfc;
/* address can be either a block address or a block index */ /* 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, ...@@ -219,7 +220,8 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
stsch(sch->schid, schib); /* restore the schib */ if (cio_update_schib(sch))
return -ENODEV;
if (ret) if (ret)
break; break;
...@@ -338,7 +340,7 @@ static int cmf_copy_block(struct ccw_device *cdev) ...@@ -338,7 +340,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
if (stsch(sch->schid, &sch->schib)) if (cio_update_schib(sch))
return -ENODEV; return -ENODEV;
if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) { if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
......
...@@ -1350,10 +1350,7 @@ static void io_subchannel_verify(struct subchannel *sch) ...@@ -1350,10 +1350,7 @@ static void io_subchannel_verify(struct subchannel *sch)
static int check_for_io_on_path(struct subchannel *sch, int mask) static int check_for_io_on_path(struct subchannel *sch, int mask)
{ {
int cc; if (cio_update_schib(sch))
cc = stsch(sch->schid, &sch->schib);
if (cc)
return 0; return 0;
if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask) if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
return 1; return 1;
...@@ -1422,15 +1419,13 @@ static int io_subchannel_chp_event(struct subchannel *sch, ...@@ -1422,15 +1419,13 @@ static int io_subchannel_chp_event(struct subchannel *sch,
io_subchannel_verify(sch); io_subchannel_verify(sch);
break; break;
case CHP_OFFLINE: case CHP_OFFLINE:
if (stsch(sch->schid, &sch->schib)) if (cio_update_schib(sch))
return -ENXIO;
if (!css_sch_is_valid(&sch->schib))
return -ENODEV; return -ENODEV;
io_subchannel_terminate_path(sch, mask); io_subchannel_terminate_path(sch, mask);
break; break;
case CHP_ONLINE: case CHP_ONLINE:
if (stsch(sch->schid, &sch->schib)) if (cio_update_schib(sch))
return -ENXIO; return -ENODEV;
sch->lpm |= mask & sch->opm; sch->lpm |= mask & sch->opm;
io_subchannel_verify(sch); io_subchannel_verify(sch);
break; break;
......
...@@ -140,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) ...@@ -140,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
int ret; int ret;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
ret = stsch(sch->schid, &sch->schib); if (cio_update_schib(sch))
if (ret || !sch->schib.pmcw.dnv)
return -ENODEV; return -ENODEV;
if (!sch->schib.pmcw.ena) if (!sch->schib.pmcw.ena)
/* Not operational -> done. */ /* Not operational -> done. */
...@@ -245,11 +244,13 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) ...@@ -245,11 +244,13 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
* through ssch() and the path information is up to date. * through ssch() and the path information is up to date.
*/ */
old_lpm = sch->lpm; 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. */ /* Check since device may again have become not operational. */
if (!sch->schib.pmcw.dnv) if (cio_update_schib(sch))
state = DEV_STATE_NOT_OPER; state = DEV_STATE_NOT_OPER;
else
sch->lpm = sch->schib.pmcw.pam & sch->opm;
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
/* Force reprobe on all chpids. */ /* Force reprobe on all chpids. */
old_lpm = 0; old_lpm = 0;
...@@ -549,7 +550,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ...@@ -549,7 +550,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
/* Update schib - pom may have changed. */ /* 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. */ /* Update lpm with verified path mask. */
sch->lpm = sch->vpm; sch->lpm = sch->vpm;
/* Repeat path verification? */ /* Repeat path verification? */
...@@ -667,7 +672,7 @@ ccw_device_offline(struct ccw_device *cdev) ...@@ -667,7 +672,7 @@ ccw_device_offline(struct ccw_device *cdev)
return 0; return 0;
} }
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv) if (cio_update_schib(sch))
return -ENODEV; return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0) if (scsw_actl(&sch->schib.scsw) != 0)
return -EBUSY; return -EBUSY;
...@@ -745,7 +750,10 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -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 * Since we might not just be coming from an interrupt from the
* subchannel we have to update the schib. * 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 || if (scsw_actl(&sch->schib.scsw) != 0 ||
(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) || (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
...@@ -1011,9 +1019,7 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev) ...@@ -1011,9 +1019,7 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
/* Update some values. */ /* Update some values. */
if (stsch(sch->schid, &sch->schib)) if (cio_update_schib(sch))
return;
if (!sch->schib.pmcw.dnv)
return; return;
/* /*
* The pim, pam, pom values may not be accurate, but they are the best * The pim, pam, pom values may not be accurate, but they are the best
......
...@@ -504,7 +504,7 @@ ccw_device_verify_start(struct ccw_device *cdev) ...@@ -504,7 +504,7 @@ ccw_device_verify_start(struct ccw_device *cdev)
sch->vpm = 0; sch->vpm = 0;
/* Get current pam. */ /* Get current pam. */
if (stsch(sch->schid, &sch->schib)) { if (cio_update_schib(sch)) {
ccw_device_verify_done(cdev, -ENODEV); ccw_device_verify_done(cdev, -ENODEV);
return; return;
} }
......
...@@ -56,7 +56,8 @@ ccw_device_path_notoper(struct ccw_device *cdev) ...@@ -56,7 +56,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
struct subchannel *sch; struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent); 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 " CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
"not operational \n", __func__, "not operational \n", __func__,
...@@ -64,6 +65,7 @@ ccw_device_path_notoper(struct ccw_device *cdev) ...@@ -64,6 +65,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom); sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom; sch->lpm &= ~sch->schib.pmcw.pnom;
doverify:
cdev->private->flags.doverify = 1; cdev->private->flags.doverify = 1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment