aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2011-01-05 06:47:49 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2011-01-05 06:47:28 -0500
commit4f325184f2d4c1f2258873b2a333005dc4dfcbc0 (patch)
treee69a7940ca8d51a6e1f639cf4df0b5e33925d357
parent078f8ecaa30718694d1e13d9f415b7ce75b3c968 (diff)
[S390] qdio: prevent race for shared indicators
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>
-rw-r--r--drivers/s390/cio/qdio.h4
-rw-r--r--drivers/s390/cio/qdio_main.c4
-rw-r--r--drivers/s390/cio/qdio_thinint.c54
3 files changed, 13 insertions, 49 deletions
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 0f4ef8769a3d..9b6ea3ca3ece 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -423,9 +423,9 @@ struct indicator_t {
423 423
424extern struct indicator_t *q_indicators; 424extern struct indicator_t *q_indicators;
425 425
426static inline int shared_ind(struct qdio_irq *irq_ptr) 426static inline int shared_ind(u32 *dsci)
427{ 427{
428 return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; 428 return dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
429} 429}
430 430
431/* prototypes for thin interrupt */ 431/* prototypes for thin interrupt */
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 194ea8c182b2..6621de94f3ad 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -1552,7 +1552,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
1552 1552
1553 WARN_ON(queue_irqs_enabled(q)); 1553 WARN_ON(queue_irqs_enabled(q));
1554 1554
1555 if (!shared_ind(q->irq_ptr)) 1555 if (!shared_ind(q->irq_ptr->dsci))
1556 xchg(q->irq_ptr->dsci, 0); 1556 xchg(q->irq_ptr->dsci, 0);
1557 1557
1558 qdio_stop_polling(q); 1558 qdio_stop_polling(q);
@@ -1562,7 +1562,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
1562 * We need to check again to not lose initiative after 1562 * We need to check again to not lose initiative after
1563 * resetting the ACK state. 1563 * resetting the ACK state.
1564 */ 1564 */
1565 if (!shared_ind(q->irq_ptr) && *q->irq_ptr->dsci) 1565 if (!shared_ind(q->irq_ptr->dsci) && *q->irq_ptr->dsci)
1566 goto rescan; 1566 goto rescan;
1567 if (!qdio_inbound_q_done(q)) 1567 if (!qdio_inbound_q_done(q))
1568 goto rescan; 1568 goto rescan;
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 64b59a58a1cd..5c4e741d8221 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -36,22 +36,8 @@ static u8 *tiqdio_alsi;
36 36
37struct indicator_t *q_indicators; 37struct indicator_t *q_indicators;
38 38
39static int css_qdio_omit_svs;
40
41static u64 last_ai_time; 39static u64 last_ai_time;
42 40
43static inline unsigned long do_clear_global_summary(void)
44{
45 register unsigned long __fn asm("1") = 3;
46 register unsigned long __tmp asm("2");
47 register unsigned long __time asm("3");
48
49 asm volatile(
50 " .insn rre,0xb2650000,2,0"
51 : "+d" (__fn), "=d" (__tmp), "=d" (__time));
52 return __time;
53}
54
55/* returns addr for the device state change indicator */ 41/* returns addr for the device state change indicator */
56static u32 *get_indicator(void) 42static u32 *get_indicator(void)
57{ 43{
@@ -84,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
84 struct qdio_q *q; 70 struct qdio_q *q;
85 int i; 71 int i;
86 72
87 /* No TDD facility? If we must use SIGA-s we can also omit SVS. */
88 if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync)
89 css_qdio_omit_svs = 1;
90
91 mutex_lock(&tiq_list_lock); 73 mutex_lock(&tiq_list_lock);
92 for_each_input_queue(irq_ptr, q, i) 74 for_each_input_queue(irq_ptr, q, i)
93 list_add_rcu(&q->entry, &tiq_list); 75 list_add_rcu(&q->entry, &tiq_list);
@@ -113,9 +95,9 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
113 } 95 }
114} 96}
115 97
116static inline int shared_ind_used(void) 98static inline u32 shared_ind_set(void)
117{ 99{
118 return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count); 100 return q_indicators[TIQDIO_SHARED_IND].ind;
119} 101}
120 102
121/** 103/**
@@ -125,22 +107,12 @@ static inline int shared_ind_used(void)
125 */ 107 */
126static void tiqdio_thinint_handler(void *alsi, void *data) 108static void tiqdio_thinint_handler(void *alsi, void *data)
127{ 109{
110 u32 si_used = shared_ind_set();
128 struct qdio_q *q; 111 struct qdio_q *q;
129 112
130 last_ai_time = S390_lowcore.int_clock; 113 last_ai_time = S390_lowcore.int_clock;
131 kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; 114 kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++;
132 115
133 /*
134 * SVS only when needed: issue SVS to benefit from iqdio interrupt
135 * avoidance (SVS clears adapter interrupt suppression overwrite).
136 */
137 if (!css_qdio_omit_svs)
138 do_clear_global_summary();
139
140 /* reset local summary indicator */
141 if (shared_ind_used())
142 xchg(tiqdio_alsi, 0);
143
144 /* protect tiq_list entries, only changed in activate or shutdown */ 116 /* protect tiq_list entries, only changed in activate or shutdown */
145 rcu_read_lock(); 117 rcu_read_lock();
146 118
@@ -148,7 +120,10 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
148 list_for_each_entry_rcu(q, &tiq_list, entry) { 120 list_for_each_entry_rcu(q, &tiq_list, entry) {
149 121
150 /* only process queues from changed sets */ 122 /* only process queues from changed sets */
151 if (!*q->irq_ptr->dsci) 123 if (unlikely(shared_ind(q->irq_ptr->dsci))) {
124 if (!si_used)
125 continue;
126 } else if (!*q->irq_ptr->dsci)
152 continue; 127 continue;
153 128
154 if (q->u.in.queue_start_poll) { 129 if (q->u.in.queue_start_poll) {
@@ -164,7 +139,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
164 q->irq_ptr->int_parm); 139 q->irq_ptr->int_parm);
165 } else { 140 } else {
166 /* only clear it if the indicator is non-shared */ 141 /* only clear it if the indicator is non-shared */
167 if (!shared_ind(q->irq_ptr)) 142 if (!shared_ind(q->irq_ptr->dsci))
168 xchg(q->irq_ptr->dsci, 0); 143 xchg(q->irq_ptr->dsci, 0);
169 /* 144 /*
170 * Call inbound processing but not directly 145 * Call inbound processing but not directly
@@ -180,13 +155,8 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
180 * If the shared indicator was used clear it now after all queues 155 * If the shared indicator was used clear it now after all queues
181 * were processed. 156 * were processed.
182 */ 157 */
183 if (shared_ind_used()) { 158 if (si_used && shared_ind_set())
184 xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); 159 xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
185
186 /* prevent racing */
187 if (*tiqdio_alsi)
188 xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7);
189 }
190} 160}
191 161
192static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) 162static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
@@ -271,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr)
271{ 241{
272 if (!is_thinint_irq(irq_ptr)) 242 if (!is_thinint_irq(irq_ptr))
273 return 0; 243 return 0;
274
275 /* Check for aif time delay disablement. If installed,
276 * omit SVS even under LPAR
277 */
278 if (css_general_characteristics.aif_tdd)
279 css_qdio_omit_svs = 1;
280 return set_subchannel_ind(irq_ptr, 0); 244 return set_subchannel_ind(irq_ptr, 0);
281} 245}
282 246