include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / s390 / cio / qdio_main.c
index 31b9318..88be7b9 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/timer.h>
 #include <linux/delay.h>
+#include <linux/gfp.h>
 #include <asm/atomic.h>
 #include <asm/debug.h>
 #include <asm/qdio.h>
@@ -22,7 +23,6 @@
 #include "device.h"
 #include "qdio.h"
 #include "qdio_debug.h"
-#include "qdio_perf.h"
 
 MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>,"\
        "Jan Glauber <jang@linux.vnet.ibm.com>");
@@ -126,7 +126,7 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
        int rc;
 
        BUG_ON(!q->irq_ptr->sch_token);
-       qdio_perf_stat_inc(&perf_stats.debug_eqbs_all);
+       qperf_inc(q, eqbs);
 
        if (!q->is_input_q)
                nr += q->irq_ptr->nr_input_qs;
@@ -139,7 +139,7 @@ again:
         * buffers later.
         */
        if ((ccq == 96) && (count != tmp_count)) {
-               qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete);
+               qperf_inc(q, eqbs_partial);
                return (count - tmp_count);
        }
 
@@ -182,7 +182,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
                return 0;
 
        BUG_ON(!q->irq_ptr->sch_token);
-       qdio_perf_stat_inc(&perf_stats.debug_sqbs_all);
+       qperf_inc(q, sqbs);
 
        if (!q->is_input_q)
                nr += q->irq_ptr->nr_input_qs;
@@ -191,7 +191,7 @@ again:
        rc = qdio_check_ccq(q, ccq);
        if (rc == 1) {
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
-               qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete);
+               qperf_inc(q, sqbs_partial);
                goto again;
        }
        if (rc < 0) {
@@ -231,8 +231,8 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
        return i;
 }
 
-inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
-                 unsigned char *state, int auto_ack)
+static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
+                               unsigned char *state, int auto_ack)
 {
        return get_buf_states(q, bufnr, state, 1, auto_ack);
 }
@@ -276,7 +276,7 @@ void qdio_init_buf_states(struct qdio_irq *irq_ptr)
                               QDIO_MAX_BUFFERS_PER_Q);
 }
 
-static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
+static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output,
                          unsigned int input)
 {
        int cc;
@@ -285,7 +285,7 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
                return 0;
 
        DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr);
-       qdio_perf_stat_inc(&perf_stats.siga_sync);
+       qperf_inc(q, siga_sync);
 
        cc = do_siga_sync(q->irq_ptr->schid, output, input);
        if (cc)
@@ -293,7 +293,7 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
        return cc;
 }
 
-inline int qdio_siga_sync_q(struct qdio_q *q)
+static inline int qdio_siga_sync_q(struct qdio_q *q)
 {
        if (q->is_input_q)
                return qdio_siga_sync(q, 0, q->mask);
@@ -350,7 +350,7 @@ static inline int qdio_siga_input(struct qdio_q *q)
        int cc;
 
        DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr);
-       qdio_perf_stat_inc(&perf_stats.siga_in);
+       qperf_inc(q, siga_read);
 
        cc = do_siga_input(q->irq_ptr->schid, q->mask);
        if (cc)
@@ -358,8 +358,7 @@ static inline int qdio_siga_input(struct qdio_q *q)
        return cc;
 }
 
