Commit 28bdc6f6 authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Martin Schwidefsky

[S390] cio: always query all paths on path verification.

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