⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 qdio.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * * linux/drivers/s390/cio/qdio.c * * Linux for S/390 QDIO base support, Hipersocket base support * version 2 * * Copyright 2000,2002 IBM Corporation * Author(s):             Utz Bacher <utz.bacher@de.ibm.com> * 2.6 cio integration by Cornelia Huck <cohuck@de.ibm.com> * * Restriction: only 63 iqdio subchannels would have its own indicator, * after that, subsequent subchannels share one indicator * * * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/timer.h>#include <asm/ccwdev.h>#include <asm/io.h>#include <asm/atomic.h>#include <asm/semaphore.h>#include <asm/timex.h>#include <asm/debug.h>#include <asm/qdio.h>#include "cio.h"#include "css.h"#include "device.h"#include "airq.h"#include "qdio.h"#include "ioasm.h"#include "chsc.h"#define VERSION_QDIO_C "$Revision: 1.108 $"/****************** MODULE PARAMETER VARIABLES ********************/MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");MODULE_DESCRIPTION("QDIO base support version 2, " \		   "Copyright 2000 IBM Corporation");MODULE_LICENSE("GPL");/******************** HERE WE GO ***********************************/static const char version[] = "QDIO base support version 2 ("	VERSION_QDIO_C "/" VERSION_QDIO_H  "/" VERSION_CIO_QDIO_H ")";#ifdef QDIO_PERFORMANCE_STATSstatic int proc_perf_file_registration;static unsigned long i_p_c, i_p_nc, o_p_c, o_p_nc, ii_p_c, ii_p_nc;static struct qdio_perf_stats perf_stats;#endif /* QDIO_PERFORMANCE_STATS */static int hydra_thinints;static int omit_svs;static int indicator_used[INDICATORS_PER_CACHELINE];static __u32 * volatile indicators;static __u32 volatile spare_indicator;static atomic_t spare_indicator_usecount;static debug_info_t *qdio_dbf_setup;static debug_info_t *qdio_dbf_sbal;static debug_info_t *qdio_dbf_trace;static debug_info_t *qdio_dbf_sense;#ifdef CONFIG_QDIO_DEBUGstatic debug_info_t *qdio_dbf_slsb_out;static debug_info_t *qdio_dbf_slsb_in;#endif /* CONFIG_QDIO_DEBUG *//* iQDIO stuff: */static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change						 during a while loop */static DEFINE_SPINLOCK(ttiq_list_lock);static int register_thinint_result;static void tiqdio_tl(unsigned long);static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0);/* not a macro, as one of the arguments is atomic_read */static inline int qdio_min(int a,int b){	if (a<b)		return a;	else		return b;}/***************** SCRUBBER HELPER ROUTINES **********************/static inline __u64 qdio_get_micros(void){        return (get_clock() >> 10); /* time>>12 is microseconds */}/*  * unfortunately, we can't just xchg the values; in do_QDIO we want to reserve * the q in any case, so that we'll not be interrupted when we are in * qdio_mark_tiq... shouldn't have a really bad impact, as reserving almost * ever works (last famous words)  */static inline int qdio_reserve_q(struct qdio_q *q){	return atomic_add_return(1,&q->use_count) - 1;}static inline void qdio_release_q(struct qdio_q *q){	atomic_dec(&q->use_count);}static volatile inline void qdio_set_slsb(volatile char *slsb, unsigned char value){	xchg((char*)slsb,value);}static inline int qdio_siga_sync(struct qdio_q *q, unsigned int gpr2,	       unsigned int gpr3){	int cc;	QDIO_DBF_TEXT4(0,trace,"sigasync");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));#ifdef QDIO_PERFORMANCE_STATS	perf_stats.siga_syncs++;#endif /* QDIO_PERFORMANCE_STATS */	cc = do_siga_sync(q->irq, gpr2, gpr3);	if (cc)		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));	return cc;}static inline intqdio_siga_sync_q(struct qdio_q *q){	if (q->is_input_q)		return qdio_siga_sync(q, 0, q->mask);	return qdio_siga_sync(q, q->mask, 0);}/*  * returns QDIO_SIGA_ERROR_ACCESS_EXCEPTION as cc, when SIGA returns * an access exception  */static inline int qdio_siga_output(struct qdio_q *q){	int cc;	__u32 busy_bit;	__u64 start_time=0;#ifdef QDIO_PERFORMANCE_STATS	perf_stats.siga_outs++;#endif /* QDIO_PERFORMANCE_STATS */	QDIO_DBF_TEXT4(0,trace,"sigaout");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	for (;;) {		cc = do_siga_output(q->irq, q->mask, &busy_bit);//QDIO_PRINT_ERR("cc=%x, busy=%x\n",cc,busy_bit);		if ((cc==2) && (busy_bit) && (q->is_iqdio_q)) {			if (!start_time) 				start_time=NOW;			if ((NOW-start_time)>QDIO_BUSY_BIT_PATIENCE)				break;		} else			break;	}		if ((cc==2) && (busy_bit)) 		cc |= QDIO_SIGA_ERROR_B_BIT_SET;	if (cc)		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));	return cc;}static inline int qdio_siga_input(struct qdio_q *q){	int cc;	QDIO_DBF_TEXT4(0,trace,"sigain");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));#ifdef QDIO_PERFORMANCE_STATS	perf_stats.siga_ins++;#endif /* QDIO_PERFORMANCE_STATS */	cc = do_siga_input(q->irq, q->mask);		if (cc)		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));	return cc;}/* locked by the locks in qdio_activate and qdio_cleanup */static __u32 volatile *qdio_get_indicator(void){	int i;	for (i=1;i<INDICATORS_PER_CACHELINE;i++)		if (!indicator_used[i]) {			indicator_used[i]=1;			return indicators+i;		}	atomic_inc(&spare_indicator_usecount);	return (__u32 * volatile) &spare_indicator;}/* locked by the locks in qdio_activate and qdio_cleanup */static void qdio_put_indicator(__u32 *addr){	int i;	if ( (addr) && (addr!=&spare_indicator) ) {		i=addr-indicators;		indicator_used[i]=0;	}	if (addr == &spare_indicator)		atomic_dec(&spare_indicator_usecount);}static inline volatile void tiqdio_clear_summary_bit(__u32 *location){	QDIO_DBF_TEXT5(0,trace,"clrsummb");	QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));	xchg(location,0);}static inline volatile voidtiqdio_set_summary_bit(__u32 *location){	QDIO_DBF_TEXT5(0,trace,"setsummb");	QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));	xchg(location,-1);}static inline void tiqdio_sched_tl(void){	tasklet_hi_schedule(&tiqdio_tasklet);}static inline voidqdio_mark_tiq(struct qdio_q *q){	unsigned long flags;	QDIO_DBF_TEXT4(0,trace,"mark iq");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	spin_lock_irqsave(&ttiq_list_lock,flags);	if (unlikely(atomic_read(&q->is_in_shutdown)))		goto out_unlock;	if (!q->is_input_q)		goto out_unlock;	if ((q->list_prev) || (q->list_next)) 		goto out_unlock;	if (!tiq_list) {		tiq_list=q;		q->list_prev=q;		q->list_next=q;	} else {		q->list_next=tiq_list;		q->list_prev=tiq_list->list_prev;		tiq_list->list_prev->list_next=q;		tiq_list->list_prev=q;	}	spin_unlock_irqrestore(&ttiq_list_lock,flags);	tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);	tiqdio_sched_tl();	return;out_unlock:	spin_unlock_irqrestore(&ttiq_list_lock,flags);	return;}static inline voidqdio_mark_q(struct qdio_q *q){	QDIO_DBF_TEXT4(0,trace,"mark q");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	if (unlikely(atomic_read(&q->is_in_shutdown)))		return;	tasklet_schedule(&q->tasklet);}static inline intqdio_stop_polling(struct qdio_q *q){#ifdef QDIO_USE_PROCESSING_STATE	int gsf;	if (!atomic_swap(&q->polling,0)) 		return 1;	QDIO_DBF_TEXT4(0,trace,"stoppoll");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	/* show the card that we are not polling anymore */	if (!q->is_input_q)		return 1;	gsf=GET_SAVED_FRONTIER(q);	set_slsb(&q->slsb.acc.val[(gsf+QDIO_MAX_BUFFERS_PER_Q-1)&				  (QDIO_MAX_BUFFERS_PER_Q-1)],		 SLSB_P_INPUT_NOT_INIT);	/* 	 * we don't issue this SYNC_MEMORY, as we trust Rick T and	 * moreover will not use the PROCESSING state under VM, so	 * q->polling was 0 anyway	 */	/*SYNC_MEMORY;*/	if (q->slsb.acc.val[gsf]!=SLSB_P_INPUT_PRIMED)		return 1;	/* 	 * set our summary bit again, as otherwise there is a	 * small window we can miss between resetting it and	 * checking for PRIMED state 	 */	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 inline intqdio_get_outbound_buffer_frontier(struct qdio_q *q){	int f,f_mod_no;	volatile char *slsb;	int first_not_to_check;	char dbf_text[15];	QDIO_DBF_TEXT4(0,trace,"getobfro");	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));	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->slsb.acc.val[f_mod_no], SLSB_P_OUTPUT_NOT_INIT);		/* 		 * 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)

⌨️ 快捷键说明

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