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 + -
显示快捷键?