summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2008-07-17 17:16:48 +0200
committerHeiko Carstens <heiko.carstens@de.ibm.com>2008-07-17 17:22:10 +0200
commit779e6e1c724d30e0fd1baca78b852e41e3a23c1d (patch)
treeb7fc4f0f01b66c3c65226cc627edd501e00ab44f
parentdae39843f478d181da5b5e1c2c703dfcaaf838c1 (diff)
[S390] qdio: new qdio driver.
List of major changes: - split qdio driver into several files - seperation of thin interrupt code - improved handling for multiple thin interrupt devices - inbound and outbound processing now always runs in tasklet context - significant less tasklet schedules per interrupt needed - merged qebsm with non-qebsm handling - cleanup qdio interface and added kerneldoc - coding style Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com> Reviewed-by: Utz Bacher <utz.bacher@de.ibm.com> Reviewed-by: Ursula Braun <braunu@de.ibm.com> Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/qdio.c3929
-rw-r--r--drivers/s390/cio/qdio.h835
-rw-r--r--drivers/s390/cio/qdio_debug.c240
-rw-r--r--drivers/s390/cio/qdio_debug.h91
-rw-r--r--drivers/s390/cio/qdio_main.c1755
-rw-r--r--drivers/s390/cio/qdio_perf.c151
-rw-r--r--drivers/s390/cio/qdio_perf.h54
-rw-r--r--drivers/s390/cio/qdio_setup.c521
-rw-r--r--drivers/s390/cio/qdio_thinint.c380
-rw-r--r--drivers/s390/net/qeth_core.h12
-rw-r--r--drivers/s390/net/qeth_core_main.c87
-rw-r--r--drivers/s390/net/qeth_l2_main.c26
-rw-r--r--drivers/s390/net/qeth_l3_main.c25
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c12
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h2
-rw-r--r--drivers/s390/scsi/zfcp_ext.h5
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c42
-rw-r--r--include/asm-s390/qdio.h646
-rw-r--r--include/asm-s390/setup.h2
20 files changed, 3874 insertions, 4943 deletions
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index 91e9e3f3073a..bd79bd165396 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -9,4 +9,6 @@ ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o
+
+qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_perf.o qdio_setup.o
obj-$(CONFIG_QDIO) += qdio.o
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
deleted file mode 100644
index 2bf36e14b102..000000000000
--- a/drivers/s390/cio/qdio.c
+++ /dev/null
@@ -1,3929 +0,0 @@
-/*
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-#include <linux/timer.h>
-#include <linux/mempool.h>
-#include <linux/semaphore.h>
-
-#include <asm/ccwdev.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
-#include <asm/timex.h>
-
-#include <asm/debug.h>
-#include <asm/s390_rdev.h>
-#include <asm/qdio.h>
-#include <asm/airq.h>
-
-#include "cio.h"
-#include "css.h"
-#include "device.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";
-
-static int qdio_performance_stats = 0;
-static int proc_perf_file_registration;
-static struct qdio_perf_stats perf_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 struct kmem_cache *qdio_q_cache;
-
-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_DEBUG
-static 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 void *tiqdio_ind;
-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 **********************/
-#ifdef CONFIG_64BIT
-static inline void qdio_perf_stat_inc(atomic64_t *count)
-{
- if (qdio_performance_stats)
- atomic64_inc(count);
-}
-
-static inline void qdio_perf_stat_dec(atomic64_t *count)
-{
- if (qdio_performance_stats)
- atomic64_dec(count);
-}
-#else /* CONFIG_64BIT */
-static inline void qdio_perf_stat_inc(atomic_t *count)
-{
- if (qdio_performance_stats)
- atomic_inc(count);
-}
-
-static inline void qdio_perf_stat_dec(atomic_t *count)
-{
- if (qdio_performance_stats)
- atomic_dec(count);
-}
-#endif /* CONFIG_64BIT */
-
-static inline __u64
-qdio_get_micros(void)
-{
- return (get_clock() >> 12); /* 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 int
-qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
-{
- char dbf_text[15];
-
- if (ccq == 0 || ccq == 32)
- return 0;
- if (ccq == 96 || 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 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 ((ccq == 96) && (tmp_cnt != *cnt))
- rc = 0;
- 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 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",tmp_cnt,*cnt);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
- sprintf(dbf_text,"%d,%d",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)
-{
- int cc;
-
- QDIO_DBF_TEXT4(0,trace,"sigasync");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- qdio_perf_stat_inc(&perf_stats.siga_syncs);
-
- cc = do_siga_sync(q->schid, gpr2, gpr3);
- if (cc)
- QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
-
- return cc;
-}
-
-static inline int
-qdio_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 int
-qdio_siga_output(struct qdio_q *q)
-{
- int cc;
- __u32 busy_bit;
- __u64 start_time=0;
-
- qdio_perf_stat_inc(&perf_stats.siga_outs);
-
- 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 int
-qdio_siga_input(struct qdio_q *q)
-{
- int cc;
-
- QDIO_DBF_TEXT4(0,trace,"sigain");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- qdio_perf_stat_inc(&perf_stats.siga_ins);
-
- 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 = 0; 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 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 void
-tiqdio_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 void
-qdio_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 void
-qdio_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 int
-qdio_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_xchg(&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
- */
- 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 void
-qdio_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 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;
-}
-
-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 int
-qdio_get_outbound_buffer_frontier(struct qdio_q *q)
-{
- struct qdio_irq *irq;
- volatile char *slsb;
- unsigned int count = 1;
- int first_not_to_check, f, f_mod_no;
- char dbf_text[15];
-
- QDIO_DBF_TEXT4(0,trace,"getobfro");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- irq = (struct qdio_irq *) q->irq_ptr;
- if (irq->is_qebsm)
- return qdio_qebsm_get_outbound_buffer_frontier(q);
-
- slsb=&q->slsb.acc.val[0];
- f_mod_no=f=q->first_to_check;
- /*
- * f points to already processed elements, so f+no_used is correct...
- * ... but: we don't check 128 buffers, as otherwise
- * qdio_has_outbound_q_moved would return 0
- */
- first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used),
- (QDIO_MAX_BUFFERS_PER_Q-1));
-
- if (((!q->is_iqdio_q) && (!q->hydra_gives_outbound_pcis)) ||
- (q->queue_type == QDIO_IQDIO_QFMT_ASYNCH))
- SYNC_MEMORY;
-
-check_next:
- if (f==first_not_to_check)
- goto out;
-
- switch(slsb[f_mod_no]) {
-
- /* the adapter has not fetched the output yet */
- case SLSB_CU_OUTPUT_PRIMED:
- QDIO_DBF_TEXT5(0,trace,"outpprim");
- break;
-
- /* the adapter got it */
- case SLSB_P_OUTPUT_EMPTY:
- atomic_dec(&q->number_of_buffers_used);
- f++;
- f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1);
- QDIO_DBF_TEXT5(0,trace,"outpempt");
- goto check_next;
-
- case SLSB_P_OUTPUT_ERROR:
- QDIO_DBF_TEXT3(0,trace,"outperr");
- sprintf(dbf_text,"%x-%x-%x",f_mod_no,
- q->sbal[f_mod_no]->element[14].sbalf.value,
- q->sbal[f_mod_no]->element[15].sbalf.value);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
- QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256);
-
- /* kind of process the buffer */
- set_slsb(q, &f_mod_no, SLSB_P_OUTPUT_NOT_INIT, &count);
-
- /*
- * we increment the frontier, as this buffer
- * was processed obviously
- */
- atomic_dec(&q->number_of_buffers_used);
- f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
-
- if (q->qdio_error)
- q->error_status_flags|=
- QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
- q->qdio_error=SLSB_P_OUTPUT_ERROR;
- q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;
-
- break;
-
- /* no new buffers */
- default:
- QDIO_DBF_TEXT5(0,trace,"outpni");
- }
-out:
- return (q->first_to_check=f_mod_no);
-}
-
-/* all buffers are processed */
-static 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);
- } else {
- QDIO_DBF_TEXT4(0,trace,"oqisdone");
- }
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-#endif /* CONFIG_QDIO_DEBUG */
- return (no_used==0);
-}
-
-static int
-qdio_has_outbound_q_moved(struct qdio_q *q)
-{
- int i;
-
- i=qdio_get_outbound_buffer_frontier(q);
-
- if ( (i!=GET_SAVED_FRONTIER(q)) ||
- (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
- SAVE_FRONTIER(q,i);
- QDIO_DBF_TEXT4(0,trace,"oqhasmvd");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 1;
- } else {
- QDIO_DBF_TEXT4(0,trace,"oqhsntmv");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 0;
- }
-}
-
-static 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;
-
- /* here's the story with cc=2 and busy bit set (thanks, Rick):
- * VM's CP could present us cc=2 and busy bit set on SIGA-write
- * during reconfiguration of their Guest LAN (only in HIPERS mode,
- * QDIO mode is asynchronous -- cc=2 and busy bit there will take
- * the queues down immediately; and not being under VM we have a
- * problem on cc=2 and busy bit set right away).
- *
- * Therefore qdio_siga_output will try for a short time constantly,
- * if such a condition occurs. If it doesn't change, it will
- * increase the busy_siga_counter and save the timestamp, and
- * schedule the queue for later processing (via mark_q, using the
- * queue tasklet). __qdio_outbound_processing will check out the
- * counter. If non-zero, it will call qdio_kick_outbound_q as often
- * as the value of the counter. This will attempt further SIGA
- * instructions. For each successful SIGA, the counter is
- * decreased, for failing SIGAs the counter remains the same, after
- * all.
- * After some time of no movement, qdio_kick_outbound_q will
- * finally fail and reflect corresponding error codes to call
- * the upper layer module and have it take the queues down.
- *
- * Note that this is a change from the original HiperSockets design
- * (saying cc=2 and busy bit means take the queues down), but in
- * these days Guest LAN didn't exist... excessive cc=2 with busy bit
- * conditions will still take the queues down, but the threshold is
- * higher due to the Guest LAN environment.
- */
-
-
- result=qdio_siga_output(q);
-
- switch (result) {
- case 0:
- /* went smooth this time, reset timestamp */
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT3(0,trace,"cc2reslv");
- sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,
- atomic_read(&q->busy_siga_counter));
- QDIO_DBF_TEXT3(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- q->timing.busy_start=0;
- break;
- case (2|QDIO_SIGA_ERROR_B_BIT_SET):
- /* cc=2 and busy bit: */
- atomic_inc(&q->busy_siga_counter);
-
- /* if the last siga was successful, save
- * timestamp here */
- if (!q->timing.busy_start)
- q->timing.busy_start=NOW;
-
- /* if we're in time, don't touch error_status_flags
- * and siga_error */
- if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) {
- qdio_mark_q(q);
- break;
- }
- QDIO_DBF_TEXT2(0,trace,"cc2REPRT");
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,
- atomic_read(&q->busy_siga_counter));
- QDIO_DBF_TEXT3(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- /* else fallthrough and report error */
- default:
- /* for plain cc=1, 2 or 3: */
- if (q->siga_error)
- q->error_status_flags|=
- QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
- q->error_status_flags|=
- QDIO_STATUS_LOOK_FOR_ERROR;
- q->siga_error=result;
- }
-}
-
-static 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 */
- real_end = GET_SAVED_FRONTIER(q);
- end = (real_end+QDIO_MAX_BUFFERS_PER_Q-1)&
- (QDIO_MAX_BUFFERS_PER_Q-1);
- count = (end+QDIO_MAX_BUFFERS_PER_Q+1-start)&
- (QDIO_MAX_BUFFERS_PER_Q-1);
-
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0,trace,"kickouth");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- sprintf(dbf_text,"s=%2xc=%2x",start,count);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (q->state==QDIO_IRQ_STATE_ACTIVE)
- q->handler(q->cdev,QDIO_STATUS_OUTBOUND_INT|
- q->error_status_flags,
- q->qdio_error,q->siga_error,q->q_no,start,count,
- q->int_parm);
-
- /* for the next time: */
- q->first_element_to_kick=real_end;
- q->qdio_error=0;
- q->siga_error=0;
- q->error_status_flags=0;
-}
-
-static void
-__qdio_outbound_processing(struct qdio_q *q)
-{
- int siga_attempts;
-
- QDIO_DBF_TEXT4(0,trace,"qoutproc");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- if (unlikely(qdio_reserve_q(q))) {
- qdio_release_q(q);
- qdio_perf_stat_inc(&perf_stats.outbound_tl_runs_resched);
- /* as we're sissies, we'll check next time */
- if (likely(!atomic_read(&q->is_in_shutdown))) {
- qdio_mark_q(q);
- QDIO_DBF_TEXT4(0,trace,"busy,agn");
- }
- return;
- }
- qdio_perf_stat_inc(&perf_stats.outbound_tl_runs);
- qdio_perf_stat_inc(&perf_stats.tl_runs);
-
- /* see comment in qdio_kick_outbound_q */
- siga_attempts=atomic_read(&q->busy_siga_counter);
- while (siga_attempts) {
- atomic_dec(&q->busy_siga_counter);
- qdio_kick_outbound_q(q);
- siga_attempts--;
- }
-
- if (qdio_has_outbound_q_moved(q))
- qdio_