aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2011-07-24 04:48:00 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-03 14:40:44 -0400
commit002c956aab560a58367bf6bc804548d219a5eb45 (patch)
tree60d11daf80de763f8fa38dddc211cbf3599b8235
parentcfdf7986b6398049c35f8eb6e236d2387ee7ae14 (diff)
qdio: clear shared DSCI before scheduling the queue handler
commit b02f0c2ea25781e0f94b4fc8f6f85582057857b3 upstream. The following race can occur with qdio devices that use the shared device state change indicator: Device (Shared DSCI) CPU0 CPU1 =============================================================================== 1. DSCI 0 => 1, INT pending 2. Thinint handler * si_used = 1 * Inbound tasklet_schedule * DSCI 1 => 0 3. DSCI 0 => 1, INT pending 4. Thinint handler * si_used = 1 * Inbound tasklet_schedu le => NOP 5. Inbound tasklet run 6. DSCI = 1, INT surpressed 7. DSCI 1 => 0 The race would lead to a stall where new data in the input queue is not recognized so the device stops working in case of no further traffic. Fix the race by resetting the DSCI before scheduling the inbound tasklet so the device generates an interrupt if new data arrives in the above scenario in step 6. Reviewed-by: Ursula Braun <ursula.braun@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: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/s390/cio/qdio_thinint.c15
1 files changed, 5 insertions, 10 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 5c4e741d822..68be6e15712 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -95,9 +95,11 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
95 } 95 }
96} 96}
97 97
98static inline u32 shared_ind_set(void) 98static inline u32 clear_shared_ind(void)
99{ 99{
100 return q_indicators[TIQDIO_SHARED_IND].ind; 100 if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
101 return 0;
102 return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
101} 103}
102 104
103/** 105/**
@@ -107,7 +109,7 @@ static inline u32 shared_ind_set(void)
107 */ 109 */
108static void tiqdio_thinint_handler(void *alsi, void *data) 110static void tiqdio_thinint_handler(void *alsi, void *data)
109{ 111{
110 u32 si_used = shared_ind_set(); 112 u32 si_used = clear_shared_ind();
111 struct qdio_q *q; 113 struct qdio_q *q;
112 114
113 last_ai_time = S390_lowcore.int_clock; 115 last_ai_time = S390_lowcore.int_clock;
@@ -150,13 +152,6 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
150 qperf_inc(q, adapter_int); 152 qperf_inc(q, adapter_int);
151 } 153 }
152 rcu_read_unlock(); 154 rcu_read_unlock();
153
154 /*
155 * If the shared indicator was used clear it now after all queues
156 * were processed.
157 */
158 if (si_used && shared_ind_set())
159 xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
160} 155}
161 156
162static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) 157static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)