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 + -
显示快捷键?