Commit d23861ff authored by Cornelia Huck's avatar Cornelia Huck Committed by Martin Schwidefsky

[S390] cio: Retry internal operations after vary off.

If I/O was running on a just varied off chpid, it will be terminated.
If this was a common I/O layer internal I/O, it needs to be retried.
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 24cb5b48
...@@ -251,6 +251,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) ...@@ -251,6 +251,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
cc = cio_clear(sch); cc = cio_clear(sch);
if (cc == -ENODEV) if (cc == -ENODEV)
goto out_unreg; goto out_unreg;
/* Request retry of internal operation. */
device_set_intretry(sch);
/* Call handler. */ /* Call handler. */
if (sch->driver && sch->driver->termination) if (sch->driver && sch->driver->termination)
sch->driver->termination(&sch->dev); sch->driver->termination(&sch->dev);
...@@ -711,9 +713,6 @@ static inline int check_for_io_on_path(struct subchannel *sch, int index) ...@@ -711,9 +713,6 @@ static inline int check_for_io_on_path(struct subchannel *sch, int index)
{ {
int cc; int cc;
if (!device_is_online(sch))
/* cio could be doing I/O. */
return 0;
cc = stsch(sch->schid, &sch->schib); cc = stsch(sch->schid, &sch->schib);
if (cc) if (cc)
return 0; return 0;
...@@ -722,6 +721,26 @@ static inline int check_for_io_on_path(struct subchannel *sch, int index) ...@@ -722,6 +721,26 @@ static inline int check_for_io_on_path(struct subchannel *sch, int index)
return 0; return 0;
} }
static void terminate_internal_io(struct subchannel *sch)
{
if (cio_clear(sch)) {
/* Recheck device in case clear failed. */
sch->lpm = 0;
if (device_trigger_verify(sch) != 0) {
if(css_enqueue_subchannel_slow(sch->schid)) {
css_clear_subchannel_slow_list();
need_rescan = 1;
}
}
return;
}
/* Request retry of internal operation. */
device_set_intretry(sch);
/* Call handler. */
if (sch->driver && sch->driver->termination)
sch->driver->termination(&sch->dev);
}
static inline void static inline void
__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
{ {
...@@ -748,10 +767,14 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) ...@@ -748,10 +767,14 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
} }
sch->opm &= ~(0x80 >> chp); sch->opm &= ~(0x80 >> chp);
sch->lpm &= ~(0x80 >> chp); sch->lpm &= ~(0x80 >> chp);
if (check_for_io_on_path(sch, chp)) if (check_for_io_on_path(sch, chp)) {
if (device_is_online(sch))
/* Path verification is done after killing. */ /* Path verification is done after killing. */
device_kill_io(sch); device_kill_io(sch);
else if (!sch->lpm) { else
/* Kill and retry internal I/O. */
terminate_internal_io(sch);
} else if (!sch->lpm) {
if (device_trigger_verify(sch) != 0) { if (device_trigger_verify(sch) != 0) {
if (css_enqueue_subchannel_slow(sch->schid)) { if (css_enqueue_subchannel_slow(sch->schid)) {
css_clear_subchannel_slow_list(); css_clear_subchannel_slow_list();
......
...@@ -94,6 +94,7 @@ struct ccw_device_private { ...@@ -94,6 +94,7 @@ struct ccw_device_private {
unsigned int donotify:1; /* call notify function */ unsigned int donotify:1; /* call notify function */
unsigned int recog_done:1; /* dev. recog. complete */ unsigned int recog_done:1; /* dev. recog. complete */
unsigned int fake_irb:1; /* deliver faked irb */ unsigned int fake_irb:1; /* deliver faked irb */
unsigned int intretry:1; /* retry internal operation */
} __attribute__((packed)) flags; } __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */ unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data; struct qdio_irq *qdio_data;
...@@ -171,6 +172,7 @@ void device_trigger_reprobe(struct subchannel *); ...@@ -171,6 +172,7 @@ void device_trigger_reprobe(struct subchannel *);
/* Helper functions for vary on/off. */ /* Helper functions for vary on/off. */
int device_is_online(struct subchannel *); int device_is_online(struct subchannel *);
void device_kill_io(struct subchannel *); void device_kill_io(struct subchannel *);
void device_set_intretry(struct subchannel *sch);
int device_trigger_verify(struct subchannel *sch); int device_trigger_verify(struct subchannel *sch);
/* Machine check helper function. */ /* Machine check helper function. */
......
...@@ -948,6 +948,9 @@ io_subchannel_ioterm(struct device *dev) ...@@ -948,6 +948,9 @@ io_subchannel_ioterm(struct device *dev)
cdev = dev->driver_data; cdev = dev->driver_data;
if (!cdev) if (!cdev)
return; return;
/* Internal I/O will be retried by the interrupt handler. */
if (cdev->private->flags.intretry)
return;
cdev->private->state = DEV_STATE_CLEAR_VERIFY; cdev->private->state = DEV_STATE_CLEAR_VERIFY;
if (cdev->handler) if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm, cdev->handler(cdev, cdev->private->intparm,
......
...@@ -59,6 +59,16 @@ device_set_disconnected(struct subchannel *sch) ...@@ -59,6 +59,16 @@ device_set_disconnected(struct subchannel *sch)
cdev->private->state = DEV_STATE_DISCONNECTED; cdev->private->state = DEV_STATE_DISCONNECTED;
} }
void device_set_intretry(struct subchannel *sch)
{
struct ccw_device *cdev;
cdev = sch->dev.driver_data;
if (!cdev)
return;
cdev->private->flags.intretry = 1;
}
int device_trigger_verify(struct subchannel *sch) int device_trigger_verify(struct subchannel *sch)
{ {
struct ccw_device *cdev; struct ccw_device *cdev;
...@@ -904,6 +914,12 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -904,6 +914,12 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
* had killed the original request. * had killed the original request.
*/ */
if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
/* Retry Basic Sense if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
ccw_device_do_sense(cdev, irb);
return;
}
cdev->private->flags.dosense = 0; cdev->private->flags.dosense = 0;
memset(&cdev->private->irb, 0, sizeof(struct irb)); memset(&cdev->private->irb, 0, sizeof(struct irb));
ccw_device_accumulate_irb(cdev, irb); ccw_device_accumulate_irb(cdev, irb);
......
...@@ -191,6 +191,8 @@ __ccw_device_sense_id_start(struct ccw_device *cdev) ...@@ -191,6 +191,8 @@ __ccw_device_sense_id_start(struct ccw_device *cdev)
if ((sch->opm & cdev->private->imask) != 0 && if ((sch->opm & cdev->private->imask) != 0 &&
cdev->private->iretry > 0) { cdev->private->iretry > 0) {
cdev->private->iretry--; cdev->private->iretry--;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
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 */ /* ret is 0, -EBUSY, -EACCES or -ENODEV */
...@@ -237,8 +239,14 @@ ccw_device_check_sense_id(struct ccw_device *cdev) ...@@ -237,8 +239,14 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
return 0; /* Success */ return 0; /* Success */
} }
/* Check the error cases. */ /* Check the error cases. */
if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Sense ID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
return -EAGAIN;
}
return -ETIME; return -ETIME;
}
if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) { if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
/* /*
* if the device doesn't support the SenseID * if the device doesn't support the SenseID
......
...@@ -71,6 +71,8 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev) ...@@ -71,6 +71,8 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
if (cdev->private->iretry > 0) { if (cdev->private->iretry > 0) {
cdev->private->iretry--; cdev->private->iretry--;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
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 */ /* ret is 0, -EBUSY, -EACCES or -ENODEV */
...@@ -122,8 +124,14 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) ...@@ -122,8 +124,14 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb; irb = &cdev->private->irb;
if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Sense PGID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
return -EAGAIN;
}
return -ETIME; return -ETIME;
}
if (irb->esw.esw0.erw.cons && if (irb->esw.esw0.erw.cons &&
(irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
/* /*
...@@ -253,6 +261,8 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) ...@@ -253,6 +261,8 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
ret = -EACCES; ret = -EACCES;
if (cdev->private->iretry > 0) { if (cdev->private->iretry > 0) {
cdev->private->iretry--; cdev->private->iretry--;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
ret = cio_start (sch, cdev->private->iccws, ret = cio_start (sch, cdev->private->iccws,
cdev->private->imask); cdev->private->imask);
/* We expect an interrupt in case of success or busy /* We expect an interrupt in case of success or busy
...@@ -293,6 +303,8 @@ static int __ccw_device_do_nop(struct ccw_device *cdev) ...@@ -293,6 +303,8 @@ static int __ccw_device_do_nop(struct ccw_device *cdev)
ret = -EACCES; ret = -EACCES;
if (cdev->private->iretry > 0) { if (cdev->private->iretry > 0) {
cdev->private->iretry--; cdev->private->iretry--;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
ret = cio_start (sch, cdev->private->iccws, ret = cio_start (sch, cdev->private->iccws,
cdev->private->imask); cdev->private->imask);
/* We expect an interrupt in case of success or busy /* We expect an interrupt in case of success or busy
...@@ -321,8 +333,14 @@ __ccw_device_check_pgid(struct ccw_device *cdev) ...@@ -321,8 +333,14 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb; irb = &cdev->private->irb;
if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Set PGID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
return -EAGAIN;
}
return -ETIME; return -ETIME;
}
if (irb->esw.esw0.erw.cons) { if (irb->esw.esw0.erw.cons) {
if (irb->ecw[0] & SNS0_CMD_REJECT) if (irb->ecw[0] & SNS0_CMD_REJECT)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -360,8 +378,14 @@ static int __ccw_device_check_nop(struct ccw_device *cdev) ...@@ -360,8 +378,14 @@ static int __ccw_device_check_nop(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb; irb = &cdev->private->irb;
if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry NOP if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
return -EAGAIN;
}
return -ETIME; return -ETIME;
}
if (irb->scsw.cc == 3) { if (irb->scsw.cc == 3) {
CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x," CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n", " lpm %02X, became 'not operational'\n",
......
...@@ -319,6 +319,9 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) ...@@ -319,6 +319,9 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
sch->sense_ccw.count = SENSE_MAX_COUNT; sch->sense_ccw.count = SENSE_MAX_COUNT;
sch->sense_ccw.flags = CCW_FLAG_SLI; sch->sense_ccw.flags = CCW_FLAG_SLI;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
return cio_start (sch, &sch->sense_ccw, 0xff); return cio_start (sch, &sch->sense_ccw, 0xff);
} }
......
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