device_fsm.c

来自「linux 内核源代码」· C语言 代码 · 共 1,244 行 · 第 1/3 页

C
1,244
字号
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->dev_id.ssid,			      cdev->private->dev_id.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],		       &channel_subsystems[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. */voidccw_device_sense_pgid_done(struct ccw_device *cdev, int err){	struct subchannel *sch;	sch = to_subchannel(cdev->dev.parent);	switch (err) {	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);		return;	default:		ccw_device_done(cdev, DEV_STATE_NOT_OPER);		return;	}	/* Start Path Group verification. */	cdev->private->state = DEV_STATE_VERIFY;	cdev->private->flags.doverify = 0;	ccw_device_verify_start(cdev);}/* * Start device recognition. */intccw_device_recognition(struct ccw_device *cdev){	struct subchannel *sch;	int ret;	if ((cdev->private->state != DEV_STATE_NOT_OPER) &&	    (cdev->private->state != DEV_STATE_BOXED))		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);	ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);	if (ret != 0)		/* Couldn't enable the subchannel for i/o. Sick device. */		return ret;	/* After 60s the device recognition is considered to have failed. */	ccw_device_set_timeout(cdev, 60*HZ);	/*	 * 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;	ccw_device_sense_id_start(cdev);	return 0;}/* * Handle timeout in device recognition. */static voidccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event){	int ret;	ret = ccw_device_cancel_halt_clear(cdev);	switch (ret) {	case 0:		ccw_device_recog_done(cdev, DEV_STATE_BOXED);		break;	case -ENODEV:		ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);		break;	default:		ccw_device_set_timeout(cdev, 3*HZ);	}}voidccw_device_verify_done(struct ccw_device *cdev, int err){	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;	case 0:		ccw_device_done(cdev, DEV_STATE_ONLINE);		/* 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.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,					      &cdev->private->irb);			memset(&cdev->private->irb, 0, sizeof(struct irb));		}		break;	case -ETIME:		/* Reset oper notify indication after verify error. */		cdev->private->flags.donotify = 0;		ccw_device_done(cdev, DEV_STATE_BOXED);		break;	default:		/* Reset oper notify indication after verify error. */		cdev->private->flags.donotify = 0;		if (cdev->online)			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);		else			ccw_device_done(cdev, DEV_STATE_NOT_OPER);		break;	}}/* * Get device online. */intccw_device_online(struct ccw_device *cdev){	struct subchannel *sch;	int ret;	if ((cdev->private->state != DEV_STATE_OFFLINE) &&	    (cdev->private->state != DEV_STATE_BOXED))		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);	if (css_init_done && !get_device(&cdev->dev))		return -ENODEV;	ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);	if (ret != 0) {		/* Couldn't enable the subchannel for i/o. Sick device. */		if (ret == -ENODEV)			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);		return ret;	}	/* Do we want to do path grouping? */	if (!cdev->private->options.pgroup) {		/* 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. */	cdev->private->state = DEV_STATE_SENSE_PGID;	ccw_device_sense_pgid_start(cdev);	return 0;}voidccw_device_disband_done(struct ccw_device *cdev, int err){	switch (err) {	case 0:		ccw_device_done(cdev, DEV_STATE_OFFLINE);		break;	case -ETIME:		ccw_device_done(cdev, DEV_STATE_BOXED);		break;	default:		cdev->private->flags.donotify = 0;		dev_fsm_event(cdev, DEV_EVENT_NOTOPER);		ccw_device_done(cdev, DEV_STATE_NOT_OPER);		break;	}}/* * Shutdown device. */intccw_device_offline(struct ccw_device *cdev){	struct subchannel *sch;	if (ccw_device_is_orphan(cdev)) {		ccw_device_done(cdev, DEV_STATE_OFFLINE);		return 0;	}	sch = to_subchannel(cdev->dev.parent);	if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)		return -ENODEV;	if (cdev->private->state != DEV_STATE_ONLINE) {		if (sch->schib.scsw.actl != 0)			return -EBUSY;		return -EINVAL;	}	if (sch->schib.scsw.actl != 0)		return -EBUSY;	/* Are we doing path grouping? */	if (!cdev->private->options.pgroup) {		/* No, set state offline immediately. */		ccw_device_done(cdev, DEV_STATE_OFFLINE);		return 0;	}	/* Start Set Path Group commands. */	cdev->private->state = DEV_STATE_DISBAND_PGID;	ccw_device_disband_start(cdev);	return 0;}/* * Handle timeout in device online/offline process. */static voidccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event){	int ret;	ret = ccw_device_cancel_halt_clear(cdev);	switch (ret) {	case 0:		ccw_device_done(cdev, DEV_STATE_BOXED);		break;	case -ENODEV:		ccw_device_done(cdev, DEV_STATE_NOT_OPER);		break;	default:		ccw_device_set_timeout(cdev, 3*HZ);	}}/* * Handle not oper event in device recognition. */static voidccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event){	ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);}/* * Handle not operational event in non-special state. */static void ccw_device_generic_notoper(struct ccw_device *cdev,				       enum dev_event dev_event){	struct subchannel *sch;	cdev->private->state = DEV_STATE_NOT_OPER;	sch = to_subchannel(cdev->dev.parent);	css_schedule_eval(sch->schid);}/* * Handle path verification event. */static voidccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event){	struct subchannel *sch;	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.	 */	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		 * to the device driver. Can't do path verfication 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;	cdev->private->flags.doverify = 0;	ccw_device_verify_start(cdev);}/* * Got an interrupt for a normal io (state online). */static voidccw_device_irq(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))	    && (!irb->scsw.cc)) {		if ((irb->scsw.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->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 (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. */static voidccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event){	int ret;	ccw_device_set_timeout(cdev, 0);	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;	}	if (ret == -ENODEV)		dev_fsm_event(cdev, DEV_EVENT_NOTOPER);	else if (cdev->handler)		cdev->handler(cdev, cdev->private->intparm,			      ERR_PTR(-ETIMEDOUT));}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?