Merge commit 'v2.6.31-rc1' into dmaengine
[safe/jmp/linux-2.6] / drivers / s390 / cio / qdio_thinint.c
index 9291a77..981a77e 100644 (file)
@@ -31,6 +31,7 @@
 
 /* list of thin interrupt input queues */
 static LIST_HEAD(tiq_list);
+DEFINE_MUTEX(tiq_list_lock);
 
 /* adapter local summary indicator */
 static unsigned char *tiqdio_alsi;
@@ -42,9 +43,6 @@ struct indicator_t {
 };
 static struct indicator_t *q_indicators;
 
-static void tiqdio_tasklet_fn(unsigned long data);
-static DECLARE_TASKLET(tiqdio_tasklet, tiqdio_tasklet_fn, 0);
-
 static int css_qdio_omit_svs;
 
 static inline unsigned long do_clear_global_summary(void)
@@ -95,102 +93,64 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
        if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync)
                css_qdio_omit_svs = 1;
 
-       for_each_input_queue(irq_ptr, q, i) {
+       mutex_lock(&tiq_list_lock);
+       for_each_input_queue(irq_ptr, q, i)
                list_add_rcu(&q->entry, &tiq_list);
-               synchronize_rcu();
-       }
+       mutex_unlock(&tiq_list_lock);
        xchg(irq_ptr->dsci, 1);
-       tasklet_schedule(&tiqdio_tasklet);
 }
 
-/*
- * we cannot stop the tiqdio tasklet here since it is for all
- * thinint qdio devices and it must run as long as there is a
- * thinint device left
- */
 void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
 {
        struct qdio_q *q;
        int i;
 
-       for_each_input_queue(irq_ptr, q, i) {
+       for (i = 0; i < irq_ptr->nr_input_qs; i++) {
+               q = irq_ptr->input_qs[i];
+               /* if establish triggered an error */
+               if (!q || !q->entry.prev || !q->entry.next)
+                       continue;
+
+               mutex_lock(&tiq_list_lock);
                list_del_rcu(&q->entry);
+               mutex_unlock(&tiq_list_lock);
                synchronize_rcu();
        }
 }
 
-static inline int tiqdio_inbound_q_done(struct qdio_q *q)
-{
-       unsigned char state;
-
-       if (!atomic_read(&q->nr_buf_used))
-               return 1;
-
-       qdio_siga_sync_q(q);
-       get_buf_state(q, q->first_to_check, &state);
-
-       if (state == SLSB_P_INPUT_PRIMED)
-               /* more work coming */
-               return 0;
-       return 1;
-}
-
 static inline int shared_ind(struct qdio_irq *irq_ptr)
 {
        return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
 }
 
-static void __tiqdio_inbound_processing(struct qdio_q *q)
+/**
+ * tiqdio_thinint_handler - thin interrupt handler for qdio
+ * @ind: pointer to adapter local summary indicator
+ * @drv_data: NULL
+ */
+static void tiqdio_thinint_handler(void *ind, void *drv_data)
 {
-       qdio_perf_stat_inc(&perf_stats.thinint_inbound);
-       qdio_sync_after_thinint(q);
+       struct qdio_q *q;
+
+       qdio_perf_stat_inc(&perf_stats.thin_int);
 
        /*
-        * Maybe we have work on our outbound queues... at least
-        * we have to check the PCI capable queues.
+        * SVS only when needed: issue SVS to benefit from iqdio interrupt
+        * avoidance (SVS clears adapter interrupt suppression overwrite)
         */
-       qdio_check_outbound_after_thinint(q);
-
-again:
-       if (!qdio_inbound_q_moved(q))
-               return;
-
-       qdio_kick_inbound_handler(q);
-
-       if (!tiqdio_inbound_q_done(q)) {
-               qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop);
-               goto again;
-       }
+       if (!css_qdio_omit_svs)
+               do_clear_global_summary();
 
-       qdio_stop_polling(q);
        /*
-        * We need to check again to not lose initiative after
-        * resetting the ACK state.
+        * reset local summary indicator (tiqdio_alsi) to stop adapter
+        * interrupts for now
         */
-       if (!tiqdio_inbound_q_done(q)) {
-               qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2);
-               goto again;
-       }
-}
-
-void tiqdio_inbound_processing(unsigned long data)
-{
-       struct qdio_q *q = (struct qdio_q *)data;
-
-       __tiqdio_inbound_processing(q);
-}
-
-/* check for work on all inbound thinint queues */
-static void tiqdio_tasklet_fn(unsigned long data)
-{
-       struct qdio_q *q;
-
-       qdio_perf_stat_inc(&perf_stats.tasklet_thinint);
-again:
+       xchg((u8 *)ind, 0);
 
        /* protect tiq_list entries, only changed in activate or shutdown */
        rcu_read_lock();
 
+       /* check for work on all inbound thinint queues */
        list_for_each_entry_rcu(q, &tiq_list, entry)
                /* only process queues from changed sets */
                if (*q->irq_ptr->dsci) {
@@ -218,44 +178,11 @@ again:
                if (*tiqdio_alsi)
                        xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1);
        }
