mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 15:30:54 +07:00
[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: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
e0e32c8eba
commit
28bdc6f623
@ -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)
|
|||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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)
|
|||||||
void
|
void
|
||||||
ccw_device_verify_done(struct ccw_device *cdev, int err)
|
ccw_device_verify_done(struct ccw_device *cdev, int err)
|
||||||
{
|
{
|
||||||
cdev->private->flags.doverify = 0;
|
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;
|
||||||
|
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
}
|
}
|
||||||
/* 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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
* 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] = {
|
|||||||
[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] = {
|
|||||||
[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] = {
|
|||||||
[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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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
|
|||||||
__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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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:
|
||||||
|
Loading…
Reference in New Issue
Block a user