-/* called from thinint inbound handler */
-void qdio_sync_after_thinint(struct qdio_q *q)
+static inline void qdio_sync_after_thinint(struct qdio_q *q)
 {
        if (pci_out_supported(q)) {
                if (need_siga_sync_thinint(q))
@@ -370,13 +369,20 @@ void qdio_sync_after_thinint(struct qdio_q *q)
                qdio_siga_sync_q(q);
 }
 
-inline void qdio_stop_polling(struct qdio_q *q)
+int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr,
+                       unsigned char *state)
+{
+       qdio_siga_sync_q(q);
+       return get_buf_states(q, bufnr, state, 1, 0);
+}
+
+static inline void qdio_stop_polling(struct qdio_q *q)
 {
        if (!q->u.in.polling)
                return;
 
        q->u.in.polling = 0;
-       qdio_perf_stat_inc(&perf_stats.debug_stop_polling);
+       qperf_inc(q, stop_polling);
 
        /* show the card that we are not polling anymore */
        if (is_qebsm(q)) {
@@ -387,6 +393,20 @@ inline void qdio_stop_polling(struct qdio_q *q)
                set_buf_state(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT);
 }
 
+static inline void account_sbals(struct qdio_q *q, int count)
+{
+       int pos = 0;
+
+       q->q_stats.nr_sbal_total += count;
+       if (count == QDIO_MAX_BUFFERS_MASK) {
+               q->q_stats.nr_sbals[7]++;
+               return;
+       }
+       while (count >>= 1)
+               pos++;
+       q->q_stats.nr_sbals[pos]++;
+}
+
 static void announce_buffer_error(struct qdio_q *q, int count)
 {
        q->qdio_error |= QDIO_ERROR_SLSB_STATE;
@@ -394,8 +414,8 @@ static void announce_buffer_error(struct qdio_q *q, int count)
        /* special handling for no target buffer empty */
        if ((!q->is_input_q &&
            (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) {
-               qdio_perf_stat_inc(&perf_stats.outbound_target_full);
-               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d",
+               qperf_inc(q, target_full);
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%02x",
                              q->first_to_check);
                return;
        }
@@ -412,7 +432,7 @@ static inline void inbound_primed(struct qdio_q *q, int count)
 {
        int new;
 
-       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count);
+       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %02x", count);
 
        /* for QEBSM the ACK was already set by EQBS */
        if (is_qebsm(q)) {
@@ -449,13 +469,8 @@ static inline void inbound_primed(struct qdio_q *q, int count)
        count--;
        if (!count)
                return;
-
-       /*
-        * Need to change all PRIMED buffers to NOT_INIT, otherwise
-        * we're loosing initiative in the thinint code.
-        */
-       set_buf_states(q, q->first_to_check, SLSB_P_INPUT_NOT_INIT,
-                      count);
+       /* need to change ALL buffers to get more interrupts */
+       set_buf_states(q, q->first_to_check, SLSB_P_INPUT_NOT_INIT, count);
 }
 
 static int get_inbound_buffer_frontier(struct qdio_q *q)
@@ -470,19 +485,13 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
        count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
        stop = add_buf(q->first_to_check, count);
 
-       /*
-        * No siga sync here, as a PCI or we after a thin interrupt
-        * will sync the queues.
-        */
-
-       /* need to set count to 1 for non-qebsm */
-       if (!is_qebsm(q))
-               count = 1;
-
-check_next:
        if (q->first_to_check == stop)
                goto out;
 
+       /*
+        * No siga sync here, as a PCI or we after a thin interrupt
+        * already sync'ed the queues.
+        */
        count = get_buf_states(q, q->first_to_check, &state, count, 1);
        if (!count)
                goto out;
@@ -490,23 +499,25 @@ check_next:
        switch (state) {
        case SLSB_P_INPUT_PRIMED:
                inbound_primed(q, count);
-               /*
-                * No siga-sync needed for non-qebsm here, as the inbound queue
-                * will be synced on the next siga-r, resp.
-                * tiqdio_is_inbound_q_done will do the siga-sync.
-                */
                q->first_to_check = add_buf(q->first_to_check, count);
-               atomic_sub(count, &q->nr_buf_used);
-               goto check_next;
+               if (atomic_sub(count, &q->nr_buf_used) == 0)
+                       qperf_inc(q, inbound_queue_full);
+               if (q->irq_ptr->perf_stat_enabled)
+                       account_sbals(q, count);
+               break;
        case SLSB_P_INPUT_ERROR:
                announce_buffer_error(q, count);
                /* process the buffer, the upper layer will take care of it */
                q->first_to_check = add_buf(q->first_to_check, count);
                atomic_sub(count, &q->nr_buf_used);
+               if (q->irq_ptr->perf_stat_enabled)
+                       account_sbals_error(q, count);
                break;
        case SLSB_CU_INPUT_EMPTY:
        case SLSB_P_INPUT_NOT_INIT:
        case SLSB_P_INPUT_ACK:
+               if (q->irq_ptr->perf_stat_enabled)
+                       q->q_stats.nr_sbal_nop++;
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
                break;
        default:
@@ -516,7 +527,7 @@ out:
        return q->first_to_check;
 }
 
-int qdio_inbound_q_moved(struct qdio_q *q)
+static int qdio_inbound_q_moved(struct qdio_q *q)
 {
        int bufnr;
 
@@ -524,35 +535,32 @@ int qdio_inbound_q_moved(struct qdio_q *q)
 
        if ((bufnr != q->last_move) || q->qdio_error) {
                q->last_move = bufnr;
-               if (!need_siga_sync(q) && !pci_out_supported(q))
+               if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR)
                        q->u.in.timestamp = get_usecs();
-
-               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved");
                return 1;
        } else
                return 0;
 }
 
-static int qdio_inbound_q_done(struct qdio_q *q)
+static inline int qdio_inbound_q_done(struct qdio_q *q)
 {
        unsigned char state = 0;
 
        if (!atomic_read(&q->nr_buf_used))
                return 1;
 
-       /*
-        * We need that one for synchronization with the adapter, as it
-        * does a kind of PCI avoidance.
-        */
        qdio_siga_sync_q(q);
-
        get_buf_state(q, q->first_to_check, &state, 0);
-       if (state == SLSB_P_INPUT_PRIMED)
-               /* we got something to do */
+
+       if (state == SLSB_P_INPUT_PRIMED || state == SLSB_P_INPUT_ERROR)
+               /* more work coming */
                return 0;
 
-       /* on VM, we don't poll, so the q is always done here */
-       if (need_siga_sync(q) || pci_out_supported(q))
+       if (is_thinint_irq(q->irq_ptr))
+               return 1;
+
+       /* don't poll under z/VM */
+       if (MACHINE_IS_VM)
                return 1;
 
        /*
@@ -560,65 +568,67 @@ static int qdio_inbound_q_done(struct qdio_q *q)
         * has (probably) not moved (see qdio_inbound_processing).
         */
        if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
-               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d",
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x",
                              q->first_to_check);
                return 1;
-       } else {
-               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d",
-                             q->first_to_check);
+       } else
                return 0;
-       }
 }
 
-void qdio_kick_inbound_handler(struct qdio_q *q)
+static void qdio_kick_handler(struct qdio_q *q)
 {
-       int count, start, end;
-
-       qdio_perf_stat_inc(&perf_stats.inbound_handler);
-
-       start = q->first_to_kick;
-       end = q->first_to_check;
-       if (end >= start)
-               count = end - start;
-       else
-               count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-
-       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count);
+       int start = q->first_to_kick;
+       int end = q->first_to_check;
+       int count;
 
        if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
                return;
 
-       q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr,
-                  start, count, q->irq_ptr->int_parm);
+       count = sub_buf(end, start);
+
+       if (q->is_input_q) {
+               qperf_inc(q, inbound_handler);
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count);
+       } else {
+               qperf_inc(q, outbound_handler);
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x",
+                             start, count);
+       }
+
+       q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
+                  q->irq_ptr->int_parm);
 
        /* for the next time */
-       q->first_to_kick = q->first_to_check;
+       q->first_to_kick = end;
        q->qdio_error = 0;
 }
 
 static void __qdio_inbound_processing(struct qdio_q *q)
 {
-       qdio_perf_stat_inc(&perf_stats.tasklet_inbound);
+       qperf_inc(q, tasklet_inbound);
 again:
        if (!qdio_inbound_q_moved(q))
                return;
 
-       qdio_kick_inbound_handler(q);
+       qdio_kick_handler(q);
 
-       if (!qdio_inbound_q_done(q))
+       if (!qdio_inbound_q_done(q)) {
                /* means poll time is not yet over */
+               qperf_inc(q, tasklet_inbound_resched);
                goto again;
+       }
 
        qdio_stop_polling(q);
        /*
         * We need to check again to not lose initiative after
         * resetting the ACK state.
         */
-       if (!qdio_inbound_q_done(q))
+       if (!qdio_inbound_q_done(q)) {
+               qperf_inc(q, tasklet_inbound_resched2);
                goto again;
+       }
 }
 
