diff options
Diffstat (limited to 'drivers/s390/cio/device_fsm.c')
| -rw-r--r-- | drivers/s390/cio/device_fsm.c | 166 |
1 files changed, 120 insertions, 46 deletions
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 49ec562d7f6..dace46fc32e 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -9,7 +9,6 @@ */ #include <linux/module.h> -#include <linux/config.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/string.h> @@ -153,7 +152,8 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) if (cdev->private->iretry) { cdev->private->iretry--; ret = cio_halt(sch); - return (ret == 0) ? -EBUSY : ret; + if (ret != -EBUSY) + return (ret == 0) ? -EBUSY : ret; } /* halt io unsuccessful. */ cdev->private->iretry = 255; /* 255 clear retries. */ @@ -232,10 +232,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) */ old_lpm = sch->lpm; stsch(sch->schid, &sch->schib); - sch->lpm = sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom & - sch->opm; + sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Check since device may again have become not operational. */ if (!sch->schib.pmcw.dnv) state = DEV_STATE_NOT_OPER; @@ -267,12 +264,11 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) notify = 1; } /* fill out sense information */ - cdev->id = (struct ccw_device_id) { - .cu_type = cdev->private->senseid.cu_type, - .cu_model = cdev->private->senseid.cu_model, - .dev_type = cdev->private->senseid.dev_type, - .dev_model = cdev->private->senseid.dev_model, - }; + memset(&cdev->id, 0, sizeof(cdev->id)); + cdev->id.cu_type = cdev->private->senseid.cu_type; + cdev->id.cu_model = cdev->private->senseid.cu_model; + cdev->id.dev_type = cdev->private->senseid.dev_type; + cdev->id.dev_model = cdev->private->senseid.dev_model; if (notify) { cdev->private->state = DEV_STATE_OFFLINE; if (same_dev) { @@ -336,8 +332,11 @@ ccw_device_oper_notify(void *data) if (!ret) /* Driver doesn't want device back. */ ccw_device_do_unreg_rereg((void *)cdev); - else + else { + /* Reenable channel measurements, if needed. */ + cmf_reenable(cdev); wake_up(&cdev->private->wait_q); + } } /* @@ -376,6 +375,56 @@ ccw_device_done(struct ccw_device *cdev, int state) put_device (&cdev->dev); } +static inline int cmp_pgid(struct pgid *p1, struct pgid *p2) +{ + char *c1; + char *c2; + + c1 = (char *)p1; + c2 = (char *)p2; + + return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1); +} + +static void __ccw_device_get_common_pgid(struct ccw_device *cdev) +{ + int i; + int last; + + last = 0; + for (i = 0; i < 8; i++) { + if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET) + /* No PGID yet */ + continue; + if (cdev->private->pgid[last].inf.ps.state1 == + SNID_STATE1_RESET) { + /* First non-zero PGID */ + last = i; + continue; + } + if (cmp_pgid(&cdev->private->pgid[i], + &cdev->private->pgid[last]) == 0) + /* Non-conflicting PGIDs */ + continue; + + /* PGID mismatch, can't pathgroup. */ + CIO_MSG_EVENT(0, "SNID - pgid mismatch for device " + "0.%x.%04x, can't pathgroup\n", + cdev->private->ssid, cdev->private->devno); + cdev->private->options.pgroup = 0; + return; + } + if (cdev->private->pgid[last].inf.ps.state1 == + SNID_STATE1_RESET) + /* No previous pgid found */ + memcpy(&cdev->private->pgid[0], &css[0]->global_pgid, + sizeof(struct pgid)); + else + /* Use existing pgid */ + memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last], + sizeof(struct pgid)); +} + /* * Function called from device_pgid.c after sense path ground has completed. */ @@ -386,24 +435,26 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) sch = to_subchannel(cdev->dev.parent); switch (err) { - case 0: - /* Start Path Group verification. */ - sch->vpm = 0; /* Start with no path groups set. */ - cdev->private->state = DEV_STATE_VERIFY; - ccw_device_verify_start(cdev); + case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ + cdev->private->options.pgroup = 0; + break; + case 0: /* success */ + case -EACCES: /* partial success, some paths not operational */ + /* Check if all pgids are equal or 0. */ + __ccw_device_get_common_pgid(cdev); break; case -ETIME: /* Sense path group id stopped by timeout. */ case -EUSERS: /* device is reserved for someone else. */ ccw_device_done(cdev, DEV_STATE_BOXED); - break; - case -EOPNOTSUPP: /* path grouping not supported, just set online. */ - cdev->private->options.pgroup = 0; - ccw_device_done(cdev, DEV_STATE_ONLINE); - break; + return; default: ccw_device_done(cdev, DEV_STATE_NOT_OPER); - break; + return; } + /* Start Path Group verification. */ + cdev->private->state = DEV_STATE_VERIFY; + cdev->private->flags.doverify = 0; + ccw_device_verify_start(cdev); } /* @@ -502,7 +553,19 @@ ccw_device_nopath_notify(void *data) void 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) { case -EOPNOTSUPP: /* path grouping not supported, just set online. */ cdev->private->options.pgroup = 0; @@ -511,12 +574,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) /* Deliver fake irb to device driver, if needed. */ if (cdev->private->flags.fake_irb) { memset(&cdev->private->irb, 0, sizeof(struct irb)); - cdev->private->irb.scsw = (struct scsw) { - .cc = 1, - .fctl = SCSW_FCTL_START_FUNC, - .actl = SCSW_ACTL_START_PEND, - .stctl = SCSW_STCTL_STATUS_PEND, - }; + cdev->private->irb.scsw.cc = 1; + cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC; + cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND; + cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND; cdev->private->flags.fake_irb = 0; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, @@ -560,8 +621,10 @@ ccw_device_online(struct ccw_device *cdev) } /* Do we want to do path grouping? */ if (!cdev->private->options.pgroup) { - /* No, set state online immediately. */ - ccw_device_done(cdev, DEV_STATE_ONLINE); + /* Start initial path verification. */ + cdev->private->state = DEV_STATE_VERIFY; + cdev->private->flags.doverify = 0; + ccw_device_verify_start(cdev); return 0; } /* Do a SensePGID first. */ @@ -703,8 +766,6 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) { struct subchannel *sch; - if (!cdev->private->options.pgroup) - return; if (cdev->private->state == DEV_STATE_W4SENSE) { cdev->private->flags.doverify = 1; return; @@ -717,6 +778,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) stsch(sch->schid, &sch->schib); if (sch->schib.scsw.actl != 0 || + (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) || (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) { /* * No final status yet or final status not yet delivered @@ -728,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. */ cdev->private->state = DEV_STATE_VERIFY; + cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); } @@ -861,6 +924,8 @@ ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event) irb = (struct irb *) __LC_IRB; /* Accumulate status. We don't do basic sense. */ ccw_device_accumulate_irb(cdev, irb); + /* Remember to clear irb to avoid residuals. */ + memset(&cdev->private->irb, 0, sizeof(struct irb)); /* Try to start delayed device verification. */ ccw_device_online_verify(cdev, 0); /* Note: Don't call handler for cio initiated clear! */ @@ -988,11 +1053,10 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event) } 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. */ - if (cdev->private->options.pgroup) - cdev->private->flags.doverify = 1; + /* Start verification after current task finished. */ + cdev->private->flags.doverify = 1; } static void @@ -1057,10 +1121,7 @@ device_trigger_reprobe(struct subchannel *sch) * The pim, pam, pom values may not be accurate, but they are the best * we have before performing device selection :/ */ - sch->lpm = sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom & - sch->opm; + sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Re-set some bits in the pmcw that were lost. */ sch->schib.pmcw.isc = 3; sch->schib.pmcw.csense = 1; @@ -1093,6 +1154,13 @@ ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event) dev_fsm_event(cdev, dev_event); } +static void ccw_device_update_cmfblock(struct ccw_device *cdev, + enum dev_event dev_event) +{ + cmf_retry_copy_block(cdev); + cdev->private->state = DEV_STATE_ONLINE; + dev_fsm_event(cdev, dev_event); +} static void ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) @@ -1177,7 +1245,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, - [DEV_EVENT_VERIFY] = ccw_device_nop, + [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_ONLINE] = { [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, @@ -1220,7 +1288,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_wait4io_irq, [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_EVENT_NOTOPER] = ccw_device_quiesce_done, @@ -1233,7 +1301,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, [DEV_EVENT_INTERRUPT] = ccw_device_start_id, [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_EVENT_NOTOPER] = ccw_device_recog_notoper, @@ -1247,6 +1315,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_TIMEOUT] = ccw_device_change_cmfstate, [DEV_EVENT_VERIFY] = ccw_device_change_cmfstate, }, + [DEV_STATE_CMFUPDATE] = { + [DEV_EVENT_NOTOPER] = ccw_device_update_cmfblock, + [DEV_EVENT_INTERRUPT] = ccw_device_update_cmfblock, + [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock, + [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock, + }, }; /* |