qdio.c

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

C
2,574
字号
	 */	if (q->is_thinint_q)		tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);	return 0;#else /* QDIO_USE_PROCESSING_STATE */	return 1;#endif /* QDIO_USE_PROCESSING_STATE */}/*  * see the comment in do_QDIO and before qdio_reserve_q about the * sophisticated locking outside of unmark_q, so that we don't need to * disable the interrupts :-) */static inline voidqdio_unmark_q(struct qdio_q *q){	unsigned long flags;	QDIO_DBF_TEXT4(0,trace,"unmark q");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	if ((!q->list_prev)||(!q->list_next))		return;	if ((q->is_thinint_q)&&(q->is_input_q)) {		/* iQDIO */		spin_lock_irqsave(&ttiq_list_lock,flags);		/* in case cleanup has done this already and simultanously		 * qdio_unmark_q is called from the interrupt handler, we've		 * got to check this in this specific case again */		if ((!q->list_prev)||(!q->list_next))			goto out;		if (q->list_next==q) {			/* q was the only interesting q */			tiq_list=NULL;			q->list_next=NULL;			q->list_prev=NULL;		} else {			q->list_next->list_prev=q->list_prev;			q->list_prev->list_next=q->list_next;			tiq_list=q->list_next;			q->list_next=NULL;			q->list_prev=NULL;		}out:		spin_unlock_irqrestore(&ttiq_list_lock,flags);	}}static inline unsigned long tiqdio_clear_global_summary(void){	unsigned long time;	QDIO_DBF_TEXT5(0,trace,"clrglobl");		time = do_clear_global_summary();	QDIO_DBF_HEX5(0,trace,&time,sizeof(unsigned long));	return time;}/************************* OUTBOUND ROUTINES *******************************/static intqdio_qebsm_get_outbound_buffer_frontier(struct qdio_q *q){        struct qdio_irq *irq;        unsigned char state;        unsigned int cnt, count, ftc;        irq = (struct qdio_irq *) q->irq_ptr;        if ((!q->is_iqdio_q) && (!q->hydra_gives_outbound_pcis))                SYNC_MEMORY;        ftc = q->first_to_check;        count = qdio_min(atomic_read(&q->number_of_buffers_used),                        (QDIO_MAX_BUFFERS_PER_Q-1));        if (count == 0)                return q->first_to_check;        cnt = qdio_do_eqbs(q, &state, &ftc, &count);        if (cnt == 0)                return q->first_to_check;        switch (state) {        case SLSB_P_OUTPUT_ERROR:                QDIO_DBF_TEXT3(0,trace,"outperr");                atomic_sub(cnt , &q->number_of_buffers_used);                if (q->qdio_error)                        q->error_status_flags |=                                QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;                q->qdio_error = SLSB_P_OUTPUT_ERROR;                q->error_status_flags |= QDIO_STATUS_LOOK_FOR_ERROR;                q->first_to_check = ftc;                break;        case SLSB_P_OUTPUT_EMPTY:                QDIO_DBF_TEXT5(0,trace,"outpempt");                atomic_sub(cnt, &q->number_of_buffers_used);                q->first_to_check = ftc;                break;        case SLSB_CU_OUTPUT_PRIMED:                /* all buffers primed */                QDIO_DBF_TEXT5(0,trace,"outpprim");                break;        default:                break;        }        QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));        return q->first_to_check;}static intqdio_qebsm_get_inbound_buffer_frontier(struct qdio_q *q){        struct qdio_irq *irq;        unsigned char state;        int tmp, ftc, count, cnt;        char dbf_text[15];        irq = (struct qdio_irq *) q->irq_ptr;        ftc = q->first_to_check;        count = qdio_min(atomic_read(&q->number_of_buffers_used),                        (QDIO_MAX_BUFFERS_PER_Q-1));        if (count == 0)                 return q->first_to_check;        cnt = qdio_do_eqbs(q, &state, &ftc, &count);        if (cnt == 0)                 return q->first_to_check;        switch (state) {        case SLSB_P_INPUT_ERROR :#ifdef CONFIG_QDIO_DEBUG                QDIO_DBF_TEXT3(1,trace,"inperr");                sprintf(dbf_text,"%2x,%2x",ftc,count);                QDIO_DBF_TEXT3(1,trace,dbf_text);#endif /* CONFIG_QDIO_DEBUG */                if (q->qdio_error)                        q->error_status_flags |=                                QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;                q->qdio_error = SLSB_P_INPUT_ERROR;                q->error_status_flags |= QDIO_STATUS_LOOK_FOR_ERROR;                atomic_sub(cnt, &q->number_of_buffers_used);                q->first_to_check = ftc;                break;        case SLSB_P_INPUT_PRIMED :                QDIO_DBF_TEXT3(0,trace,"inptprim");                sprintf(dbf_text,"%2x,%2x",ftc,count);                QDIO_DBF_TEXT3(1,trace,dbf_text);                tmp = 0;                ftc = q->first_to_check;#ifdef QDIO_USE_PROCESSING_STATE		if (cnt > 1) {			cnt -= 1;			tmp = set_slsb(q, &ftc, SLSB_P_INPUT_NOT_INIT, &cnt);			if (!tmp)				break;		}		cnt = 1;		tmp += set_slsb(q, &ftc,			       SLSB_P_INPUT_PROCESSING, &cnt);		atomic_set(&q->polling, 1);#else                tmp = set_slsb(q, &ftc, SLSB_P_INPUT_NOT_INIT, &cnt);#endif                atomic_sub(tmp, &q->number_of_buffers_used);                q->first_to_check = ftc;                break;        case SLSB_CU_INPUT_EMPTY:        case SLSB_P_INPUT_NOT_INIT:        case SLSB_P_INPUT_PROCESSING:                QDIO_DBF_TEXT5(0,trace,"inpnipro");                break;        default:                break;        }        QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));        return q->first_to_check;}static inline intqdio_get_outbound_buffer_frontier(struct qdio_q *q){	struct qdio_irq *irq;        volatile char *slsb;        unsigned int count = 1;        int first_not_to_check, f, f_mod_no;	char dbf_text[15];	QDIO_DBF_TEXT4(0,trace,"getobfro");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	irq = (struct qdio_irq *) q->irq_ptr;	if (irq->is_qebsm)		return qdio_qebsm_get_outbound_buffer_frontier(q);	slsb=&q->slsb.acc.val[0];	f_mod_no=f=q->first_to_check;	/* 	 * f points to already processed elements, so f+no_used is correct...	 * ... but: we don't check 128 buffers, as otherwise	 * qdio_has_outbound_q_moved would return 0 	 */	first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used),				      (QDIO_MAX_BUFFERS_PER_Q-1));	if ((!q->is_iqdio_q)&&(!q->hydra_gives_outbound_pcis))		SYNC_MEMORY;check_next:	if (f==first_not_to_check) 		goto out;	switch(slsb[f_mod_no]) {        /* the adapter has not fetched the output yet */	case SLSB_CU_OUTPUT_PRIMED:		QDIO_DBF_TEXT5(0,trace,"outpprim");		break;	/* the adapter got it */	case SLSB_P_OUTPUT_EMPTY:		atomic_dec(&q->number_of_buffers_used);		f++;		f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1);		QDIO_DBF_TEXT5(0,trace,"outpempt");		goto check_next;	case SLSB_P_OUTPUT_ERROR:		QDIO_DBF_TEXT3(0,trace,"outperr");		sprintf(dbf_text,"%x-%x-%x",f_mod_no,			q->sbal[f_mod_no]->element[14].sbalf.value,			q->sbal[f_mod_no]->element[15].sbalf.value);		QDIO_DBF_TEXT3(1,trace,dbf_text);		QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256);		/* kind of process the buffer */		set_slsb(q, &f_mod_no, SLSB_P_OUTPUT_NOT_INIT, &count);		/* 		 * we increment the frontier, as this buffer		 * was processed obviously 		 */		atomic_dec(&q->number_of_buffers_used);		f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);		if (q->qdio_error)			q->error_status_flags|=				QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;		q->qdio_error=SLSB_P_OUTPUT_ERROR;		q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;		break;	/* no new buffers */	default:		QDIO_DBF_TEXT5(0,trace,"outpni");	}out:	return (q->first_to_check=f_mod_no);}/* all buffers are processed */static inline intqdio_is_outbound_q_done(struct qdio_q *q){	int no_used;#ifdef CONFIG_QDIO_DEBUG	char dbf_text[15];#endif	no_used=atomic_read(&q->number_of_buffers_used);#ifdef CONFIG_QDIO_DEBUG	if (no_used) {		sprintf(dbf_text,"oqisnt%02x",no_used);		QDIO_DBF_TEXT4(0,trace,dbf_text);	} else {		QDIO_DBF_TEXT4(0,trace,"oqisdone");	}	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));#endif /* CONFIG_QDIO_DEBUG */	return (no_used==0);}static inline intqdio_has_outbound_q_moved(struct qdio_q *q){	int i;	i=qdio_get_outbound_buffer_frontier(q);	if ( (i!=GET_SAVED_FRONTIER(q)) ||	     (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {		SAVE_FRONTIER(q,i);		QDIO_DBF_TEXT4(0,trace,"oqhasmvd");		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));		return 1;	} else {		QDIO_DBF_TEXT4(0,trace,"oqhsntmv");		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));		return 0;	}}static inline voidqdio_kick_outbound_q(struct qdio_q *q){	int result;#ifdef CONFIG_QDIO_DEBUG	char dbf_text[15];	QDIO_DBF_TEXT4(0,trace,"kickoutq");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));#endif /* CONFIG_QDIO_DEBUG */	if (!q->siga_out)		return;	/* here's the story with cc=2 and busy bit set (thanks, Rick):	 * VM's CP could present us cc=2 and busy bit set on SIGA-write	 * during reconfiguration of their Guest LAN (only in HIPERS mode,	 * QDIO mode is asynchronous -- cc=2 and busy bit there will take	 * the queues down immediately; and not being under VM we have a	 * problem on cc=2 and busy bit set right away).	 *	 * Therefore qdio_siga_output will try for a short time constantly,	 * if such a condition occurs. If it doesn't change, it will	 * increase the busy_siga_counter and save the timestamp, and	 * schedule the queue for later processing (via mark_q, using the	 * queue tasklet). __qdio_outbound_processing will check out the	 * counter. If non-zero, it will call qdio_kick_outbound_q as often	 * as the value of the counter. This will attempt further SIGA	 * instructions. For each successful SIGA, the counter is	 * decreased, for failing SIGAs the counter remains the same, after	 * all.	 * After some time of no movement, qdio_kick_outbound_q will	 * finally fail and reflect corresponding error codes to call	 * the upper layer module and have it take the queues down.	 *	 * Note that this is a change from the original HiperSockets design	 * (saying cc=2 and busy bit means take the queues down), but in	 * these days Guest LAN didn't exist... excessive cc=2 with busy bit	 * conditions will still take the queues down, but the threshold is	 * higher due to the Guest LAN environment.	 */	result=qdio_siga_output(q);	switch (result) {	case 0:		/* went smooth this time, reset timestamp */#ifdef CONFIG_QDIO_DEBUG		QDIO_DBF_TEXT3(0,trace,"cc2reslv");		sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,			atomic_read(&q->busy_siga_counter));		QDIO_DBF_TEXT3(0,trace,dbf_text);#endif /* CONFIG_QDIO_DEBUG */		q->timing.busy_start=0;		break;	case (2|QDIO_SIGA_ERROR_B_BIT_SET):		/* cc=2 and busy bit: */		atomic_inc(&q->busy_siga_counter);		/* if the last siga was successful, save		 * timestamp here */		if (!q->timing.busy_start)			q->timing.busy_start=NOW;		/* if we're in time, don't touch error_status_flags		 * and siga_error */		if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) {			qdio_mark_q(q);			break;		}		QDIO_DBF_TEXT2(0,trace,"cc2REPRT");#ifdef CONFIG_QDIO_DEBUG		sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,			atomic_read(&q->busy_siga_counter));		QDIO_DBF_TEXT3(0,trace,dbf_text);#endif /* CONFIG_QDIO_DEBUG */		/* else fallthrough and report error */	default:		/* for plain cc=1, 2 or 3: */		if (q->siga_error)			q->error_status_flags|=				QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;		q->error_status_flags|=			QDIO_STATUS_LOOK_FOR_ERROR;		q->siga_error=result;	}}static inline voidqdio_kick_outbound_handler(struct qdio_q *q){	int start, end, real_end, count;#ifdef CONFIG_QDIO_DEBUG	char dbf_text[15];#endif	start = q->first_element_to_kick;	/* last_move_ftc was just updated */	real_end = GET_SAVED_FRONTIER(q);	end = (real_end+QDIO_MAX_BUFFERS_PER_Q-1)&		(QDIO_MAX_BUFFERS_PER_Q-1);	count = (end+QDIO_MAX_BUFFERS_PER_Q+1-start)&		(QDIO_MAX_BUFFERS_PER_Q-1);#ifdef CONFIG_QDIO_DEBUG	QDIO_DBF_TEXT4(0,trace,"kickouth");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	sprintf(dbf_text,"s=%2xc=%2x",start,count);	QDIO_DBF_TEXT4(0,trace,dbf_text);#endif /* CONFIG_QDIO_DEBUG */	if (q->state==QDIO_IRQ_STATE_ACTIVE)		q->handler(q->cdev,QDIO_STATUS_OUTBOUND_INT|			   q->error_status_flags,			   q->qdio_error,q->siga_error,q->q_no,start,count,			   q->int_parm);	/* for the next time: */	q->first_element_to_kick=real_end;	q->qdio_error=0;	q->siga_error=0;	q->error_status_flags=0;}static inline void__qdio_outbound_processing(struct qdio_q *q){	int siga_attempts;	QDIO_DBF_TEXT4(0,trace,"qoutproc");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	if (unlikely(qdio_reserve_q(q))) {		qdio_release_q(q);#ifdef QDIO_PERFORMANCE_STATS		o_p_c++;#endif /* QDIO_PERFORMANCE_STATS */		/* as we're sissies, we'll check next time */		if (likely(!atomic_read(&q->is_in_shutdown))) {			qdio_mark_q(q);			QDIO_DBF_TEXT4(0,trace,"busy,agn");		}		return;	}#ifdef QDIO_PERFORMANCE_STATS	o_p_nc++;	perf_stats.tl_runs++;#endif /* QDIO_PERFORMANCE_STATS */	/* see comment in qdio_kick_outbound_q */	siga_attempts=atomic_read(&q->busy_siga_counter);	while (siga_attempts) {		atomic_dec(&q->busy_siga_counter);		qdio_kick_outbound_q(q);		siga_attempts--;	}	if (qdio_has_outbound_q_moved(q))		qdio_kick_outbound_handler(q);	if (q->is_iqdio_q) {		/* 		 * for asynchronous queues, we better check, if the fill		 * level is too high. for synchronous queues, the fill		 * level will never be that high. 		 */		if (atomic_read(&q->number_of_buffers_used)>		    IQDIO_FILL_LEVEL_TO_POLL)			qdio_mark_q(q);	} else if (!q->hydra_gives_outbound_pcis)		if (!qdio_is_outbound_q_done(q))			qdio_mark_q(q);	qdio_release_q(q);}static voidqdio_outbound_processing(struct qdio_q *q){	__qdio_outbound_processing(q);}/************************* INBOUND ROUTINES *******************************/static inline intqdio_get_inbound_buffer_frontier(struct qdio_q *q){	struct qdio_irq *irq;	int f,f_mod_no;	volatile char *slsb;	unsigned int count = 1;	int first_not_to_check;#ifdef CONFIG_QDIO_DEBUG	char dbf_text[15];#endif /* CONFIG_QDIO_DEBUG */#ifdef QDIO_USE_PROCESSING_STATE	int last_position=-1;#endif /* QDIO_USE_PROCESSING_STATE */	QDIO_DBF_TEXT4(0,trace,"getibfro");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	irq = (struct qdio_irq *) q->irq_ptr;	if (irq->is_qebsm)		return qdio_qebsm_get_inbound_buffer_frontier(q);

⌨️ 快捷键说明

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