diff options
Diffstat (limited to 'drivers/s390/cio/qdio_thinint.c')
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 114 |
1 files changed, 17 insertions, 97 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index c655d011a78d..981a77ea7ee2 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -43,9 +43,6 @@ struct indicator_t { | |||
43 | }; | 43 | }; |
44 | static struct indicator_t *q_indicators; | 44 | static struct indicator_t *q_indicators; |
45 | 45 | ||
46 | static void tiqdio_tasklet_fn(unsigned long data); | ||
47 | static DECLARE_TASKLET(tiqdio_tasklet, tiqdio_tasklet_fn, 0); | ||
48 | |||
49 | static int css_qdio_omit_svs; | 46 | static int css_qdio_omit_svs; |
50 | 47 | ||
51 | static inline unsigned long do_clear_global_summary(void) | 48 | static inline unsigned long do_clear_global_summary(void) |
@@ -103,11 +100,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) | |||
103 | xchg(irq_ptr->dsci, 1); | 100 | xchg(irq_ptr->dsci, 1); |
104 | } | 101 | } |
105 | 102 | ||
106 | /* | ||
107 | * we cannot stop the tiqdio tasklet here since it is for all | ||
108 | * thinint qdio devices and it must run as long as there is a | ||
109 | * thinint device left | ||
110 | */ | ||
111 | void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) | 103 | void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) |
112 | { | 104 | { |
113 | struct qdio_q *q; | 105 | struct qdio_q *q; |
@@ -126,79 +118,39 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) | |||
126 | } | 118 | } |
127 | } | 119 | } |
128 | 120 | ||
129 | static inline int tiqdio_inbound_q_done(struct qdio_q *q) | ||
130 | { | ||
131 | unsigned char state = 0; | ||
132 | |||
133 | if (!atomic_read(&q->nr_buf_used)) | ||
134 | return 1; | ||
135 | |||
136 | qdio_siga_sync_q(q); | ||
137 | get_buf_state(q, q->first_to_check, &state, 0); | ||
138 | |||
139 | if (state == SLSB_P_INPUT_PRIMED) | ||
140 | /* more work coming */ | ||
141 | return 0; | ||
142 | return 1; | ||
143 | } | ||
144 | |||
145 | static inline int shared_ind(struct qdio_irq *irq_ptr) | 121 | static inline int shared_ind(struct qdio_irq *irq_ptr) |
146 | { | 122 | { |
147 | return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; | 123 | return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; |
148 | } | 124 | } |
149 | 125 | ||
150 | static void __tiqdio_inbound_processing(struct qdio_q *q) | 126 | /** |
127 | * tiqdio_thinint_handler - thin interrupt handler for qdio | ||
128 | * @ind: pointer to adapter local summary indicator | ||
129 | * @drv_data: NULL | ||
130 | */ | ||
131 | static void tiqdio_thinint_handler(void *ind, void *drv_data) | ||
151 | { | 132 | { |
152 | qdio_perf_stat_inc(&perf_stats.thinint_inbound); | 133 | struct qdio_q *q; |
153 | qdio_sync_after_thinint(q); | 134 | |
135 | qdio_perf_stat_inc(&perf_stats.thin_int); | ||
154 | 136 | ||
155 | /* | 137 | /* |
156 | * Maybe we have work on our outbound queues... at least | 138 | * SVS only when needed: issue SVS to benefit from iqdio interrupt |
157 | * we have to check the PCI capable queues. | 139 | * avoidance (SVS clears adapter interrupt suppression overwrite) |
158 | */ | 140 | */ |
159 | qdio_check_outbound_after_thinint(q); | 141 | if (!css_qdio_omit_svs) |
160 | 142 | do_clear_global_summary(); | |
161 | if (!qdio_inbound_q_moved(q)) | ||
162 | return; | ||
163 | |||
164 | qdio_kick_handler(q); | ||
165 | |||
166 | if (!tiqdio_inbound_q_done(q)) { | ||
167 | qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); | ||
168 | if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) | ||
169 | tasklet_schedule(&q->tasklet); | ||
170 | } | ||
171 | 143 | ||
172 | qdio_stop_polling(q); | ||
173 | /* | 144 | /* |
174 | * We need to check again to not lose initiative after | 145 | * reset local summary indicator (tiqdio_alsi) to stop adapter |
175 | * resetting the ACK state. | 146 | * interrupts for now |
176 | */ | 147 | */ |
177 | if (!tiqdio_inbound_q_done(q)) { | 148 | xchg((u8 *)ind, 0); |
178 | qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); | ||
179 | if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) | ||
180 | tasklet_schedule(&q->tasklet); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | void tiqdio_inbound_processing(unsigned long data) | ||
185 | { | ||
186 | struct qdio_q *q = (struct qdio_q *)data; | ||
187 | |||
188 | __tiqdio_inbound_processing(q); | ||
189 | } | ||
190 | |||
191 | /* check for work on all inbound thinint queues */ | ||
192 | static void tiqdio_tasklet_fn(unsigned long data) | ||
193 | { | ||
194 | struct qdio_q *q; | ||
195 | |||
196 | qdio_perf_stat_inc(&perf_stats.tasklet_thinint); | ||
197 | again: | ||
198 | 149 | ||
199 | /* protect tiq_list entries, only changed in activate or shutdown */ | 150 | /* protect tiq_list entries, only changed in activate or shutdown */ |
200 | rcu_read_lock(); | 151 | rcu_read_lock(); |
201 | 152 | ||
153 | /* check for work on all inbound thinint queues */ | ||
202 | list_for_each_entry_rcu(q, &tiq_list, entry) | 154 | list_for_each_entry_rcu(q, &tiq_list, entry) |
203 | /* only process queues from changed sets */ | 155 | /* only process queues from changed sets */ |
204 | if (*q->irq_ptr->dsci) { | 156 | if (*q->irq_ptr->dsci) { |
@@ -226,37 +178,6 @@ again: | |||
226 | if (*tiqdio_alsi) | 178 | if (*tiqdio_alsi) |
227 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1); | 179 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1); |
228 | } | 180 | } |
229 | |||
230 | /* check for more work */ | ||
231 | if (*tiqdio_alsi) { | ||
232 | xchg(tiqdio_alsi, 0); | ||
233 | qdio_perf_stat_inc(&perf_stats.tasklet_thinint_loop); | ||
234 | goto again; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * tiqdio_thinint_handler - thin interrupt handler for qdio | ||
240 | * @ind: pointer to adapter local summary indicator | ||
241 | * @drv_data: NULL | ||
242 | */ | ||
243 | static void tiqdio_thinint_handler(void *ind, void *drv_data) | ||
244 | { | ||
245 | qdio_perf_stat_inc(&perf_stats.thin_int); | ||
246 | |||
247 | /* | ||
248 | * SVS only when needed: issue SVS to benefit from iqdio interrupt | ||
249 | * avoidance (SVS clears adapter interrupt suppression overwrite) | ||
250 | */ | ||
251 | if (!css_qdio_omit_svs) | ||
252 | do_clear_global_summary(); | ||
253 | |||
254 | /* | ||
255 | * reset local summary indicator (tiqdio_alsi) to stop adapter | ||
256 | * interrupts for now, the tasklet will clean all dsci's | ||
257 | */ | ||
258 | xchg((u8 *)ind, 0); | ||
259 | tasklet_hi_schedule(&tiqdio_tasklet); | ||
260 | } | 181 | } |
261 | 182 | ||
262 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) | 183 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) |
@@ -376,5 +297,4 @@ void __exit tiqdio_unregister_thinints(void) | |||
376 | s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); | 297 | s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); |
377 | isc_unregister(QDIO_AIRQ_ISC); | 298 | isc_unregister(QDIO_AIRQ_ISC); |
378 | } | 299 | } |
379 | tasklet_kill(&tiqdio_tasklet); | ||
380 | } | 300 | } |