-
-       /* check for more work */
-       if (*tiqdio_alsi) {
-               xchg(tiqdio_alsi, 0);
-               qdio_perf_stat_inc(&perf_stats.tasklet_thinint_loop);
-               goto again;
-       }
-}
-
-/**
- * tiqdio_thinint_handler - thin interrupt handler for qdio
- * @ind: pointer to adapter local summary indicator
- * @drv_data: NULL
- */
-static void tiqdio_thinint_handler(void *ind, void *drv_data)
-{
-       qdio_perf_stat_inc(&perf_stats.thin_int);
-
-       /*
-        * SVS only when needed: issue SVS to benefit from iqdio interrupt
-        * avoidance (SVS clears adapter interrupt suppression overwrite)
-        */
-       if (!css_qdio_omit_svs)
-               do_clear_global_summary();
-
-       /*
-        * reset local summary indicator (tiqdio_alsi) to stop adapter
-        * interrupts for now, the tasklet will clean all dsci's
-        */
-       xchg((u8 *)ind, 0);
-       tasklet_hi_schedule(&tiqdio_tasklet);
 }
 
 static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
 {
        struct scssc_area *scssc_area;
-       char dbf_text[15];
-       void *ptr;
        int rc;
 
        scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
@@ -290,19 +217,15 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
 
        rc = chsc_error_from_response(scssc_area->response.code);
        if (rc) {
-               sprintf(dbf_text, "sidR%4x", scssc_area->response.code);
-               QDIO_DBF_TEXT1(0, trace, dbf_text);
-               QDIO_DBF_TEXT1(0, setup, dbf_text);
-               ptr = &scssc_area->response;
-               QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN);
+               DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
+                         scssc_area->response.code);
+               DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
                return rc;
        }
 
-       QDIO_DBF_TEXT2(0, setup, "setscind");
-       QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr,
-                     sizeof(unsigned long));
-       QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr,
-                     sizeof(unsigned long));
+       DBF_EVENT("setscind");
+       DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
+       DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long));
        return 0;
 }
 
@@ -323,14 +246,11 @@ void tiqdio_free_memory(void)
 
 int __init tiqdio_register_thinints(void)
 {
-       char dbf_text[20];
-
        isc_register(QDIO_AIRQ_ISC);
        tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
                                                      NULL, QDIO_AIRQ_ISC);
        if (IS_ERR(tiqdio_alsi)) {
-               sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi));
-               QDIO_DBF_TEXT0(0, setup, dbf_text);
+               DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
                tiqdio_alsi = NULL;
                isc_unregister(QDIO_AIRQ_ISC);
                return -ENOMEM;
@@ -356,7 +276,7 @@ void qdio_setup_thinint(struct qdio_irq *irq_ptr)
        if (!is_thinint_irq(irq_ptr))
                return;
        irq_ptr->dsci = get_indicator();
-       QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *));
+       DBF_HEX(&irq_ptr->dsci, sizeof(void *));
 }
 
 void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
@@ -371,7 +291,7 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
 
 void __exit tiqdio_unregister_thinints(void)
 {
-       tasklet_disable(&tiqdio_tasklet);
+       WARN_ON(!list_empty(&tiq_list));
 
        if (tiqdio_alsi) {
                s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC);