[S390] qdio: prevent race for shared indicators
authorJan Glauber <jang@linux.vnet.ibm.com>
Wed, 5 Jan 2011 11:47:49 +0000 (12:47 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Wed, 5 Jan 2011 11:47:28 +0000 (12:47 +0100)
If the shared indicator is used the following race leads to
an inbound stall:

Device                  CPU0                    CPU1
========================================================

non-shared DSCI =>1
ALSI => 1
                        Thin INT
                        ALSI => 0

                        non-shared DSCI
                        tasklets scheduled

shared DSCI => 1
ALSI => 1

                        shared DSCI => 0
                        ALSI ? -> set
                                                Thin INT
                                                ALSI => 0
                        ALSI was set,
                        shared DSCI => 1

After that no more interrupts occur because the DSCI is still set.
Fix that race by only resetting the shared DSCI if it was actually
set so the tasklets for all shared devices are scheduled and will
run after the interrupt.

Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/qdio.h
drivers/s390/cio/qdio_main.c
drivers/s390/cio/qdio_thinint.c

index 0f4ef8769a3dfb9b74670a7eb4270ade36192215..9b6ea3ca3ececdfac9a63b1fc232c28e52764cdd 100644 (file)
@@ -423,9 +423,9 @@ struct indicator_t {
 
 extern struct indicator_t *q_indicators;
 
-static inline int shared_ind(struct qdio_irq *irq_ptr)
+static inline int shared_ind(u32 *dsci)
 {
-       return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
+       return dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
 }
 
 /* prototypes for thin interrupt */
index 194ea8c182b23fae6f449c8c666d89da6e5e25cd..6621de94f3ad86057fd78515b8e07b681d11f9a7 100644 (file)
@@ -1552,7 +1552,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
 
        WARN_ON(queue_irqs_enabled(q));
 
-       if (!shared_ind(q->irq_ptr))
+       if (!shared_ind(q->irq_ptr->dsci))
                xchg(q->irq_ptr->dsci, 0);
 
        qdio_stop_polling(q);
@@ -1562,7 +1562,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
         * We need to check again to not lose initiative after
         * resetting the ACK state.
         */
-       if (!shared_ind(q->irq_ptr) && *q->irq_ptr->dsci)
+       if (!shared_ind(q->irq_ptr->dsci) && *q->irq_ptr->dsci)
                goto rescan;
        if (!qdio_inbound_q_done(q))
                goto rescan;
index 64b59a58a1cd8472f56faf355f090d1307a74d7c..5c4e741d82217f37fe4e7c52081206b83f8163e9 100644 (file)
@@ -36,22 +36,8 @@ static u8 *tiqdio_alsi;
 
 struct indicator_t *q_indicators;
 
-static int css_qdio_omit_svs;
-
 static u64 last_ai_time;
 
-static inline unsigned long do_clear_global_summary(void)
-{
-       register unsigned long __fn asm("1") = 3;
-       register unsigned long __tmp asm("2");
-       register unsigned long __time asm("3");
-
-       asm volatile(
-               "       .insn   rre,0xb2650000,2,0"
-               : "+d" (__fn), "=d" (__tmp), "=d" (__time));
-       return __time;
-}
-
 /* returns addr for the device state change indicator */
 static u32 *get_indicator(void)
 {
@@ -84,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
        struct qdio_q *q;
        int i;
 
-       /* No TDD facility? If we must use SIGA-s we can also omit SVS. */
-       if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync)
-               css_qdio_omit_svs = 1;
-
        mutex_lock(&tiq_list_lock);
        for_each_input_queue(irq_ptr, q, i)
                list_add_rcu(&q->entry, &tiq_list);
@@ -113,9 +95,9 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
        }
 }
 
-static inline int shared_ind_used(void)
+static inline u32 shared_ind_set(void)
 {
-       return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count);
+       return q_indicators[TIQDIO_SHARED_IND].ind;
 }
 
 /**
@@ -125,22 +107,12 @@ static inline int shared_ind_used(void)
  */
 static void tiqdio_thinint_handler(void *alsi, void *data)
 {
+       u32 si_used = shared_ind_set();
        struct qdio_q *q;
 
        last_ai_time = S390_lowcore.int_clock;
        kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++;
 
-       /*
-        * 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 */
-       if (shared_ind_used())
-               xchg(tiqdio_alsi, 0);
-
        /* protect tiq_list entries, only changed in activate or shutdown */
        rcu_read_lock();
 
@@ -148,7 +120,10 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
        list_for_each_entry_rcu(q, &tiq_list, entry) {
 
                /* only process queues from changed sets */
-               if (!*q->irq_ptr->dsci)
+               if (unlikely(shared_ind(q->irq_ptr->dsci))) {
+                       if (!si_used)
+                               continue;
+               } else if (!*q->irq_ptr->dsci)
                        continue;
 
                if (q->u.in.queue_start_poll) {
@@ -164,7 +139,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
                                                 q->irq_ptr->int_parm);
                } else {
                        /* only clear it if the indicator is non-shared */
-                       if (!shared_ind(q->irq_ptr))
+                       if (!shared_ind(q->irq_ptr->dsci))
                                xchg(q->irq_ptr->dsci, 0);
                        /*
                         * Call inbound processing but not directly
@@ -180,13 +155,8 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
         * If the shared indicator was used clear it now after all queues
         * were processed.
         */
-       if (shared_ind_used()) {
+       if (si_used && shared_ind_set())
                xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
-
-               /* prevent racing */
-               if (*tiqdio_alsi)
-                       xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7);
-       }
 }
 
 static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
@@ -271,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr)
 {
        if (!is_thinint_irq(irq_ptr))
                return 0;
-
-       /* Check for aif time delay disablement. If installed,
-        * omit SVS even under LPAR
-        */
-       if (css_general_characteristics.aif_tdd)
-               css_qdio_omit_svs = 1;
        return set_subchannel_ind(irq_ptr, 0);
 }