/* * The machine won't give us any notification by machine check if a chpid has * been varied online on the SE so we have to find out by magic (i. e. driving * the channel subsystem to device selection and updating our path masks).
*/ staticvoid
__recover_lost_chpids(struct subchannel *sch, int old_lpm)
{ int mask, i; struct chp_id chpid;
chp_id_init(&chpid); for (i = 0; i<8; i++) {
mask = 0x80 >> i; if (!(sch->lpm & mask)) continue; if (old_lpm & mask) continue;
chpid.id = sch->schib.pmcw.chpid[i]; if (!chp_is_registered(chpid))
css_schedule_eval_all();
}
}
/* * Stop device recognition.
*/ staticvoid
ccw_device_recog_done(struct ccw_device *cdev, int state)
{ struct subchannel *sch; int old_lpm;
sch = to_subchannel(cdev->dev.parent);
if (cio_disable_subchannel(sch))
state = DEV_STATE_NOT_OPER; /* * Now that we tried recognition, we have performed device selection * through ssch() and the path information is up to date.
*/
old_lpm = sch->lpm;
/* Check since device may again have become not operational. */ if (cio_update_schib(sch))
state = DEV_STATE_NOT_OPER; else
sch->lpm = sch->schib.pmcw.pam & sch->opm;
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) /* Force reprobe on all chpids. */
old_lpm = 0; if (sch->lpm != old_lpm)
__recover_lost_chpids(sch, old_lpm); if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID &&
(state == DEV_STATE_NOT_OPER || state == DEV_STATE_BOXED)) {
cdev->private->flags.recog_done = 1;
cdev->private->state = DEV_STATE_DISCONNECTED;
wake_up(&cdev->private->wait_q); return;
} switch (state) { case DEV_STATE_NOT_OPER: break; case DEV_STATE_OFFLINE: if (!cdev->online) {
ccw_device_update_sense_data(cdev); break;
}
cdev->private->state = DEV_STATE_OFFLINE;
cdev->private->flags.recog_done = 1; if (ccw_device_test_sense_data(cdev)) {
cdev->private->flags.donotify = 1;
ccw_device_online(cdev);
wake_up(&cdev->private->wait_q);
} else {
ccw_device_update_sense_data(cdev);
ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
} return; case DEV_STATE_BOXED: if (cdev->id.cu_type != 0) { /* device was recognized before */
cdev->private->flags.recog_done = 1;
cdev->private->state = DEV_STATE_BOXED;
wake_up(&cdev->private->wait_q); return;
} break;
}
cdev->private->state = state;
io_subchannel_recog_done(cdev);
wake_up(&cdev->private->wait_q);
}
/* * Function called from device_id.c after sense id has completed.
*/ void
ccw_device_sense_id_done(struct ccw_device *cdev, int err)
{ switch (err) { case 0:
ccw_device_recog_done(cdev, DEV_STATE_OFFLINE); break; case -ETIME: /* Sense id stopped by timeout. */
ccw_device_recog_done(cdev, DEV_STATE_BOXED); break; default:
ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); break;
}
}
/** * ccw_device_notify() - inform the device's driver about an event * @cdev: device for which an event occurred * @event: event that occurred * * Returns: * -%EINVAL if the device is offline or has no driver. * -%EOPNOTSUPP if the device's driver has no notifier registered. * %NOTIFY_OK if the driver wants to keep the device. * %NOTIFY_BAD if the driver doesn't want to keep the device.
*/ int ccw_device_notify(struct ccw_device *cdev, int event)
{ int ret = -EINVAL;
if (!cdev->drv) goto out; if (!cdev->online) goto out;
CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
event); if (!cdev->drv->notify) {
ret = -EOPNOTSUPP; goto out;
} if (cdev->drv->notify(cdev, event))
ret = NOTIFY_OK; else
ret = NOTIFY_BAD;
out: return ret;
}
/* * We used to start here with a sense pgid to find out whether a device * is locked by someone else. Unfortunately, the sense pgid command * code has other meanings on devices predating the path grouping * algorithm, so we start with sense id and box the device after an * timeout (or if sense pgid during path verification detects the device * is locked, as may happen on newer devices).
*/
cdev->private->flags.recog_done = 0;
cdev->private->state = DEV_STATE_SENSE_ID; if (cio_enable_subchannel(sch, (u32)virt_to_phys(sch))) {
ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); return;
}
ccw_device_sense_id_start(cdev);
}
/* * Handle events for states that use the ccw request infrastructure.
*/ staticvoid ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
{ switch (e) { case DEV_EVENT_NOTOPER:
ccw_request_notoper(cdev); break; case DEV_EVENT_INTERRUPT:
ccw_request_handler(cdev); break; case DEV_EVENT_TIMEOUT:
ccw_request_timeout(cdev); break; default: break;
}
}
staticvoid ccw_device_report_path_events(struct ccw_device *cdev)
{ struct subchannel *sch = to_subchannel(cdev->dev.parent); int path_event[8]; int chp, mask;
if (cdev->private->state == DEV_STATE_W4SENSE) {
cdev->private->flags.doverify = 1; return;
}
sch = to_subchannel(cdev->dev.parent); /* * Since we might not just be coming from an interrupt from the * subchannel we have to update the schib.
*/ if (cio_update_schib(sch)) {
ccw_device_verify_done(cdev, -ENODEV); return;
}
if (scsw_actl(&sch->schib.scsw) != 0 ||
(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
(scsw_stctl(&cdev->private->dma_area->irb.scsw) &
SCSW_STCTL_STATUS_PEND)) { /* * No final status yet or final status not yet delivered * to the device driver. Can't do path verification now, * delay until final status was delivered.
*/
cdev->private->flags.doverify = 1; return;
} /* Device is idle, we can do the path verification. */
cdev->private->state = DEV_STATE_VERIFY;
ccw_device_verify_start(cdev);
}
if (cdev->online) { if (cio_enable_subchannel(sch, (u32)virt_to_phys(sch)))
ccw_device_done(cdev, DEV_STATE_NOT_OPER); else
ccw_device_online_verify(cdev, dev_event);
} else
css_schedule_eval(sch->schid);
}
/* * Pass interrupt to device driver.
*/ staticint ccw_device_call_handler(struct ccw_device *cdev)
{ unsignedint stctl; int ending_status;
/* * we allow for the device action handler if . * - we received ending status * - the action handler requested to see all interrupts * - we received an intermediate status * - fast notification was requested (primary status) * - unsolicited interrupts
*/
stctl = scsw_stctl(&cdev->private->dma_area->irb.scsw);
ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
(stctl == SCSW_STCTL_STATUS_PEND); if (!ending_status &&
!cdev->private->options.repall &&
!(stctl & SCSW_STCTL_INTER_STATUS) &&
!(cdev->private->options.fast &&
(stctl & SCSW_STCTL_PRIM_STATUS))) return 0;
if (ending_status)
ccw_device_set_timeout(cdev, 0);
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
&cdev->private->dma_area->irb);
/* * Got an interrupt for a normal io (state online).
*/ staticvoid
ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
{ struct irb *irb; int is_cmd;
irb = this_cpu_ptr(&cio_irb);
is_cmd = !scsw_is_tm(&irb->scsw); /* Check for unsolicited interrupt. */ if (!scsw_is_solicited(&irb->scsw)) { if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
!irb->esw.esw0.erw.cons) { /* Unit check but no sense data. Need basic sense. */ if (ccw_device_do_sense(cdev, irb) != 0) goto call_handler_unsol;
memcpy(&cdev->private->dma_area->irb, irb, sizeof(struct irb));
cdev->private->state = DEV_STATE_W4SENSE;
cdev->private->intparm = 0; return;
}
call_handler_unsol: if (cdev->handler)
cdev->handler (cdev, 0, irb); if (cdev->private->flags.doverify)
ccw_device_online_verify(cdev, 0); return;
} /* Accumulate status and find out if a basic sense is needed. */
ccw_device_accumulate_irb(cdev, irb); if (is_cmd && cdev->private->flags.dosense) { if (ccw_device_do_sense(cdev, irb) == 0) {
cdev->private->state = DEV_STATE_W4SENSE;
} return;
} /* Call the handler. */ if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify) /* Start delayed path verification. */
ccw_device_online_verify(cdev, 0);
}
/* * Got an timeout in online state.
*/ staticvoid
ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
{ int ret;
/* * Got an interrupt for a basic sense.
*/ staticvoid
ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
{ struct irb *irb;
irb = this_cpu_ptr(&cio_irb); /* Check for unsolicited interrupt. */ if (scsw_stctl(&irb->scsw) ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { if (scsw_cc(&irb->scsw) == 1) /* Basic sense hasn't started. Try again. */
ccw_device_do_sense(cdev, irb); else {
CIO_MSG_EVENT(0, "0.%x.%04x: unsolicited " "interrupt during w4sense...\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno); if (cdev->handler)
cdev->handler (cdev, 0, irb);
} return;
} /* * Check if a halt or clear has been issued in the meanwhile. If yes, * only deliver the halt/clear interrupt to the device driver as if it * had killed the original request.
*/ if (scsw_fctl(&irb->scsw) &
(SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
cdev->private->flags.dosense = 0;
memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
ccw_device_accumulate_irb(cdev, irb); goto call_handler;
} /* Add basic sense info to irb. */
ccw_device_accumulate_basic_sense(cdev, irb); if (cdev->private->flags.dosense) { /* Another basic sense is needed. */
ccw_device_do_sense(cdev, irb); return;
}
call_handler:
cdev->private->state = DEV_STATE_ONLINE; /* In case sensing interfered with setting the device online */
wake_up(&cdev->private->wait_q); /* Call the handler. */ if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify) /* Start delayed path verification. */
ccw_device_online_verify(cdev, 0);
}
if (cdev->private->state != DEV_STATE_DISCONNECTED) return;
sch = to_subchannel(cdev->dev.parent); /* Update some values. */ if (cio_update_schib(sch)) return; /* * 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.pam & sch->opm; /* * Use the initial configuration since we can't be sure that the old * paths are valid.
*/
io_subchannel_init_config(sch); if (cio_commit_config(sch)) return;
/* We should also udate ssd info, but this has to wait. */ /* Check if this is another device which appeared on the same sch. */ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno)
css_schedule_eval(sch->schid); else
ccw_device_start_id(cdev, 0);
}
sch = to_subchannel(cdev->dev.parent); /* * An interrupt in a disabled state means a previous disable was not * successful - should not happen, but we try to disable again.
*/
cio_disable_subchannel(sch);
}
staticvoid
ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
{ int ret;
ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, HZ/10);
} else {
cdev->private->state = DEV_STATE_NOT_OPER;
wake_up(&cdev->private->wait_q);
}
}
/* * No operation action. This is used e.g. to ignore a timeout event in * state offline.
*/ staticvoid
ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
{
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.