diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2011-07-24 04:48:00 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-07-24 04:48:00 -0400 |
commit | b02f0c2ea25781e0f94b4fc8f6f85582057857b3 (patch) | |
tree | 688ed8fea4f857f5280be3f4d8adae663fea3d4b /drivers/s390 | |
parent | 50a15981a1fac7e019ff7c3cba87531fb580f065 (diff) |
[S390] qdio: clear shared DSCI before scheduling the queue handler
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>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 15 |
1 files changed, 5 insertions, 10 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5c4e741d8221..68be6e157126 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 | ||
98 | static inline u32 shared_ind_set(void) | 98 | static 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 | */ |
108 | static void tiqdio_thinint_handler(void *alsi, void *data) | 110 | static 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 | ||
162 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) | 157 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) |