qdio.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,574 行 · 第 1/5 页

C
2,574
字号
	struct qdio_irq *irq_ptr;	char dbf_text[15];	QDIO_DBF_TEXT2(0, trace, "qtoh");	sprintf(dbf_text, "%s", cdev->dev.bus_id);	QDIO_DBF_TEXT2(0, trace, dbf_text);	irq_ptr = cdev->private->qdio_data;	sprintf(dbf_text, "state:%d", irq_ptr->state);	QDIO_DBF_TEXT2(0, trace, dbf_text);	switch (irq_ptr->state) {	case QDIO_IRQ_STATE_INACTIVE:		QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: timed out\n",			       irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		QDIO_DBF_TEXT2(1,setup,"eq:timeo");		qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);		break;	case QDIO_IRQ_STATE_CLEANUP:		QDIO_PRINT_INFO("Did not get interrupt on cleanup, "				"irq=0.%x.%x.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);		break;	case QDIO_IRQ_STATE_ESTABLISHED:	case QDIO_IRQ_STATE_ACTIVE:		/* I/O has been terminated by common I/O layer. */		QDIO_PRINT_INFO("Queues on irq 0.%x.%04x killed by cio.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		QDIO_DBF_TEXT2(1, trace, "cio:term");		qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);		if (get_device(&cdev->dev)) {			/* Can't call shutdown from interrupt context. */			PREPARE_WORK(&cdev->private->kick_work,				     qdio_call_shutdown, (void *)cdev);			queue_work(ccw_device_work, &cdev->private->kick_work);		}		break;	default:		BUG();	}	ccw_device_set_timeout(cdev, 0);	wake_up(&cdev->private->wait_q);}static voidqdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb){	struct qdio_irq *irq_ptr;	int cstat,dstat;	char dbf_text[15];#ifdef CONFIG_QDIO_DEBUG	QDIO_DBF_TEXT4(0, trace, "qint");	sprintf(dbf_text, "%s", cdev->dev.bus_id);	QDIO_DBF_TEXT4(0, trace, dbf_text);#endif /* CONFIG_QDIO_DEBUG */		if (!intparm) {		QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \				  "handler, device %s\n", cdev->dev.bus_id);		return;	}	irq_ptr = cdev->private->qdio_data;	if (!irq_ptr) {		QDIO_DBF_TEXT2(1, trace, "uint");		sprintf(dbf_text,"%s", cdev->dev.bus_id);		QDIO_DBF_TEXT2(1,trace,dbf_text);		QDIO_PRINT_ERR("received interrupt on unused device %s!\n",			       cdev->dev.bus_id);		return;	}	if (IS_ERR(irb)) {		/* Currently running i/o is in error. */		switch (PTR_ERR(irb)) {		case -EIO:			QDIO_PRINT_ERR("i/o error on device %s\n",				       cdev->dev.bus_id);			return;		case -ETIMEDOUT:			qdio_timeout_handler(cdev);			return;		default:			QDIO_PRINT_ERR("unknown error state %ld on device %s\n",				       PTR_ERR(irb), cdev->dev.bus_id);			return;		}	}	qdio_irq_check_sense(irq_ptr->schid, irb);#ifdef CONFIG_QDIO_DEBUG	sprintf(dbf_text, "state:%d", irq_ptr->state);	QDIO_DBF_TEXT4(0, trace, dbf_text);#endif /* CONFIG_QDIO_DEBUG */        cstat = irb->scsw.cstat;        dstat = irb->scsw.dstat;	switch (irq_ptr->state) {	case QDIO_IRQ_STATE_INACTIVE:		qdio_establish_handle_irq(cdev, cstat, dstat);		break;	case QDIO_IRQ_STATE_CLEANUP:		qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);		break;	case QDIO_IRQ_STATE_ESTABLISHED:	case QDIO_IRQ_STATE_ACTIVE:		if (cstat & SCHN_STAT_PCI) {			qdio_handle_pci(irq_ptr);			break;		}		if ((cstat&~SCHN_STAT_PCI)||dstat) {			qdio_handle_activate_check(cdev, intparm, cstat, dstat);			break;		}	default:		QDIO_PRINT_ERR("got interrupt for queues in state %d on " \			       "device %s?!\n",			       irq_ptr->state, cdev->dev.bus_id);	}	wake_up(&cdev->private->wait_q);}intqdio_synchronize(struct ccw_device *cdev, unsigned int flags,		 unsigned int queue_number){	int cc = 0;	struct qdio_q *q;	struct qdio_irq *irq_ptr;	void *ptr;#ifdef CONFIG_QDIO_DEBUG	char dbf_text[15]="SyncXXXX";#endif	irq_ptr = cdev->private->qdio_data;	if (!irq_ptr)		return -ENODEV;#ifdef CONFIG_QDIO_DEBUG	*((int*)(&dbf_text[4])) = irq_ptr->schid.sch_no;	QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);	*((int*)(&dbf_text[0]))=flags;	*((int*)(&dbf_text[4]))=queue_number;	QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);#endif /* CONFIG_QDIO_DEBUG */	if (flags&QDIO_FLAG_SYNC_INPUT) {		q=irq_ptr->input_qs[queue_number];		if (!q)			return -EINVAL;		if (!(irq_ptr->is_qebsm))			cc = do_siga_sync(q->schid, 0, q->mask);	} else if (flags&QDIO_FLAG_SYNC_OUTPUT) {		q=irq_ptr->output_qs[queue_number];		if (!q)			return -EINVAL;		if (!(irq_ptr->is_qebsm))			cc = do_siga_sync(q->schid, q->mask, 0);	} else 		return -EINVAL;	ptr=&cc;	if (cc)		QDIO_DBF_HEX3(0,trace,&ptr,sizeof(int));	return cc;}static inline voidqdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac,			    unsigned long token){	struct qdio_q *q;	int i;	unsigned int count, start_buf;	char dbf_text[15];	/*check if QEBSM is disabled */	if (!(irq_ptr->is_qebsm) || !(qdioac & 0x01)) {		irq_ptr->is_qebsm  = 0;		irq_ptr->sch_token = 0;		irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;		QDIO_DBF_TEXT0(0,setup,"noV=V");		return;	}	irq_ptr->sch_token = token;	/*input queue*/	for (i = 0; i < irq_ptr->no_input_qs;i++) {		q = irq_ptr->input_qs[i];		count = QDIO_MAX_BUFFERS_PER_Q;		start_buf = 0;		set_slsb(q, &start_buf, SLSB_P_INPUT_NOT_INIT, &count);	}	sprintf(dbf_text,"V=V:%2x",irq_ptr->is_qebsm);	QDIO_DBF_TEXT0(0,setup,dbf_text);	sprintf(dbf_text,"%8lx",irq_ptr->sch_token);	QDIO_DBF_TEXT0(0,setup,dbf_text);	/*output queue*/	for (i = 0; i < irq_ptr->no_output_qs; i++) {		q = irq_ptr->output_qs[i];		count = QDIO_MAX_BUFFERS_PER_Q;		start_buf = 0;		set_slsb(q, &start_buf, SLSB_P_OUTPUT_NOT_INIT, &count);	}}static voidqdio_get_ssqd_information(struct qdio_irq *irq_ptr){	int result;	unsigned char qdioac;	struct {		struct chsc_header request;		u16 reserved1:10;		u16 ssid:2;		u16 fmt:4;		u16 first_sch;		u16 reserved2;		u16 last_sch;		u32 reserved3;		struct chsc_header response;		u32 reserved4;		u8  flags;		u8  reserved5;		u16 sch;		u8  qfmt;		u8  parm;		u8  qdioac1;		u8  sch_class;		u8  reserved7;		u8  icnt;		u8  reserved8;		u8  ocnt;		u8 reserved9;		u8 mbccnt;		u16 qdioac2;		u64 sch_token;	} *ssqd_area;	QDIO_DBF_TEXT0(0,setup,"getssqd");	qdioac = 0;	ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);	if (!ssqd_area) {	        QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \				"SIGAs for sch x%x.\n", irq_ptr->schid.sch_no);		irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||				  CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||				  CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */		irq_ptr->is_qebsm = 0;		irq_ptr->sch_token = 0;		irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;		return;	}	ssqd_area->request = (struct chsc_header) {		.length = 0x0010,		.code   = 0x0024,	};	ssqd_area->first_sch = irq_ptr->schid.sch_no;	ssqd_area->last_sch = irq_ptr->schid.sch_no;	ssqd_area->ssid = irq_ptr->schid.ssid;	result = chsc(ssqd_area);	if (result) {		QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \				"SIGAs for sch 0.%x.%x.\n", result,				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||			CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */		irq_ptr->is_qebsm  = 0;		goto out;	}	if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {		QDIO_PRINT_WARN("response upon checking SIGA needs " \				"is 0x%x. Using all SIGAs for sch 0.%x.%x.\n",				ssqd_area->response.code,				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||			CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */		irq_ptr->is_qebsm  = 0;		goto out;	}	if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||	    !(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||	    (ssqd_area->sch != irq_ptr->schid.sch_no)) {		QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \				"using all SIGAs.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |			CHSC_FLAG_SIGA_OUTPUT_NECESSARY |			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */		irq_ptr->is_qebsm  = 0;		goto out;	}	qdioac = ssqd_area->qdioac1;out:	qdio_check_subchannel_qebsm(irq_ptr, qdioac,				    ssqd_area->sch_token);	mempool_free(ssqd_area, qdio_mempool_scssc);	irq_ptr->qdioac = qdioac;}static unsigned inttiqdio_check_chsc_availability(void){	char dbf_text[15];	if (!css_characteristics_avail)		return -EIO;	/* Check for bit 41. */	if (!css_general_characteristics.aif) {		QDIO_PRINT_WARN("Adapter interruption facility not " \				"installed.\n");		return -ENOENT;	}	/* Check for bits 107 and 108. */	if (!css_chsc_characteristics.scssc ||	    !css_chsc_characteristics.scsscf) {		QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \				"not available.\n");		return -ENOENT;	}	/* Check for OSA/FCP thin interrupts (bit 67). */	hydra_thinints = css_general_characteristics.aif_osa;	sprintf(dbf_text,"hydrati%1x", hydra_thinints);	QDIO_DBF_TEXT0(0,setup,dbf_text);#ifdef CONFIG_64BIT	/* Check for QEBSM support in general (bit 58). */	is_passthrough = css_general_characteristics.qebsm;#endif	sprintf(dbf_text,"cssQBS:%1x", is_passthrough);	QDIO_DBF_TEXT0(0,setup,dbf_text);	/* Check for aif time delay disablement fac (bit 56). If installed,	 * omit svs even under lpar (good point by rick again) */	omit_svs = css_general_characteristics.aif_tdd;	sprintf(dbf_text,"omitsvs%1x", omit_svs);	QDIO_DBF_TEXT0(0,setup,dbf_text);	return 0;}static unsigned inttiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero){	unsigned long real_addr_local_summary_bit;	unsigned long real_addr_dev_st_chg_ind;	void *ptr;	char dbf_text[15];	unsigned int resp_code;	int result;	struct {		struct chsc_header request;		u16 operation_code;		u16 reserved1;		u32 reserved2;		u32 reserved3;		u64 summary_indicator_addr;		u64 subchannel_indicator_addr;		u32 ks:4;		u32 kc:4;		u32 reserved4:21;		u32 isc:3;		u32 word_with_d_bit;		/* set to 0x10000000 to enable		 * time delay disablement facility */		u32 reserved5;		struct subchannel_id schid;		u32 reserved6[1004];		struct chsc_header response;		u32 reserved7;	} *scssc_area;	if (!irq_ptr->is_thinint_irq)		return -ENODEV;	if (reset_to_zero) {		real_addr_local_summary_bit=0;		real_addr_dev_st_chg_ind=0;	} else {		real_addr_local_summary_bit=			virt_to_phys((volatile void *)indicators);		real_addr_dev_st_chg_ind=			virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);	}	scssc_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);	if (!scssc_area) {		QDIO_PRINT_WARN("No memory for setting indicators on " \				"subchannel 0.%x.%x.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		return -ENOMEM;	}	scssc_area->request = (struct chsc_header) {		.length = 0x0fe0,		.code   = 0x0021,	};	scssc_area->operation_code = 0;        scssc_area->summary_indicator_addr = real_addr_local_summary_bit;	scssc_area->subchannel_indicator_addr = real_addr_dev_st_chg_ind;	scssc_area->ks = QDIO_STORAGE_KEY;	scssc_area->kc = QDIO_STORAGE_KEY;	scssc_area->isc = TIQDIO_THININT_ISC;	scssc_area->schid = irq_ptr->schid;	/* enables the time delay disablement facility. Don't care	 * whether it is really there (i.e. we haven't checked for	 * it) */	if (css_general_characteristics.aif_tdd)		scssc_area->word_with_d_bit = 0x10000000;	else		QDIO_PRINT_WARN("Time delay disablement facility " \				"not available\n");	result = chsc(scssc_area);	if (result) {		QDIO_PRINT_WARN("could not set indicators on irq 0.%x.%x, " \				"cc=%i.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no,result);		result = -EIO;		goto out;	}	resp_code = scssc_area->response.code;	if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {		QDIO_PRINT_WARN("response upon setting indicators " \				"is 0x%x.\n",resp_code);		sprintf(dbf_text,"sidR%4x",resp_code);		QDIO_DBF_TEXT1(0,trace,dbf_text);		QDIO_DBF_TEXT1(0,setup,dbf_text);		ptr=&scssc_area->response;		QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN);		result = -EIO;		goto out;	}	QDIO_DBF_TEXT2(0,setup,"setscind");	QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit,		      sizeof(unsigned long));	QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long));	result = 0;out:	mempool_free(scssc_area, qdio_mempool_scssc);	return result;}static unsigned inttiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target){	unsigned int resp_code;	int result;	void *ptr;	char dbf_text[15];	struct {		struct chsc_header request;		u16 operation_code;		u16 reserved1;		u32 reserved2;		u32 reserved3;		u32 reserved4[2];		u32 delay_target;		u32 reserved5[1009];		struct chsc_header response;		u32 reserved6;	} *scsscf_area;	if (!irq_ptr->is_thinint_irq)		return -ENODEV;	scsscf_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);	if (!scsscf_area) {		QDIO_PRINT_WARN("No memory for setting delay target on " \				"subchannel 0.%x.%x.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);		return -ENOMEM;	}	scsscf_area->request = (struct chsc_header) {		.length = 0x0fe0,		.code   = 0x1027,	};	scsscf_area->delay_target = delay_target<<16;	result=chsc(scsscf_area);	if (result) {		QDIO_PRINT_WARN("could not set delay target on irq 0.%x.%x, " \				"cc=%i. Continuing.\n",				irq_ptr->schid.ssid, irq_ptr->schid.sch_no,				result);		result = -EIO;		goto out;	}	resp_code = scsscf_area->response.code;	if (resp_code!=QDIO_CHSC_RESPONSE_C

⌨️ 快捷键说明

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