device_fsm.c
来自「linux 内核源代码」· C语言 代码 · 共 1,244 行 · 第 1/3 页
C
1,244 行
/* * Got an interrupt for a basic sense. */static voidccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event){ struct irb *irb; irb = (struct irb *) __LC_IRB; /* Check for unsolicited interrupt. */ if (irb->scsw.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { if (irb->scsw.cc == 1) /* Basic sense hasn't started. Try again. */ ccw_device_do_sense(cdev, irb); else { CIO_MSG_EVENT(2, "Huh? 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 (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; memset(&cdev->private->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; /* Call the handler. */ if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify) /* Start delayed path verification. */ ccw_device_online_verify(cdev, 0);}static voidccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event){ struct irb *irb; 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! */}static voidccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event){ struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); ccw_device_set_timeout(cdev, 0); /* Start delayed path verification. */ ccw_device_online_verify(cdev, 0); /* OK, i/o is dead now. Call interrupt handler. */ if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));}static voidccw_device_killing_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, 3*HZ); return; } /* Start delayed path verification. */ ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));}void device_kill_io(struct subchannel *sch){ int ret; struct ccw_device *cdev; cdev = sch->dev.driver_data; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); cdev->private->state = DEV_STATE_TIMEOUT_KILL; return; } /* Start delayed path verification. */ ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));}static voidccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event){ /* Start verification after current task finished. */ cdev->private->flags.doverify = 1;}static voidccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event){ struct irb *irb; switch (dev_event) { case DEV_EVENT_INTERRUPT: irb = (struct irb *) __LC_IRB; /* Check for unsolicited interrupt. */ if ((irb->scsw.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) && (!irb->scsw.cc)) /* FIXME: we should restart stlck here, but this * is extremely unlikely ... */ goto out_wakeup; ccw_device_accumulate_irb(cdev, irb); /* We don't care about basic sense etc. */ break; default: /* timeout */ break; }out_wakeup: wake_up(&cdev->private->wait_q);}static voidccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event){ struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; /* After 60s the device recognition is considered to have failed. */ ccw_device_set_timeout(cdev, 60*HZ); cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID; ccw_device_sense_id_start(cdev);}voiddevice_trigger_reprobe(struct subchannel *sch){ struct ccw_device *cdev; if (!sch->dev.driver_data) return; cdev = sch->dev.driver_data; if (cdev->private->state != DEV_STATE_DISCONNECTED) return; /* Update some values. */ if (stsch(sch->schid, &sch->schib)) return; if (!sch->schib.pmcw.dnv) 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; /* Re-set some bits in the pmcw that were lost. */ sch->schib.pmcw.isc = 3; sch->schib.pmcw.csense = 1; sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; /* 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) { PREPARE_WORK(&cdev->private->kick_work, ccw_device_move_to_orphanage); queue_work(slow_path_wq, &cdev->private->kick_work); } else ccw_device_start_id(cdev, 0);}static voidccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event){ struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); /* * An interrupt in state offline means a previous disable was not * successful. Try again. */ cio_disable_subchannel(sch);}static voidccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event){ retry_set_schib(cdev); cdev->private->state = DEV_STATE_ONLINE; 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 voidccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event){ ccw_device_set_timeout(cdev, 0); if (dev_event == DEV_EVENT_NOTOPER) cdev->private->state = DEV_STATE_NOT_OPER; else cdev->private->state = DEV_STATE_OFFLINE; wake_up(&cdev->private->wait_q);}static voidccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event){ int ret; ret = ccw_device_cancel_halt_clear(cdev); switch (ret) { case 0: cdev->private->state = DEV_STATE_OFFLINE; wake_up(&cdev->private->wait_q); break; case -ENODEV: cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); break; default: ccw_device_set_timeout(cdev, HZ/10); }}/* * No operation action. This is used e.g. to ignore a timeout event in * state offline. */static voidccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event){}/* * Bug operation action. */static voidccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event){ CIO_MSG_EVENT(0, "dev_jumptable[%i][%i] == NULL\n", cdev->private->state, dev_event); BUG();}/* * device statemachine */fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_STATE_NOT_OPER] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, [DEV_EVENT_INTERRUPT] = ccw_device_bug, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_PGID] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_ID] = { [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_OFFLINE] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_VERIFY] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_ONLINE] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_irq, [DEV_EVENT_TIMEOUT] = ccw_device_online_timeout, [DEV_EVENT_VERIFY] = ccw_device_online_verify, }, [DEV_STATE_W4SENSE] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_w4sense, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_online_verify, }, [DEV_STATE_DISBAND_PGID] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_BOXED] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, [DEV_EVENT_VERIFY] = ccw_device_nop, }, /* states to wait for i/o completion before doing something */ [DEV_STATE_CLEAR_VERIFY] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_TIMEOUT_KILL] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_killing_irq, [DEV_EVENT_TIMEOUT] = ccw_device_killing_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, //FIXME }, [DEV_STATE_QUIESCE] = { [DEV_EVENT_NOTOPER] = ccw_device_quiesce_done, [DEV_EVENT_INTERRUPT] = ccw_device_quiesce_done, [DEV_EVENT_TIMEOUT] = ccw_device_quiesce_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, }, /* special states for devices gone not operational */ [DEV_STATE_DISCONNECTED] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, [DEV_EVENT_INTERRUPT] = ccw_device_start_id, [DEV_EVENT_TIMEOUT] = ccw_device_bug, [DEV_EVENT_VERIFY] = ccw_device_start_id, }, [DEV_STATE_DISCONNECTED_SENSE_ID] = { [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_CMFCHANGE] = { [DEV_EVENT_NOTOPER] = ccw_device_change_cmfstate, [DEV_EVENT_INTERRUPT] = ccw_device_change_cmfstate, [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, },};/* * io_subchannel_irq is called for "real" interrupts or for status * pending conditions on msch. */voidio_subchannel_irq (struct device *pdev){ struct ccw_device *cdev; cdev = to_subchannel(pdev)->dev.driver_data; CIO_TRACE_EVENT (3, "IRQ"); CIO_TRACE_EVENT (3, pdev->bus_id); if (cdev) dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);}EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?