-/* inbound tasklet */
 void qdio_inbound_processing(unsigned long data)
 {
        struct qdio_q *q = (struct qdio_q *)data;
@@ -641,11 +651,6 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
        count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
        stop = add_buf(q->first_to_check, count);
 
-       /* need to set count to 1 for non-qebsm */
-       if (!is_qebsm(q))
-               count = 1;
-
-check_next:
        if (q->first_to_check == stop)
                return q->first_to_check;
 
@@ -656,25 +661,25 @@ check_next:
        switch (state) {
        case SLSB_P_OUTPUT_EMPTY:
                /* the adapter got it */
-               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count);
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %02x", q->nr, count);
 
                atomic_sub(count, &q->nr_buf_used);
                q->first_to_check = add_buf(q->first_to_check, count);
-               /*
-                * We fetch all buffer states at once. get_buf_states may
-                * return count < stop. For QEBSM we do not loop.
-                */
-               if (is_qebsm(q))
-                       break;
-               goto check_next;
+               if (q->irq_ptr->perf_stat_enabled)
+                       account_sbals(q, count);
+               break;
        case SLSB_P_OUTPUT_ERROR:
                announce_buffer_error(q, count);
                /* process the buffer, the upper layer will take care of it */
                q->first_to_check = add_buf(q->first_to_check, count);
                atomic_sub(count, &q->nr_buf_used);
+               if (q->irq_ptr->perf_stat_enabled)
+                       account_sbals_error(q, count);
                break;
        case SLSB_CU_OUTPUT_PRIMED:
                /* the adapter has not fetched the output yet */
+               if (q->irq_ptr->perf_stat_enabled)
+                       q->q_stats.nr_sbal_nop++;
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr);
                break;
        case SLSB_P_OUTPUT_NOT_INIT:
@@ -706,16 +711,16 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
                return 0;
 }
 
-static void qdio_kick_outbound_q(struct qdio_q *q)
+static int qdio_kick_outbound_q(struct qdio_q *q)
 {
        unsigned int busy_bit;
        int cc;
 
        if (!need_siga_out(q))
-               return;
+               return 0;
 
        DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
-       qdio_perf_stat_inc(&perf_stats.siga_out);
+       qperf_inc(q, siga_write);
 
        cc = qdio_siga_output(q, &busy_bit);
        switch (cc) {
@@ -724,59 +729,25 @@ static void qdio_kick_outbound_q(struct qdio_q *q)
        case 2:
                if (busy_bit) {
                        DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
-                       q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY;
-               } else {
-                       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d",
-                                     q->nr);
-                       q->qdio_error = cc;
-               }
+                       cc |= QDIO_ERROR_SIGA_BUSY;
+               } else
+                       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
                break;
        case 1:
        case 3:
                DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
-               q->qdio_error = cc;
                break;
        }
-}
-
-static void qdio_kick_outbound_handler(struct qdio_q *q)
-{
-       int start, end, count;
-
-       start = q->first_to_kick;
-       end = q->last_move;
-       if (end >= start)
-               count = end - start;
-       else
-               count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-
-       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickouth: %1d", q->nr);
-       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count);
-
-       if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
-               return;
-
-       q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
-                  q->irq_ptr->int_parm);
-
-       /* for the next time: */
-       q->first_to_kick = q->last_move;
-       q->qdio_error = 0;
+       return cc;
 }
 
 static void __qdio_outbound_processing(struct qdio_q *q)
 {
-       unsigned long flags;
-
-       qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
-       spin_lock_irqsave(&q->lock, flags);
-
+       qperf_inc(q, tasklet_outbound);
        BUG_ON(atomic_read(&q->nr_buf_used) < 0);
 
        if (qdio_outbound_q_moved(q))
-               qdio_kick_outbound_handler(q);
-
-       spin_unlock_irqrestore(&q->lock, flags);
+               qdio_kick_handler(q);
 
        if (queue_type(q) == QDIO_ZFCP_QFMT)
                if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
@@ -800,12 +771,9 @@ static void __qdio_outbound_processing(struct qdio_q *q)
         */
        if (qdio_outbound_q_done(q))
                del_timer(&q->u.out.timer);
-       else {
-               if (!timer_pending(&q->u.out.timer)) {
+       else
+               if (!timer_pending(&q->u.out.timer))
                        mod_timer(&q->u.out.timer, jiffies + 10 * HZ);
-                       qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer);
-               }
-       }
        return;
 
 sched:
@@ -830,8 +798,7 @@ void qdio_outbound_timer(unsigned long data)
        tasklet_schedule(&q->tasklet);
 }
 
-/* called from thinint inbound tasklet */
-void qdio_check_outbound_after_thinint(struct qdio_q *q)
+static inline void qdio_check_outbound_after_thinint(struct qdio_q *q)
 {
        struct qdio_q *out;
        int i;
@@ -844,6 +811,48 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q)
                        tasklet_schedule(&out->tasklet);
 }
 
+static void __tiqdio_inbound_processing(struct qdio_q *q)
+{
+       qperf_inc(q, tasklet_inbound);
+       qdio_sync_after_thinint(q);
+
+       /*
+        * The interrupt could be caused by a PCI request. Check the
+        * PCI capable outbound queues.
+        */
+       qdio_check_outbound_after_thinint(q);
+
+       if (!qdio_inbound_q_moved(q))
+               return;
+
+       qdio_kick_handler(q);
+
+       if (!qdio_inbound_q_done(q)) {
+               qperf_inc(q, tasklet_inbound_resched);
+               if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) {
+                       tasklet_schedule(&q->tasklet);
+                       return;
+               }
+       }
+
+       qdio_stop_polling(q);
+       /*
+        * We need to check again to not lose initiative after
+        * resetting the ACK state.
+        */
+       if (!qdio_inbound_q_done(q)) {
+               qperf_inc(q, tasklet_inbound_resched2);
+               if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED))
+                       tasklet_schedule(&q->tasklet);
+       }
+}
+
+void tiqdio_inbound_processing(unsigned long data)
+{
+       struct qdio_q *q = (struct qdio_q *)data;
+       __tiqdio_inbound_processing(q);
+}
+
 static inline void qdio_set_state(struct qdio_irq *irq_ptr,
                                  enum qdio_irq_states state)
 {
@@ -871,8 +880,6 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
        if (unlikely(irq_ptr->state == QDIO_IRQ_STATE_STOPPED))
                return;
 
-       qdio_perf_stat_inc(&perf_stats.pci_int);
-
        for_each_input_queue(irq_ptr, q, i)
                tasklet_schedule(&q->tasklet);
 
@@ -914,78 +921,26 @@ no_handler:
        qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
 }
 
-static void qdio_call_shutdown(struct work_struct *work)
-{
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
-
-       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);
-}
-
-static void qdio_int_error(struct ccw_device *cdev)
+static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
+                                     int dstat)
 {
        struct qdio_irq *irq_ptr = cdev->private->qdio_data;
 
-       switch (irq_ptr->state) {
-       case QDIO_IRQ_STATE_INACTIVE:
-       case QDIO_IRQ_STATE_CLEANUP:
-               qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
-               break;
-       case QDIO_IRQ_STATE_ESTABLISHED:
-       case QDIO_IRQ_STATE_ACTIVE:
-               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);
-                       queue_work(ccw_device_work, &cdev->private->kick_work);
-               }
-               break;
-       default:
-               WARN_ON(1);
-       }
-       wake_up(&cdev->private->wait_q);
-}
-
-static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
-                                      int dstat)
-{
-       struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+       DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
 
-       if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
-               DBF_ERROR("EQ:ck con");
+       if (cstat)
                goto error;
-       }
-
-       if (!(dstat & DEV_STAT_DEV_END)) {
-               DBF_ERROR("EQ:no dev");
+       if (dstat & ~(DEV_STAT_DEV_END | DEV_STAT_CHN_END))
                goto error;
-       }
-
-       if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
-               DBF_ERROR("EQ: bad io");
+       if (!(dstat & DEV_STAT_DEV_END))
                goto error;
-       }
-       return 0;
+       qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
+       return;
+
 error:
        DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
        DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
-
        qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
