X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fcio%2Fqdio.c;h=6fd1940842ebd05d9185b6959fae90e37653546b;hb=a2f44b27303a5353859d77a3e96a1d3f33f56ab7;hp=f4c9779d2d4aa1a427ff78fbe40c977e0a98395a;hpb=daddc0d38b3571bed170afa273a49a0eba090c1e;p=linux-2.6.git diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index f4c9779d2..6fd194084 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -7,7 +7,7 @@ * * Copyright 2000,2002 IBM Corporation * Author(s): Utz Bacher - * 2.6 cio integration by Cornelia Huck + * 2.6 cio integration by Cornelia Huck * * Restriction: only 63 iqdio subchannels would have its own indicator, * after that, subsequent subchannels share one indicator @@ -30,7 +30,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include @@ -38,6 +37,7 @@ #include #include #include +#include #include #include @@ -46,6 +46,7 @@ #include #include +#include #include #include "cio.h" @@ -56,8 +57,6 @@ #include "ioasm.h" #include "chsc.h" -#define VERSION_QDIO_C "$Revision: 1.80 $" - /****************** MODULE PARAMETER VARIABLES ********************/ MODULE_AUTHOR("Utz Bacher "); MODULE_DESCRIPTION("QDIO base support version 2, " \ @@ -66,36 +65,38 @@ 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 ")"; +static const char version[] = "QDIO base support version 2"; +extern struct bus_type ccw_bus_type; -#ifdef QDIO_PERFORMANCE_STATS +static int qdio_performance_stats = 0; static 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 2 +static 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 QDIO_DBF_LIKE_HELL +#ifdef CONFIG_QDIO_DEBUG static debug_info_t *qdio_dbf_slsb_out; static debug_info_t *qdio_dbf_slsb_in; -#endif /* QDIO_DBF_LIKE_HELL */ +#endif /* CONFIG_QDIO_DEBUG */ /* iQDIO stuff: */ static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change during a while loop */ -static spinlock_t ttiq_list_lock=SPIN_LOCK_UNLOCKED; +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); @@ -112,10 +113,10 @@ qdio_min(int a,int b) /***************** SCRUBBER HELPER ROUTINES **********************/ -static inline volatile __u64 +static inline __u64 qdio_get_micros(void) { - return (get_clock() >> 10); /* time>>12 is microseconds */ + return (get_clock() >> 12); /* time>>12 is microseconds */ } /* @@ -136,12 +137,136 @@ 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) +/*check ccq */ +static inline int +qdio_check_ccq(struct qdio_q *q, unsigned int ccq) { - xchg((char*)slsb,value); + 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 int +qdio_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 int +qdio_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 int +qdio_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_DEBUG +static inline void +qdio_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); + } +} +#endif + +static inline int +set_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) @@ -151,11 +276,10 @@ qdio_siga_sync(struct qdio_q *q, unsigned int gpr2, 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 */ + if (qdio_performance_stats) + perf_stats.siga_syncs++; - cc = do_siga_sync(q->irq, gpr2, gpr3); + cc = do_siga_sync(q->schid, gpr2, gpr3); if (cc) QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*)); @@ -170,6 +294,23 @@ qdio_siga_sync_q(struct qdio_q *q) 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 @@ -181,15 +322,14 @@ qdio_siga_output(struct qdio_q *q) __u32 busy_bit; __u64 start_time=0; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.siga_outs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.siga_outs++; 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); + 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) @@ -217,11 +357,10 @@ qdio_siga_input(struct qdio_q *q) 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 */ + if (qdio_performance_stats) + perf_stats.siga_ins++; - cc = do_siga_input(q->irq, q->mask); + cc = do_siga_input(q->schid, q->mask); if (cc) QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*)); @@ -230,7 +369,7 @@ qdio_siga_input(struct qdio_q *q) } /* locked by the locks in qdio_activate and qdio_cleanup */ -static __u32 * volatile +static __u32 * qdio_get_indicator(void) { int i; @@ -258,7 +397,7 @@ qdio_put_indicator(__u32 *addr) atomic_dec(&spare_indicator_usecount); } -static inline volatile void +static inline void tiqdio_clear_summary_bit(__u32 *location) { QDIO_DBF_TEXT5(0,trace,"clrsummb"); @@ -267,7 +406,7 @@ tiqdio_clear_summary_bit(__u32 *location) xchg(location,0); } -static inline volatile void +static inline void tiqdio_set_summary_bit(__u32 *location) { QDIO_DBF_TEXT5(0,trace,"setsummb"); @@ -336,9 +475,11 @@ static inline int qdio_stop_polling(struct qdio_q *q) { #ifdef QDIO_USE_PROCESSING_STATE - int gsf; + 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)) + if (!atomic_xchg(&q->polling,0)) return 1; QDIO_DBF_TEXT4(0,trace,"stoppoll"); @@ -348,23 +489,29 @@ qdio_stop_polling(struct qdio_q *q) 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); + 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, so q->polling was 0 + * 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) + 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 */ - if (q->is_iqdio_q) + if (q->is_thinint_q) tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); return 0; @@ -430,18 +577,136 @@ tiqdio_clear_global_summary(void) /************************* OUTBOUND ROUTINES *******************************/ +static int +qdio_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; +} -inline static int +static int +qdio_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 int qdio_get_outbound_buffer_frontier(struct qdio_q *q) { - int f,f_mod_no; - volatile char *slsb; - int first_not_to_check; + 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; /* @@ -483,7 +748,7 @@ check_next: 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); + set_slsb(q, &f_mod_no, SLSB_P_OUTPUT_NOT_INIT, &count); /* * we increment the frontier, as this buffer @@ -509,14 +774,17 @@ out: } /* all buffers are processed */ -inline static int +static inline int qdio_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); @@ -524,10 +792,11 @@ qdio_is_outbound_q_done(struct qdio_q *q) QDIO_DBF_TEXT4(0,trace,"oqisdone"); } QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); +#endif /* CONFIG_QDIO_DEBUG */ return (no_used==0); } -inline static int +static inline int qdio_has_outbound_q_moved(struct qdio_q *q) { int i; @@ -547,14 +816,16 @@ qdio_has_outbound_q_moved(struct qdio_q *q) } } -inline static void +static inline void qdio_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; @@ -590,51 +861,57 @@ qdio_kick_outbound_q(struct qdio_q *q) result=qdio_siga_output(q); - switch (result) { - case 0: + switch (result) { + case 0: /* went smooth this time, reset timestamp */ - QDIO_DBF_TEXT3(0,trace,"cc2reslv"); - sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no, - atomic_read(&q->busy_siga_counter)); - QDIO_DBF_TEXT3(0,trace,dbf_text); - q->timing.busy_start=0; - break; - case (2|QDIO_SIGA_ERROR_B_BIT_SET): - /* cc=2 and busy bit: */ +#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 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_startirq,q->q_no, - atomic_read(&q->busy_siga_counter)); - QDIO_DBF_TEXT3(0,trace,dbf_text); - /* 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; + /* if we're in time, don't touch error_status_flags + * and siga_error */ + if (NOW-q->timing.busy_startschid.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; + } } -inline static void +static inline void qdio_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 */ @@ -644,11 +921,13 @@ qdio_kick_outbound_handler(struct qdio_q *q) 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| @@ -673,9 +952,8 @@ __qdio_outbound_processing(struct qdio_q *q) if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - o_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + o_p_c++; /* as we're sissies, we'll check next time */ if (likely(!atomic_read(&q->is_in_shutdown))) { qdio_mark_q(q); @@ -683,10 +961,10 @@ __qdio_outbound_processing(struct qdio_q *q) } return; } -#ifdef QDIO_PERFORMANCE_STATS - o_p_nc++; - perf_stats.tl_runs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + o_p_nc++; + perf_stats.tl_runs++; + } /* see comment in qdio_kick_outbound_q */ siga_attempts=atomic_read(&q->busy_siga_counter); @@ -701,12 +979,11 @@ __qdio_outbound_processing(struct qdio_q *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. + * for asynchronous queues, we better check, if the sent + * buffer is already switched from PRIMED to EMPTY. */ - if (atomic_read(&q->number_of_buffers_used)> - IQDIO_FILL_LEVEL_TO_POLL) + if ((q->queue_type == QDIO_IQDIO_QFMT_ASYNCH) && + !qdio_is_outbound_q_done(q)) qdio_mark_q(q); } else if (!q->hydra_gives_outbound_pcis) @@ -725,17 +1002,28 @@ qdio_outbound_processing(struct qdio_q *q) /************************* INBOUND ROUTINES *******************************/ -inline static int +static inline int qdio_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); + slsb=&q->slsb.acc.val[0]; f_mod_no=f=q->first_to_check; /* @@ -772,17 +1060,23 @@ check_next: * kill VM in terms of CP overhead */ if (q->siga_sync) { - set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT); + set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count); } else { - set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_PROCESSING); + /* set the previous buffer to NOT_INIT. The current + * buffer will be set to PROCESSING at the end of + * this function to avoid further interrupts. */ + if (last_position>=0) + set_slsb(q, &last_position, + SLSB_P_INPUT_NOT_INIT, &count); atomic_set(&q->polling,1); + last_position=f_mod_no; } #else /* QDIO_USE_PROCESSING_STATE */ - set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT); + set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count); #endif /* QDIO_USE_PROCESSING_STATE */ /* * not needed, as the inbound queue will be synced on the next - * siga-r + * siga-r, resp. tiqdio_is_inbound_q_done will do the siga-s */ /*SYNC_MEMORY;*/ f++; @@ -796,12 +1090,14 @@ check_next: /* P_ERROR means frontier is reached, break and report error */ case SLSB_P_INPUT_ERROR: +#ifdef CONFIG_QDIO_DEBUG sprintf(dbf_text,"inperr%2x",f_mod_no); QDIO_DBF_TEXT3(1,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256); /* kind of process the buffer */ - set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT); + set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count); if (q->qdio_error) q->error_status_flags|= @@ -814,6 +1110,10 @@ check_next: f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1); atomic_dec(&q->number_of_buffers_used); +#ifdef QDIO_USE_PROCESSING_STATE + last_position=-1; +#endif /* QDIO_USE_PROCESSING_STATE */ + break; /* everything else means frontier not changed (HALTED or so) */ @@ -823,25 +1123,31 @@ check_next: out: q->first_to_check=f_mod_no; +#ifdef QDIO_USE_PROCESSING_STATE + if (last_position>=0) + set_slsb(q, &last_position, SLSB_P_INPUT_PROCESSING, &count); +#endif /* QDIO_USE_PROCESSING_STATE */ + QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int)); return q->first_to_check; } -inline static int +static inline int qdio_has_inbound_q_moved(struct qdio_q *q) { int i; -#ifdef QDIO_PERFORMANCE_STATS static int old_pcis=0; static int old_thinints=0; - if ((old_pcis==perf_stats.pcis)&&(old_thinints==perf_stats.thinints)) - perf_stats.start_time_inbound=NOW; - else - old_pcis=perf_stats.pcis; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + if ((old_pcis==perf_stats.pcis)&& + (old_thinints==perf_stats.thinints)) + perf_stats.start_time_inbound=NOW; + else + old_pcis=perf_stats.pcis; + } i=qdio_get_inbound_buffer_frontier(q); if ( (i!=GET_SAVED_FRONTIER(q)) || @@ -861,17 +1167,24 @@ qdio_has_inbound_q_moved(struct qdio_q *q) } /* means, no more buffers to be filled */ -inline static int -iqdio_is_inbound_q_done(struct qdio_q *q) +static inline int +tiqdio_is_inbound_q_done(struct qdio_q *q) { int no_used; + unsigned int start_buf, count; + unsigned char state = 0; + struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + +#ifdef CONFIG_QDIO_DEBUG char dbf_text[15]; +#endif no_used=atomic_read(&q->number_of_buffers_used); /* propagate the change from 82 to 80 through VM */ SYNC_MEMORY; +#ifdef CONFIG_QDIO_DEBUG if (no_used) { sprintf(dbf_text,"iqisnt%02x",no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); @@ -879,15 +1192,20 @@ iqdio_is_inbound_q_done(struct qdio_q *q) QDIO_DBF_TEXT4(0,trace,"iniqisdo"); } QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); +#endif /* CONFIG_QDIO_DEBUG */ if (!no_used) return 1; - - if (!q->siga_sync) + if (!q->siga_sync && !irq->is_qebsm) /* we'll check for more primed buffers in qeth_stop_polling */ return 0; - - if (q->slsb.acc.val[q->first_to_check]!=SLSB_P_INPUT_PRIMED) + if (irq->is_qebsm) { + count = 1; + start_buf = q->first_to_check; + qdio_do_eqbs(q, &state, &start_buf, &count); + } else + state = q->slsb.acc.val[q->first_to_check]; + if (state != SLSB_P_INPUT_PRIMED) /* * nothing more to do, if next buffer is not PRIMED. * note that we did a SYNC_MEMORY before, that there @@ -910,11 +1228,17 @@ iqdio_is_inbound_q_done(struct qdio_q *q) return 0; } -inline static int +static inline int qdio_is_inbound_q_done(struct qdio_q *q) { int no_used; + unsigned int start_buf, count; + unsigned char state = 0; + struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + +#ifdef CONFIG_QDIO_DEBUG char dbf_text[15]; +#endif no_used=atomic_read(&q->number_of_buffers_used); @@ -930,8 +1254,13 @@ qdio_is_inbound_q_done(struct qdio_q *q) QDIO_DBF_TEXT4(0,trace,dbf_text); return 1; } - - if (q->slsb.acc.val[q->first_to_check]==SLSB_P_INPUT_PRIMED) { + if (irq->is_qebsm) { + count = 1; + start_buf = q->first_to_check; + qdio_do_eqbs(q, &state, &start_buf, &count); + } else + state = q->slsb.acc.val[q->first_to_check]; + if (state == SLSB_P_INPUT_PRIMED) { /* we got something to do */ QDIO_DBF_TEXT4(0,trace,"inqisntA"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); @@ -949,25 +1278,31 @@ qdio_is_inbound_q_done(struct qdio_q *q) * has (probably) not moved (see qdio_inbound_processing) */ if (NOW>GET_SAVED_TIMESTAMP(q)+q->timing.threshold) { +#ifdef CONFIG_QDIO_DEBUG QDIO_DBF_TEXT4(0,trace,"inqisdon"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ return 1; } else { +#ifdef CONFIG_QDIO_DEBUG QDIO_DBF_TEXT4(0,trace,"inqisntd"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used); QDIO_DBF_TEXT4(0,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ return 0; } } -inline static void +static inline void qdio_kick_inbound_handler(struct qdio_q *q) { int count, start, end, real_end, i; +#ifdef CONFIG_QDIO_DEBUG char dbf_text[15]; +#endif QDIO_DBF_TEXT4(0,trace,"kickinh"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); @@ -985,8 +1320,10 @@ qdio_kick_inbound_handler(struct qdio_q *q) i=(i+1)&(QDIO_MAX_BUFFERS_PER_Q-1); } +#ifdef CONFIG_QDIO_DEBUG sprintf(dbf_text,"s=%2xc=%2x",start,count); QDIO_DBF_TEXT4(0,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ if (likely(q->state==QDIO_IRQ_STATE_ACTIVE)) q->handler(q->cdev, @@ -1000,10 +1337,10 @@ qdio_kick_inbound_handler(struct qdio_q *q) q->siga_error=0; q->error_status_flags=0; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound; - perf_stats.inbound_cnt++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound; + perf_stats.inbound_cnt++; + } } static inline void @@ -1023,9 +1360,8 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) */ if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - ii_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + ii_p_c++; /* * as we might just be about to stop polling, we make * sure that we check again at least once more @@ -1033,9 +1369,8 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) tiqdio_sched_tl(); return; } -#ifdef QDIO_PERFORMANCE_STATS - ii_p_nc++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + ii_p_nc++; if (unlikely(atomic_read(&q->is_in_shutdown))) { qdio_unmark_q(q); goto out; @@ -1076,11 +1411,11 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) irq_ptr = (struct qdio_irq*)q->irq_ptr; for (i=0;ino_output_qs;i++) { oq = irq_ptr->output_qs[i]; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs--; -#endif /* QDIO_PERFORMANCE_STATS */ - if (!qdio_is_outbound_q_done(oq)) + if (!qdio_is_outbound_q_done(oq)) { + if (qdio_performance_stats) + perf_stats.tl_runs--; __qdio_outbound_processing(oq); + } } } @@ -1088,7 +1423,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) goto out; qdio_kick_inbound_handler(q); - if (iqdio_is_inbound_q_done(q)) + if (tiqdio_is_inbound_q_done(q)) if (!qdio_stop_polling(q)) { /* * we set the flags to get into the stuff next time, @@ -1117,9 +1452,8 @@ __qdio_inbound_processing(struct qdio_q *q) if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - i_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + i_p_c++; /* as we're sissies, we'll check next time */ if (likely(!atomic_read(&q->is_in_shutdown))) { qdio_mark_q(q); @@ -1127,10 +1461,10 @@ __qdio_inbound_processing(struct qdio_q *q) } return; } -#ifdef QDIO_PERFORMANCE_STATS - i_p_nc++; - perf_stats.tl_runs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + i_p_nc++; + perf_stats.tl_runs++; + } again: if (qdio_has_inbound_q_moved(q)) { @@ -1160,7 +1494,7 @@ qdio_inbound_processing(struct qdio_q *q) #ifdef QDIO_USE_PROCESSING_STATE static inline int -tiqdio_do_inbound_checks(struct qdio_q *q, int q_laps) +tiqdio_reset_processing_state(struct qdio_q *q, int q_laps) { if (!q) { tiqdio_sched_tl(); @@ -1176,9 +1510,8 @@ tiqdio_do_inbound_checks(struct qdio_q *q, int q_laps) if (unlikely(qdio_reserve_q(q))) { qdio_release_q(q); -#ifdef QDIO_PERFORMANCE_STATS - ii_p_c++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + ii_p_c++; /* * as we might just be about to stop polling, we make * sure that we check again at least once more @@ -1247,7 +1580,7 @@ again: do { int ret; - ret = tiqdio_do_inbound_checks(q, q_laps); + ret = tiqdio_reset_processing_state(q, q_laps); switch (ret) { case 0: return; @@ -1269,9 +1602,8 @@ tiqdio_tl(unsigned long data) { QDIO_DBF_TEXT4(0,trace,"iqdio_tl"); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.tl_runs++; tiqdio_inbound_checks(); } @@ -1287,21 +1619,19 @@ qdio_release_irq_memory(struct qdio_irq *irq_ptr) if (!irq_ptr->input_qs[i]) goto next; - if (irq_ptr->input_qs[i]->slib) - kfree(irq_ptr->input_qs[i]->slib); + kfree(irq_ptr->input_qs[i]->slib); kfree(irq_ptr->input_qs[i]); next: if (!irq_ptr->output_qs[i]) continue; - if (irq_ptr->output_qs[i]->slib) - kfree(irq_ptr->output_qs[i]->slib); + kfree(irq_ptr->output_qs[i]->slib); kfree(irq_ptr->output_qs[i]); } kfree(irq_ptr->qdr); - kfree(irq_ptr); + free_page((unsigned long) irq_ptr); } static void @@ -1350,16 +1680,14 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, int result=-ENOMEM; for (i=0;islib=kmalloc(PAGE_SIZE,GFP_KERNEL); + q->slib = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!q->slib) { QDIO_PRINT_ERR("kmalloc of slib failed!\n"); goto out; @@ -1369,14 +1697,12 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, } for (i=0;islib=kmalloc(PAGE_SIZE,GFP_KERNEL); if (!q->slib) { QDIO_PRINT_ERR("kmalloc of slib failed!\n"); @@ -1407,7 +1733,7 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, void *ptr; int available; - sprintf(dbf_text,"qfqs%4x",cdev->private->irq); + sprintf(dbf_text,"qfqs%4x",cdev->private->schid.sch_no); QDIO_DBF_TEXT0(0,setup,dbf_text); for (i=0;iinput_qs[i]; @@ -1427,7 +1753,7 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, q->queue_type=q_format; q->int_parm=int_parm; - q->irq=irq_ptr->irq; + q->schid = irq_ptr->schid; q->irq_ptr = irq_ptr; q->cdev = cdev; q->mask=1<<(31-i); @@ -1474,11 +1800,11 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); /* fill in slsb */ - for (j=0;jslsb.acc.val[j], - SLSB_P_INPUT_NOT_INIT); -/* q->sbal[j]->element[1].sbalf.i1.key=QDIO_STORAGE_KEY;*/ - } + if (!irq_ptr->is_qebsm) { + unsigned int count = 1; + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) + set_slsb(q, &j, SLSB_P_INPUT_NOT_INIT, &count); + } } for (i=0;isbal[j]=*(outbound_sbals_array++); q->queue_type=q_format; + if ((q->queue_type == QDIO_IQDIO_QFMT) && + (no_output_qs > 1) && + (i == no_output_qs-1)) + q->queue_type = QDIO_IQDIO_QFMT_ASYNCH; q->int_parm=int_parm; q->is_input_q=0; - q->irq=irq_ptr->irq; + q->schid = irq_ptr->schid; q->cdev = cdev; q->irq_ptr = irq_ptr; q->mask=1<<(31-i); @@ -1535,11 +1865,11 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); /* fill in slsb */ - for (j=0;jslsb.acc.val[j], - SLSB_P_OUTPUT_NOT_INIT); -/* q->sbal[j]->element[1].sbalf.i1.key=QDIO_STORAGE_KEY;*/ - } + if (!irq_ptr->is_qebsm) { + unsigned int count = 1; + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) + set_slsb(q, &j, SLSB_P_OUTPUT_NOT_INIT, &count); + } } } @@ -1584,10 +1914,10 @@ tiqdio_thinint_handler(void) { QDIO_DBF_TEXT4(0,trace,"thin_int"); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.thinints++; - perf_stats.start_time_inbound=NOW; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.thinints++; + perf_stats.start_time_inbound=NOW; + } /* SVS only when needed: * issue SVS to benefit from iqdio interrupt avoidance @@ -1603,11 +1933,13 @@ static void qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) { int i; +#ifdef CONFIG_QDIO_DEBUG char dbf_text[15]; QDIO_DBF_TEXT5(0,trace,"newstate"); - sprintf(dbf_text,"%4x%4x",irq_ptr->irq,state); + sprintf(dbf_text,"%4x%4x",irq_ptr->schid.sch_no,state); QDIO_DBF_TEXT5(0,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ irq_ptr->state=state; for (i=0;ino_input_qs;i++) @@ -1618,18 +1950,18 @@ qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) } static inline void -qdio_irq_check_sense(int irq, struct irb *irb) +qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb) { char dbf_text[15]; if (irb->esw.esw0.erw.cons) { - sprintf(dbf_text,"sens%4x",irq); + sprintf(dbf_text,"sens%4x",schid.sch_no); QDIO_DBF_TEXT2(1,trace,dbf_text); QDIO_DBF_HEX0(0,sense,irb,QDIO_DBF_SENSE_LEN); QDIO_PRINT_WARN("sense data available on qdio channel.\n"); - HEXDUMP16(WARN,"irb: ",irb); - HEXDUMP16(WARN,"sense data: ",irb->ecw); + QDIO_HEXDUMP16(WARN,"irb: ",irb); + QDIO_HEXDUMP16(WARN,"sense data: ",irb->ecw); } } @@ -1640,18 +1972,17 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) int i; struct qdio_q *q; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.pcis++; - perf_stats.start_time_inbound=NOW; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.pcis++; + perf_stats.start_time_inbound=NOW; + } for (i=0;ino_input_qs;i++) { q=irq_ptr->input_qs[i]; if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) qdio_mark_q(q); else { -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs--; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.tl_runs--; __qdio_inbound_processing(q); } } @@ -1659,11 +1990,10 @@ qdio_handle_pci(struct qdio_irq *irq_ptr) return; for (i=0;ino_output_qs;i++) { q=irq_ptr->output_qs[i]; -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.tl_runs--; -#endif /* QDIO_PERFORMANCE_STATS */ if (qdio_is_outbound_q_done(q)) continue; + if (qdio_performance_stats) + perf_stats.tl_runs--; if (!irq_ptr->sync_done_on_outb_pcis) SYNC_MEMORY; __qdio_outbound_processing(q); @@ -1709,11 +2039,13 @@ omit_handler_call: } static void -qdio_call_shutdown(void *data) +qdio_call_shutdown(struct work_struct *work) { + struct ccw_device_private *priv; struct ccw_device *cdev; - cdev = (struct ccw_device *)data; + priv = container_of(work, struct ccw_device_private, kick_work); + cdev = priv->cdev; qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); put_device(&cdev->dev); } @@ -1734,27 +2066,28 @@ qdio_timeout_handler(struct ccw_device *cdev) switch (irq_ptr->state) { case QDIO_IRQ_STATE_INACTIVE: - QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n", - irq_ptr->irq); + 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=0x%x.\n", - irq_ptr->irq); + 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 %04x killed by cio.\n", - irq_ptr->irq); + 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); + qdio_call_shutdown); queue_work(ccw_device_work, &cdev->private->kick_work); } break; @@ -1772,9 +2105,11 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) 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 " \ @@ -1809,10 +2144,12 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } } - qdio_irq_check_sense(irq_ptr->irq, irb); + 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; @@ -1850,32 +2187,38 @@ int qdio_synchronize(struct ccw_device *cdev, unsigned int flags, unsigned int queue_number) { - int cc; + int cc = 0; struct qdio_q *q; struct qdio_irq *irq_ptr; - char dbf_text[15]="SyncXXXX"; void *ptr; +#ifdef CONFIG_QDIO_DEBUG + char dbf_text[15]="SyncXXXX"; +#endif irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; - *((int*)(&dbf_text[4])) = irq_ptr->irq; +#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; - cc = do_siga_sync(q->irq, 0, q->mask); + 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; - cc = do_siga_sync(q->irq, q->mask, 0); + if (!(irq_ptr->is_qebsm)) + cc = do_siga_sync(q->schid, q->mask, 0); } else return -EINVAL; @@ -1886,15 +2229,54 @@ qdio_synchronize(struct ccw_device *cdev, unsigned int flags, return cc; } -static unsigned char -qdio_check_siga_needs(int sch) +static inline void +qdio_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 void +qdio_get_ssqd_information(struct qdio_irq *irq_ptr) { int result; unsigned char qdioac; - struct { struct chsc_header request; - u16 reserved1; + u16 reserved1:10; + u16 ssid:2; + u16 fmt:4; u16 first_sch; u16 reserved2; u16 last_sch; @@ -1905,143 +2287,126 @@ qdio_check_siga_needs(int sch) u8 reserved5; u16 sch; u8 qfmt; - u8 reserved6; - u8 qdioac; + 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; - ssqd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + 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", sch); - return CHSC_FLAG_SIGA_INPUT_NECESSARY || - CHSC_FLAG_SIGA_OUTPUT_NECESSARY || - CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ + "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 = sch; - ssqd_area->last_sch = sch; - - result=chsc(ssqd_area); + 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 x%x.\n", - result,sch); + "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 x%x.\n", - ssqd_area->response.code, sch); + "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 != sch)) { - QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \ - "using all SIGAs.\n",sch); + (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->qdioac; + qdioac = ssqd_area->qdioac1; out: - free_page ((unsigned long) ssqd_area); - return qdioac; + qdio_check_subchannel_qebsm(irq_ptr, qdioac, + ssqd_area->sch_token); + mempool_free(ssqd_area, qdio_mempool_scssc); + irq_ptr->qdioac = qdioac; } static unsigned int tiqdio_check_chsc_availability(void) { - int result; char dbf_text[15]; - struct { - struct chsc_header request; - u32 reserved1; - u32 reserved2; - u32 reserved3; - struct chsc_header response; - u32 reserved4; - u32 general_char[510]; - u32 chsc_char[518]; - } *scsc_area; - - scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scsc_area) { - QDIO_PRINT_WARN("Was not able to determine available" \ - "CHSCs due to no memory.\n"); - return -ENOMEM; - } - - scsc_area->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x0010, - }; - - result=chsc(scsc_area); - if (result) { - QDIO_PRINT_WARN("Was not able to determine " \ - "available CHSCs, cc=%i.\n", - result); - result=-EIO; - goto exit; - } + if (!css_characteristics_avail) + return -EIO; - if (scsc_area->response.code != 1) { - QDIO_PRINT_WARN("Was not able to determine " \ - "available CHSCs.\n"); - result=-EIO; - goto exit; - } /* Check for bit 41. */ - if ((scsc_area->general_char[1] & 0x00400000) != 0x00400000) { + if (!css_general_characteristics.aif) { QDIO_PRINT_WARN("Adapter interruption facility not " \ "installed.\n"); - result=-ENOENT; - goto exit; + return -ENOENT; } + /* Check for bits 107 and 108. */ - if ((scsc_area->chsc_char[3] & 0x00180000) != 0x00180000) { + if (!css_chsc_characteristics.scssc || + !css_chsc_characteristics.scsscf) { QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \ "not available.\n"); - result=-ENOENT; - goto exit; + return -ENOENT; } /* Check for OSA/FCP thin interrupts (bit 67). */ - hydra_thinints = ((scsc_area->general_char[2] & 0x10000000) - == 0x10000000); + 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 = ((scsc_area->general_char[1] & 0x00000080) - == 0x00000080); + omit_svs = css_general_characteristics.aif_tdd; sprintf(dbf_text,"omitsvs%1x", omit_svs); QDIO_DBF_TEXT0(0,setup,dbf_text); -exit: - free_page ((unsigned long) scsc_area); - return result; + return 0; } @@ -2068,8 +2433,11 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) u32 kc:4; u32 reserved4:21; u32 isc:3; - u32 reserved5[2]; - u32 subsystem_id; + 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; @@ -2088,10 +2456,11 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); } - scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + scssc_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!scssc_area) { QDIO_PRINT_WARN("No memory for setting indicators on " \ - "subchannel x%x.\n", irq_ptr->irq); + "subchannel 0.%x.%x.\n", + irq_ptr->schid.ssid, irq_ptr->schid.sch_no); return -ENOMEM; } scssc_area->request = (struct chsc_header) { @@ -2105,12 +2474,21 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) scssc_area->ks = QDIO_STORAGE_KEY; scssc_area->kc = QDIO_STORAGE_KEY; scssc_area->isc = TIQDIO_THININT_ISC; - scssc_area->subsystem_id = (1<<16) + irq_ptr->irq; + 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 x%x, " \ - "cc=%i.\n",irq_ptr->irq,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; } @@ -2134,7 +2512,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long)); result = 0; out: - free_page ((unsigned long) scssc_area); + mempool_free(scssc_area, qdio_mempool_scssc); return result; } @@ -2163,10 +2541,11 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) if (!irq_ptr->is_thinint_irq) return -ENODEV; - scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + scsscf_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!scsscf_area) { QDIO_PRINT_WARN("No memory for setting delay target on " \ - "subchannel x%x.\n", irq_ptr->irq); + "subchannel 0.%x.%x.\n", + irq_ptr->schid.ssid, irq_ptr->schid.sch_no); return -ENOMEM; } scsscf_area->request = (struct chsc_header) { @@ -2178,8 +2557,10 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) result=chsc(scsscf_area); if (result) { - QDIO_PRINT_WARN("could not set delay target on irq x%x, " \ - "cc=%i. Continuing.\n",irq_ptr->irq,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; } @@ -2198,7 +2579,7 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long)); result = 0; /* not critical */ out: - free_page ((unsigned long) scsscf_area); + mempool_free(scsscf_area, qdio_mempool_scssc); return result; } @@ -2213,7 +2594,7 @@ qdio_cleanup(struct ccw_device *cdev, int how) if (!irq_ptr) return -ENODEV; - sprintf(dbf_text,"qcln%4x",irq_ptr->irq); + sprintf(dbf_text,"qcln%4x",irq_ptr->schid.sch_no); QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text); @@ -2240,7 +2621,7 @@ qdio_shutdown(struct ccw_device *cdev, int how) down(&irq_ptr->setting_up_sema); - sprintf(dbf_text,"qsqs%4x",irq_ptr->irq); + sprintf(dbf_text,"qsqs%4x",irq_ptr->schid.sch_no); QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text); @@ -2292,6 +2673,15 @@ qdio_shutdown(struct ccw_device *cdev, int how) /* No need to wait for device no longer present. */ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); + } else if (((void *)cdev->handler != (void *)qdio_handler) && rc == 0) { + /* + * Whoever put another handler there, has to cope with the + * interrupt theirself. Might happen if qdio_shutdown was + * called on already shutdown queues, but this shouldn't have + * bad side effects. + */ + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); } else if (rc == 0) { qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP); ccw_device_set_timeout(cdev, timeout); @@ -2337,11 +2727,11 @@ qdio_free(struct ccw_device *cdev) down(&irq_ptr->setting_up_sema); - sprintf(dbf_text,"qfqs%4x",irq_ptr->irq); + sprintf(dbf_text,"qfqs%4x",irq_ptr->schid.sch_no); QDIO_DBF_TEXT1(0,trace,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text); - cdev->private->qdio_data = 0; + cdev->private->qdio_data = NULL; up(&irq_ptr->setting_up_sema); @@ -2485,13 +2875,14 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat, irq_ptr = cdev->private->qdio_data; if (cstat || (dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END))) { - sprintf(dbf_text,"ick1%4x",irq_ptr->irq); + sprintf(dbf_text,"ick1%4x",irq_ptr->schid.sch_no); QDIO_DBF_TEXT2(1,trace,dbf_text); QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int)); QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int)); QDIO_PRINT_ERR("received check condition on establish " \ - "queues on irq 0x%x (cs=x%x, ds=x%x).\n", - irq_ptr->irq,cstat,dstat); + "queues on irq 0.%x.%x (cs=x%x, ds=x%x).\n", + irq_ptr->schid.ssid, irq_ptr->schid.sch_no, + cstat,dstat); qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR); } @@ -2499,9 +2890,10 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat, QDIO_DBF_TEXT2(1,setup,"eq:no de"); QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat)); QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat)); - QDIO_PRINT_ERR("establish queues on irq %04x: didn't get " + QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: didn't get " "device end: dstat=%02x, cstat=%02x\n", - irq_ptr->irq, dstat, cstat); + irq_ptr->schid.ssid, irq_ptr->schid.sch_no, + dstat, cstat); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); return 1; } @@ -2510,10 +2902,10 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat, QDIO_DBF_TEXT2(1,setup,"eq:badio"); QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat)); QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat)); - QDIO_PRINT_ERR("establish queues on irq %04x: got " + QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: got " "the following devstat: dstat=%02x, " - "cstat=%02x\n", - irq_ptr->irq, dstat, cstat); + "cstat=%02x\n", irq_ptr->schid.ssid, + irq_ptr->schid.sch_no, dstat, cstat); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); return 1; } @@ -2528,7 +2920,7 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) irq_ptr = cdev->private->qdio_data; - sprintf(dbf_text,"qehi%4x",cdev->private->irq); + sprintf(dbf_text,"qehi%4x",cdev->private->schid.sch_no); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text); @@ -2547,7 +2939,7 @@ qdio_initialize(struct qdio_initialize *init_data) int rc; char dbf_text[15]; - sprintf(dbf_text,"qini%4x",init_data->cdev->private->irq); + sprintf(dbf_text,"qini%4x",init_data->cdev->private->schid.sch_no); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text); @@ -2568,7 +2960,7 @@ qdio_allocate(struct qdio_initialize *init_data) struct qdio_irq *irq_ptr; char dbf_text[15]; - sprintf(dbf_text,"qalc%4x",init_data->cdev->private->irq); + sprintf(dbf_text,"qalc%4x",init_data->cdev->private->schid.sch_no); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text); if ( (init_data->no_input_qs>QDIO_MAX_QUEUES_PER_IRQ) || @@ -2586,7 +2978,7 @@ qdio_allocate(struct qdio_initialize *init_data) qdio_allocate_do_dbf(init_data); /* create irq */ - irq_ptr=kmalloc(sizeof(struct qdio_irq), GFP_KERNEL | GFP_DMA); + irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); QDIO_DBF_TEXT0(0,setup,"irq_ptr:"); QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*)); @@ -2596,13 +2988,12 @@ qdio_allocate(struct qdio_initialize *init_data) return -ENOMEM; } - memset(irq_ptr,0,sizeof(struct qdio_irq)); - init_MUTEX(&irq_ptr->setting_up_sema); + /* QDR must be in DMA area since CCW data address is only 32 bit */ irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); if (!(irq_ptr->qdr)) { - kfree(irq_ptr); + free_page((unsigned long) irq_ptr); QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); return -ENOMEM; } @@ -2640,7 +3031,7 @@ int qdio_fill_irq(struct qdio_initialize *init_data) irq_ptr->int_parm=init_data->int_parm; - irq_ptr->irq = init_data->cdev->private->irq; + irq_ptr->schid = ccw_device_get_subchannel_id(init_data->cdev); irq_ptr->no_input_qs=init_data->no_input_qs; irq_ptr->no_output_qs=init_data->no_output_qs; @@ -2656,11 +3047,12 @@ int qdio_fill_irq(struct qdio_initialize *init_data) QDIO_DBF_TEXT2(0,setup,dbf_text); if (irq_ptr->is_thinint_irq) { - irq_ptr->dev_st_chg_ind=qdio_get_indicator(); + irq_ptr->dev_st_chg_ind = qdio_get_indicator(); QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*)); if (!irq_ptr->dev_st_chg_ind) { QDIO_PRINT_WARN("no indicator location available " \ - "for irq 0x%x\n",irq_ptr->irq); + "for irq 0.%x.%x\n", + irq_ptr->schid.ssid, irq_ptr->schid.sch_no); qdio_release_irq_memory(irq_ptr); return -ENOBUFS; } @@ -2705,6 +3097,10 @@ int qdio_fill_irq(struct qdio_initialize *init_data) irq_ptr->qdr->qkey=QDIO_STORAGE_KEY; /* fill in qib */ + irq_ptr->is_qebsm = is_passthrough; + if (irq_ptr->is_qebsm) + irq_ptr->qib.rflags |= QIB_RFLAGS_ENABLE_QEBSM; + irq_ptr->qib.qfmt=init_data->q_format; if (init_data->no_input_qs) irq_ptr->qib.isliba=(unsigned long)(irq_ptr->input_qs[0]->slib); @@ -2787,7 +3183,7 @@ qdio_establish(struct qdio_initialize *init_data) tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET); } - sprintf(dbf_text,"qest%4x",cdev->private->irq); + sprintf(dbf_text,"qest%4x",cdev->private->schid.sch_no); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text); @@ -2813,9 +3209,10 @@ qdio_establish(struct qdio_initialize *init_data) sprintf(dbf_text,"eq:io%4x",result); QDIO_DBF_TEXT2(1,setup,dbf_text); } - QDIO_PRINT_WARN("establish queues on irq %04x: do_IO " \ - "returned %i, next try returned %i\n", - irq_ptr->irq,result,result2); + QDIO_PRINT_WARN("establish queues on irq 0.%x.%04x: do_IO " \ + "returned %i, next try returned %i\n", + irq_ptr->schid.ssid, irq_ptr->schid.sch_no, + result, result2); result=result2; if (result) ccw_device_set_timeout(cdev, 0); @@ -2829,10 +3226,10 @@ qdio_establish(struct qdio_initialize *init_data) return result; } - wait_event_interruptible_timeout(cdev->private->wait_q, + /* Timeout is cared for already by using ccw_device_start_timeout(). */ + wait_event_interruptible(cdev->private->wait_q, irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED || - irq_ptr->state == QDIO_IRQ_STATE_ERR, - QDIO_ESTABLISH_TIMEOUT); + irq_ptr->state == QDIO_IRQ_STATE_ERR); if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED) result = 0; @@ -2842,7 +3239,7 @@ qdio_establish(struct qdio_initialize *init_data) return -EIO; } - irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq); + qdio_get_ssqd_information(irq_ptr); /* if this gets set once, we're running under VM and can omit SVSes */ if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY) omit_svs=1; @@ -2888,7 +3285,7 @@ qdio_activate(struct ccw_device *cdev, int flags) goto out; } - sprintf(dbf_text,"qact%4x", irq_ptr->irq); + sprintf(dbf_text,"qact%4x", irq_ptr->schid.sch_no); QDIO_DBF_TEXT2(0,setup,dbf_text); QDIO_DBF_TEXT2(0,trace,dbf_text); @@ -2913,9 +3310,10 @@ qdio_activate(struct ccw_device *cdev, int flags) sprintf(dbf_text,"aq:io%4x",result); QDIO_DBF_TEXT2(1,setup,dbf_text); } - QDIO_PRINT_WARN("activate queues on irq %04x: do_IO " \ - "returned %i, next try returned %i\n", - irq_ptr->irq,result,result2); + QDIO_PRINT_WARN("activate queues on irq 0.%x.%04x: do_IO " \ + "returned %i, next try returned %i\n", + irq_ptr->schid.ssid, irq_ptr->schid.sch_no, + result, result2); result=result2; } @@ -2973,30 +3371,40 @@ static inline void qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { + struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1); + if (irq->is_qebsm) { + while (count) + set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count); + return; + } for (;;) { - set_slsb(&q->slsb.acc.val[qidx],SLSB_CU_INPUT_EMPTY); + set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count); count--; if (!count) break; - qidx=(qidx+1)&(QDIO_MAX_BUFFERS_PER_Q-1); + qidx = (qidx + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1); } - - /* not necessary, as the queues are synced during the SIGA read */ - /*SYNC_MEMORY;*/ } static inline void qdio_do_qdio_fill_output(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { + struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + + qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1); + if (irq->is_qebsm) { + while (count) + set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count); + return; + } + for (;;) { - set_slsb(&q->slsb.acc.val[qidx],SLSB_CU_OUTPUT_PRIMED); + set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count); count--; if (!count) break; - qidx=(qidx+1)&(QDIO_MAX_BUFFERS_PER_Q-1); + qidx = (qidx + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1); } - - /* SIGA write will sync the queues */ - /*SYNC_MEMORY;*/ } static inline void @@ -3013,7 +3421,7 @@ do_qdio_handle_inbound(struct qdio_q *q, unsigned int callflags, if ((used_elements+count==QDIO_MAX_BUFFERS_PER_Q)&& (callflags&QDIO_FLAG_UNDER_INTERRUPT)) - atomic_swap(&q->polling,0); + atomic_xchg(&q->polling,0); if (used_elements) return; @@ -3041,21 +3449,23 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, struct qdio_buffer *buffers) { int used_elements; + unsigned int cnt, start_buf; + unsigned char state = 0; + struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; /* This is the outbound handling of queues */ -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.start_time_outbound=NOW; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.start_time_outbound=NOW; qdio_do_qdio_fill_output(q,qidx,count,buffers); used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count; if (callflags&QDIO_FLAG_DONT_SIGA) { -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; - perf_stats.outbound_cnt++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; + perf_stats.outbound_cnt++; + } return; } if (q->is_iqdio_q) { @@ -3073,15 +3483,20 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, * SYNC_MEMORY :-/ ), we try to * fast-requeue buffers */ - if (q->slsb.acc.val[(qidx+QDIO_MAX_BUFFERS_PER_Q-1) - &(QDIO_MAX_BUFFERS_PER_Q-1)]!= - SLSB_CU_OUTPUT_PRIMED) { + if (irq->is_qebsm) { + cnt = 1; + start_buf = ((qidx+QDIO_MAX_BUFFERS_PER_Q-1) & + (QDIO_MAX_BUFFERS_PER_Q-1)); + qdio_do_eqbs(q, &state, &start_buf, &cnt); + } else + state = q->slsb.acc.val[(qidx+QDIO_MAX_BUFFERS_PER_Q-1) + &(QDIO_MAX_BUFFERS_PER_Q-1) ]; + if (state != SLSB_CU_OUTPUT_PRIMED) { qdio_kick_outbound_q(q); } else { QDIO_DBF_TEXT3(0,trace, "fast-req"); -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.fast_reqs++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) + perf_stats.fast_reqs++; } } /* @@ -3092,10 +3507,10 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, __qdio_outbound_processing(q); } -#ifdef QDIO_PERFORMANCE_STATS - perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; - perf_stats.outbound_cnt++; -#endif /* QDIO_PERFORMANCE_STATS */ + if (qdio_performance_stats) { + perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; + perf_stats.outbound_cnt++; + } } /* count must be 1 in iqdio */ @@ -3105,11 +3520,12 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags, unsigned int count,struct qdio_buffer *buffers) { struct qdio_irq *irq_ptr; - +#ifdef CONFIG_QDIO_DEBUG char dbf_text[20]; - sprintf(dbf_text,"doQD%04x",cdev->private->irq); - QDIO_DBF_TEXT3(0,trace,dbf_text); + sprintf(dbf_text,"doQD%04x",cdev->private->schid.sch_no); + QDIO_DBF_TEXT3(0,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ if ( (qidx>QDIO_MAX_BUFFERS_PER_Q) || (count>QDIO_MAX_BUFFERS_PER_Q) || @@ -3123,6 +3539,7 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags, if (!irq_ptr) return -ENODEV; +#ifdef CONFIG_QDIO_DEBUG if (callflags&QDIO_FLAG_SYNC_INPUT) QDIO_DBF_HEX3(0,trace,&irq_ptr->input_qs[queue_number], sizeof(void*)); @@ -3133,6 +3550,7 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags, QDIO_DBF_TEXT3(0,trace,dbf_text); sprintf(dbf_text,"qi%02xct%02x",qidx,count); QDIO_DBF_TEXT3(0,trace,dbf_text); +#endif /* CONFIG_QDIO_DEBUG */ if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE) return -EBUSY; @@ -3150,7 +3568,6 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags, return 0; } -#ifdef QDIO_PERFORMANCE_STATS static int qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data) @@ -3166,29 +3583,29 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, _OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c); _OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c); _OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c); - _OUTP_IT("Number of tasklet runs (total) : %u\n", + _OUTP_IT("Number of tasklet runs (total) : %lu\n", perf_stats.tl_runs); _OUTP_IT("\n"); - _OUTP_IT("Number of SIGA sync's issued : %u\n", + _OUTP_IT("Number of SIGA sync's issued : %lu\n", perf_stats.siga_syncs); - _OUTP_IT("Number of SIGA in's issued : %u\n", + _OUTP_IT("Number of SIGA in's issued : %lu\n", perf_stats.siga_ins); - _OUTP_IT("Number of SIGA out's issued : %u\n", + _OUTP_IT("Number of SIGA out's issued : %lu\n", perf_stats.siga_outs); - _OUTP_IT("Number of PCIs caught : %u\n", + _OUTP_IT("Number of PCIs caught : %lu\n", perf_stats.pcis); - _OUTP_IT("Number of adapter interrupts caught : %u\n", + _OUTP_IT("Number of adapter interrupts caught : %lu\n", perf_stats.thinints); - _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA) : %u\n", + _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA) : %lu\n", perf_stats.fast_reqs); _OUTP_IT("\n"); - _OUTP_IT("Total time of all inbound actions (us) incl. UL : %u\n", + _OUTP_IT("Total time of all inbound actions (us) incl. UL : %lu\n", perf_stats.inbound_time); - _OUTP_IT("Number of inbound transfers : %u\n", + _OUTP_IT("Number of inbound transfers : %lu\n", perf_stats.inbound_cnt); - _OUTP_IT("Total time of all outbound do_QDIOs (us) : %u\n", + _OUTP_IT("Total time of all outbound do_QDIOs (us) : %lu\n", perf_stats.outbound_time); - _OUTP_IT("Number of do_QDIOs outbound : %u\n", + _OUTP_IT("Number of do_QDIOs outbound : %lu\n", perf_stats.outbound_cnt); _OUTP_IT("\n"); @@ -3196,12 +3613,10 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, } static struct proc_dir_entry *qdio_perf_proc_file; -#endif /* QDIO_PERFORMANCE_STATS */ static void qdio_add_procfs_entry(void) { -#ifdef QDIO_PERFORMANCE_STATS proc_perf_file_registration=0; qdio_perf_proc_file=create_proc_entry(QDIO_PERF, S_IFREG|0444,&proc_root); @@ -3213,20 +3628,58 @@ qdio_add_procfs_entry(void) QDIO_PRINT_WARN("was not able to register perf. " \ "proc-file (%i).\n", proc_perf_file_registration); -#endif /* QDIO_PERFORMANCE_STATS */ } static void qdio_remove_procfs_entry(void) { -#ifdef QDIO_PERFORMANCE_STATS perf_stats.tl_runs=0; if (!proc_perf_file_registration) /* means if it went ok earlier */ remove_proc_entry(QDIO_PERF,&proc_root); -#endif /* QDIO_PERFORMANCE_STATS */ } +/** + * attributes in sysfs + *****************************************************************************/ + +static ssize_t +qdio_performance_stats_show(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0); +} + +static ssize_t +qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count) +{ + char *tmp; + int i; + + i = simple_strtoul(buf, &tmp, 16); + if ((i == 0) || (i == 1)) { + if (i == qdio_performance_stats) + return count; + qdio_performance_stats = i; + if (i==0) { + /* reset perf. stat. info */ + i_p_nc = 0; + i_p_c = 0; + ii_p_nc = 0; + ii_p_c = 0; + o_p_nc = 0; + o_p_c = 0; + memset(&perf_stats, 0, sizeof(struct qdio_perf_stats)); + } + } else { + QDIO_PRINT_WARN("QDIO performance_stats: write 0 or 1 to this file!\n"); + return -EINVAL; + } + return count; +} + +static BUS_ATTR(qdio_performance_stats, 0644, qdio_performance_stats_show, + qdio_performance_stats_store); + static void tiqdio_register_thinints(void) { @@ -3258,20 +3711,20 @@ qdio_get_qdio_memory(void) for (i=1;i