qdio.c

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

C
2,574
字号
/* * * 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 <cornelia.huck@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 <linux/mempool.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"/****************** 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";#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 is_passthrough = 0;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;#define QDIO_MEMPOOL_SCSSC_ELEMENTS 2static mempool_t *qdio_mempool_scssc;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);}/*check ccq  */static inline intqdio_check_ccq(struct qdio_q *q, unsigned int ccq){	char dbf_text[15];	if (ccq == 0 || ccq == 32 || ccq == 96)		return 0;	if (ccq == 97)		return 1;	/*notify devices immediately*/	sprintf(dbf_text,"%d", ccq);	QDIO_DBF_TEXT2(1,trace,dbf_text);	return -EIO;}/* EQBS: extract buffer states */static inline intqdio_do_eqbs(struct qdio_q *q, unsigned char *state,	     unsigned int *start, unsigned int *cnt){	struct qdio_irq *irq;	unsigned int tmp_cnt, q_no, ccq;	int rc ;	char dbf_text[15];	ccq = 0;	tmp_cnt = *cnt;	irq = (struct qdio_irq*)q->irq_ptr;	q_no = q->q_no;	if(!q->is_input_q)		q_no += irq->no_input_qs;again:	ccq = do_eqbs(irq->sch_token, state, q_no, start, cnt);	rc = qdio_check_ccq(q, ccq);	if (rc == 1) {		QDIO_DBF_TEXT5(1,trace,"eqAGAIN");		goto again;	}	if (rc < 0) {                QDIO_DBF_TEXT2(1,trace,"eqberr");                sprintf(dbf_text,"%2x,%2x,%d,%d",tmp_cnt, *cnt, ccq, q_no);                QDIO_DBF_TEXT2(1,trace,dbf_text);		q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|				QDIO_STATUS_LOOK_FOR_ERROR,				0, 0, 0, -1, -1, q->int_parm);		return 0;	}	return (tmp_cnt - *cnt);}/* SQBS: set buffer states */static inline intqdio_do_sqbs(struct qdio_q *q, unsigned char state,	     unsigned int *start, unsigned int *cnt){	struct qdio_irq *irq;	unsigned int tmp_cnt, q_no, ccq;	int rc;	char dbf_text[15];	ccq = 0;	tmp_cnt = *cnt;	irq = (struct qdio_irq*)q->irq_ptr;	q_no = q->q_no;	if(!q->is_input_q)		q_no += irq->no_input_qs;again:	ccq = do_sqbs(irq->sch_token, state, q_no, start, cnt);	rc = qdio_check_ccq(q, ccq);	if (rc == 1) {		QDIO_DBF_TEXT5(1,trace,"sqAGAIN");		goto again;	}	if (rc < 0) {                QDIO_DBF_TEXT3(1,trace,"sqberr");                sprintf(dbf_text,"%2x,%2x,%d,%d",tmp_cnt,*cnt,ccq,q_no);                QDIO_DBF_TEXT3(1,trace,dbf_text);		q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|				QDIO_STATUS_LOOK_FOR_ERROR,				0, 0, 0, -1, -1, q->int_parm);		return 0;	}	return (tmp_cnt - *cnt);}static inline intqdio_set_slsb(struct qdio_q *q, unsigned int *bufno,	      unsigned char state, unsigned int *count){	volatile char *slsb;	struct qdio_irq *irq;	irq = (struct qdio_irq*)q->irq_ptr;	if (!irq->is_qebsm) {		slsb = (char *)&q->slsb.acc.val[(*bufno)];		xchg(slsb, state);		return 1;	}	return qdio_do_sqbs(q, state, bufno, count);}#ifdef CONFIG_QDIO_DEBUGstatic inline voidqdio_trace_slsb(struct qdio_q *q){	if (q->queue_type==QDIO_TRACE_QTYPE) {		if (q->is_input_q)			QDIO_DBF_HEX2(0,slsb_in,&q->slsb,				      QDIO_MAX_BUFFERS_PER_Q);		else			QDIO_DBF_HEX2(0,slsb_out,&q->slsb,				      QDIO_MAX_BUFFERS_PER_Q);	}}#endifstatic inline intset_slsb(struct qdio_q *q, unsigned int *bufno,	 unsigned char state, unsigned int *count){	int rc;#ifdef CONFIG_QDIO_DEBUG	qdio_trace_slsb(q);#endif	rc = qdio_set_slsb(q, bufno, state, count);#ifdef CONFIG_QDIO_DEBUG	qdio_trace_slsb(q);#endif	return rc;}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->schid, 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);}static int__do_siga_output(struct qdio_q *q, unsigned int *busy_bit){       struct qdio_irq *irq;       unsigned int fc = 0;       unsigned long schid;       irq = (struct qdio_irq *) q->irq_ptr;       if (!irq->is_qebsm)	       schid = *((u32 *)&q->schid);       else {	       schid = irq->sch_token;	       fc |= 0x80;       }       return do_siga_output(schid, q->mask, busy_bit, fc);}/*  * 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, &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->schid, 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 *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 voidtiqdio_clear_summary_bit(__u32 *location){	QDIO_DBF_TEXT5(0,trace,"clrsummb");	QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));	xchg(location,0);}static inline  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       unsigned int tmp, gsf, count = 1;       unsigned char state = 0;       struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;	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;       tmp = gsf = GET_SAVED_FRONTIER(q);       tmp = ((tmp + QDIO_MAX_BUFFERS_PER_Q-1) & (QDIO_MAX_BUFFERS_PER_Q-1) );       set_slsb(q, &tmp, SLSB_P_INPUT_NOT_INIT, &count);	/* 	 * 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 (irq->is_qebsm) {               count = 1;               qdio_do_eqbs(q, &state, &gsf, &count);       } else               state = q->slsb.acc.val[gsf];       if (state != 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 

⌨️ 快捷键说明

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