-       return 1;
-}
-
-static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
-                                     int dstat)
-{
-       struct qdio_irq *irq_ptr = cdev->private->qdio_data;
-
-       DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
-       if (!qdio_establish_check_errors(cdev, cstat, dstat))
-               qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
 }
 
 /* qdio interrupt handler */
@@ -995,8 +950,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
        struct qdio_irq *irq_ptr = cdev->private->qdio_data;
        int cstat, dstat;
 
-       qdio_perf_stat_inc(&perf_stats.qdio_int);
-
        if (!intparm || !irq_ptr) {
                DBF_ERROR("qint:%4x", cdev->private->schid.sch_no);
                return;
@@ -1006,10 +959,8 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
                switch (PTR_ERR(irb)) {
                case -EIO:
                        DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
-                       return;
-               case -ETIMEDOUT:
-                       DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no);
-                       qdio_int_error(cdev);
+                       qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+                       wake_up(&cdev->private->wait_q);
                        return;
                default:
                        WARN_ON(1);
@@ -1017,7 +968,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
                }
        }
        qdio_irq_check_sense(irq_ptr, irb);
-
        cstat = irb->scsw.cmd.cstat;
        dstat = irb->scsw.cmd.dstat;
 
@@ -1025,23 +975,21 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
        case QDIO_IRQ_STATE_INACTIVE:
                qdio_establish_handle_irq(cdev, cstat, dstat);
                break;
-
        case QDIO_IRQ_STATE_CLEANUP:
                qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
                break;
-
        case QDIO_IRQ_STATE_ESTABLISHED:
        case QDIO_IRQ_STATE_ACTIVE:
                if (cstat & SCHN_STAT_PCI) {
                        qdio_int_handler_pci(irq_ptr);
-                       /* no state change so no need to wake up wait_q */
                        return;
                }
-               if ((cstat & ~SCHN_STAT_PCI) || dstat) {
+               if (cstat || dstat)
                        qdio_handle_activate_check(cdev, intparm, cstat,
                                                   dstat);
-                       break;
-               }
+               break;
+       case QDIO_IRQ_STATE_STOPPED:
+               break;
        default:
                WARN_ON(1);
        }
@@ -1457,10 +1405,12 @@ static inline int buf_in_between(int bufnr, int start, int count)
  * @bufnr: first buffer to process
  * @count: how many buffers are emptied
  */
-static void handle_inbound(struct qdio_q *q, unsigned int callflags,
-                          int bufnr, int count)
+static int handle_inbound(struct qdio_q *q, unsigned int callflags,
+                         int bufnr, int count)
 {
-       int used, cc, diff;
+       int used, diff;
+
+       qperf_inc(q, inbound_call);
 
        if (!q->u.in.polling)
                goto set;
@@ -1497,13 +1447,11 @@ set:
 
        /* no need to signal as long as the adapter had free buffers */
        if (used)
-               return;
+               return 0;
 
-       if (need_siga_in(q)) {
-               cc = qdio_siga_input(q);
-               if (cc)
-                       q->qdio_error = cc;
-       }
+       if (need_siga_in(q))
+               return qdio_siga_input(q);
+       return 0;
 }
 
 /**
@@ -1513,46 +1461,47 @@ set:
  * @bufnr: first buffer to process
  * @count: how many buffers are filled
  */
-static void handle_outbound(struct qdio_q *q, unsigned int callflags,
-                           int bufnr, int count)
+static int handle_outbound(struct qdio_q *q, unsigned int callflags,
+                          int bufnr, int count)
 {
        unsigned char state;
-       int used;
+       int used, rc = 0;
 
-       qdio_perf_stat_inc(&perf_stats.outbound_handler);
+       qperf_inc(q, outbound_call);
 
        count = set_buf_states(q, bufnr, SLSB_CU_OUTPUT_PRIMED, count);
        used = atomic_add_return(count, &q->nr_buf_used);
        BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q);
 
-       if (callflags & QDIO_FLAG_PCI_OUT)
+       if (callflags & QDIO_FLAG_PCI_OUT) {
                q->u.out.pci_out_enabled = 1;
+               qperf_inc(q, pci_request_int);
+       }
        else
                q->u.out.pci_out_enabled = 0;
 
        if (queue_type(q) == QDIO_IQDIO_QFMT) {
                if (multicast_outbound(q))
-                       qdio_kick_outbound_q(q);
+                       rc = qdio_kick_outbound_q(q);
                else
                        if ((q->irq_ptr->ssqd_desc.mmwc > 1) &&
                            (count > 1) &&
                            (count <= q->irq_ptr->ssqd_desc.mmwc)) {
                                /* exploit enhanced SIGA */
                                q->u.out.use_enh_siga = 1;
-                               qdio_kick_outbound_q(q);
+                               rc = qdio_kick_outbound_q(q);
                        } else {
                                /*
                                * One siga-w per buffer required for unicast
                                * HiperSockets.
                                */
                                q->u.out.use_enh_siga = 0;
-                               while (count--)
-                                       qdio_kick_outbound_q(q);
+                               while (count--) {
+                                       rc = qdio_kick_outbound_q(q);
+                                       if (rc)
+                                               goto out;
+                               }
                        }
-
-               /* report CC=2 conditions synchronously */
-               if (q->qdio_error)
-                       __qdio_outbound_processing(q);
                goto out;
        }
 
@@ -1564,13 +1513,13 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
        /* try to fast requeue buffers */
        get_buf_state(q, prev_buf(bufnr), &state, 0);
        if (state != SLSB_CU_OUTPUT_PRIMED)
-               qdio_kick_outbound_q(q);
-       else {
-               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
-               qdio_perf_stat_inc(&perf_stats.fast_requeue);
-       }
+               rc = qdio_kick_outbound_q(q);
+       else
+               qperf_inc(q, fast_requeue);
+
 out:
        tasklet_schedule(&q->tasklet);
+       return rc;
 }
 
 /**
@@ -1582,41 +1531,30 @@ out:
  * @count: how many buffers to process
  */
 int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
-           int q_nr, int bufnr, int count)
+           int q_nr, unsigned int bufnr, unsigned int count)
 {
        struct qdio_irq *irq_ptr;
 
-       if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) ||
-           (count > QDIO_MAX_BUFFERS_PER_Q) ||
-           (q_nr > QDIO_MAX_QUEUES_PER_IRQ))
+       if (bufnr >= QDIO_MAX_BUFFERS_PER_Q || count > QDIO_MAX_BUFFERS_PER_Q)
                return -EINVAL;
 
-       if (!count)
-               return 0;
-
        irq_ptr = cdev->private->qdio_data;
        if (!irq_ptr)
                return -ENODEV;
 
-       if (callflags & QDIO_FLAG_SYNC_INPUT)
-               DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input");
-       else
-               DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output");
-       DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags);
-       DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count);
+       DBF_DEV_EVENT(DBF_INFO, irq_ptr,
+                     "do%02x b:%02x c:%02x", callflags, bufnr, count);
 
        if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
                return -EBUSY;
 
        if (callflags & QDIO_FLAG_SYNC_INPUT)
-               handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr,
-                              count);
+               return handle_inbound(irq_ptr->input_qs[q_nr],
+                                     callflags, bufnr, count);
        else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
-               handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr,
-                               count);
-       else
-               return -EINVAL;
-       return 0;
+               return handle_outbound(irq_ptr->output_qs[q_nr],
+                                      callflags, bufnr, count);
+       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(do_QDIO);
 
@@ -1633,16 +1571,11 @@ static int __init init_QDIO(void)
        rc = qdio_debug_init();
        if (rc)
                goto out_ti;
-       rc = qdio_setup_perf_stats();
-       if (rc)
-               goto out_debug;
        rc = tiqdio_register_thinints();
        if (rc)
-               goto out_perf;
+               goto out_debug;
        return 0;
 
-out_perf:
-       qdio_remove_perf_stats();
 out_debug:
        qdio_debug_exit();
 out_ti:
@@ -1656,7 +1589,6 @@ static void __exit exit_QDIO(void)
 {
        tiqdio_unregister_thinints();
        tiqdio_free_memory();
-       qdio_remove_perf_stats();
        qdio_debug_exit();
        qdio_setup_exit();
 }