diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/cio/qdio.h | 85 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_debug.c | 23 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 26 |
3 files changed, 101 insertions, 33 deletions
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 44f2f6a97f33..9a5283e16e38 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h | |||
@@ -210,16 +210,25 @@ struct qdio_dev_perf_stat { | |||
210 | unsigned int sqbs_partial; | 210 | unsigned int sqbs_partial; |
211 | }; | 211 | }; |
212 | 212 | ||
213 | struct qdio_queue_perf_stat { | ||
214 | /* | ||
215 | * Sorted into order-2 buckets: 1, 2-3, 4-7, ... 64-127, 128. | ||
216 | * Since max. 127 SBALs are scanned reuse entry for 128 as queue full | ||
217 | * aka 127 SBALs found. | ||
218 | */ | ||
219 | unsigned int nr_sbals[8]; | ||
220 | unsigned int nr_sbal_error; | ||
221 | unsigned int nr_sbal_nop; | ||
222 | unsigned int nr_sbal_total; | ||
223 | }; | ||
224 | |||
213 | struct qdio_input_q { | 225 | struct qdio_input_q { |
214 | /* input buffer acknowledgement flag */ | 226 | /* input buffer acknowledgement flag */ |
215 | int polling; | 227 | int polling; |
216 | |||
217 | /* first ACK'ed buffer */ | 228 | /* first ACK'ed buffer */ |
218 | int ack_start; | 229 | int ack_start; |
219 | |||
220 | /* how much sbals are acknowledged with qebsm */ | 230 | /* how much sbals are acknowledged with qebsm */ |
221 | int ack_count; | 231 | int ack_count; |
222 | |||
223 | /* last time of noticing incoming data */ | 232 | /* last time of noticing incoming data */ |
224 | u64 timestamp; | 233 | u64 timestamp; |
225 | }; | 234 | }; |
@@ -227,40 +236,27 @@ struct qdio_input_q { | |||
227 | struct qdio_output_q { | 236 | struct qdio_output_q { |
228 | /* PCIs are enabled for the queue */ | 237 | /* PCIs are enabled for the queue */ |
229 | int pci_out_enabled; | 238 | int pci_out_enabled; |
230 | |||
231 | /* IQDIO: output multiple buffers (enhanced SIGA) */ | 239 | /* IQDIO: output multiple buffers (enhanced SIGA) */ |
232 | int use_enh_siga; | 240 | int use_enh_siga; |
233 | |||
234 | /* timer to check for more outbound work */ | 241 | /* timer to check for more outbound work */ |
235 | struct timer_list timer; | 242 | struct timer_list timer; |
236 | }; | 243 | }; |
237 | 244 | ||
245 | /* | ||
246 | * Note on cache alignment: grouped slsb and write mostly data at the beginning | ||
247 | * sbal[] is read-only and starts on a new cacheline followed by read mostly. | ||
248 | */ | ||
238 | struct qdio_q { | 249 | struct qdio_q { |
239 | struct slsb slsb; | 250 | struct slsb slsb; |
251 | |||
240 | union { | 252 | union { |
241 | struct qdio_input_q in; | 253 | struct qdio_input_q in; |
242 | struct qdio_output_q out; | 254 | struct qdio_output_q out; |
243 | } u; | 255 | } u; |
244 | 256 | ||
245 | /* queue number */ | ||
246 | int nr; | ||
247 | |||
248 | /* bitmask of queue number */ | ||
249 | int mask; | ||
250 | |||
251 | /* input or output queue */ | ||
252 | int is_input_q; | ||
253 | |||
254 | /* list of thinint input queues */ | ||
255 | struct list_head entry; | ||
256 | |||
257 | /* upper-layer program handler */ | ||
258 | qdio_handler_t (*handler); | ||
259 | |||
260 | /* | 257 | /* |
261 | * inbound: next buffer the program should check for | 258 | * inbound: next buffer the program should check for |
262 | * outbound: next buffer to check for having been processed | 259 | * outbound: next buffer to check if adapter processed it |
263 | * by the card | ||
264 | */ | 260 | */ |
265 | int first_to_check; | 261 | int first_to_check; |
266 | 262 | ||
@@ -273,16 +269,32 @@ struct qdio_q { | |||
273 | /* number of buffers in use by the adapter */ | 269 | /* number of buffers in use by the adapter */ |
274 | atomic_t nr_buf_used; | 270 | atomic_t nr_buf_used; |
275 | 271 | ||
276 | struct qdio_irq *irq_ptr; | ||
277 | struct dentry *debugfs_q; | ||
278 | struct tasklet_struct tasklet; | ||
279 | |||
280 | /* error condition during a data transfer */ | 272 | /* error condition during a data transfer */ |
281 | unsigned int qdio_error; | 273 | unsigned int qdio_error; |
282 | 274 | ||
283 | struct sl *sl; | 275 | struct tasklet_struct tasklet; |
284 | struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; | 276 | struct qdio_queue_perf_stat q_stats; |
277 | |||
278 | struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q] ____cacheline_aligned; | ||
279 | |||
280 | /* queue number */ | ||
281 | int nr; | ||
282 | |||
283 | /* bitmask of queue number */ | ||
284 | int mask; | ||
285 | |||
286 | /* input or output queue */ | ||
287 | int is_input_q; | ||
288 | |||
289 | /* list of thinint input queues */ | ||
290 | struct list_head entry; | ||
291 | |||
292 | /* upper-layer program handler */ | ||
293 | qdio_handler_t (*handler); | ||
285 | 294 | ||
295 | struct dentry *debugfs_q; | ||
296 | struct qdio_irq *irq_ptr; | ||
297 | struct sl *sl; | ||
286 | /* | 298 | /* |
287 | * Warning: Leave this member at the end so it won't be cleared in | 299 | * Warning: Leave this member at the end so it won't be cleared in |
288 | * qdio_fill_qs. A page is allocated under this pointer and used for | 300 | * qdio_fill_qs. A page is allocated under this pointer and used for |
@@ -341,9 +353,20 @@ struct qdio_irq { | |||
341 | (irq->qib.qfmt == QDIO_IQDIO_QFMT || \ | 353 | (irq->qib.qfmt == QDIO_IQDIO_QFMT || \ |
342 | css_general_characteristics.aif_osa) | 354 | css_general_characteristics.aif_osa) |
343 | 355 | ||
344 | #define qperf(qdev,attr) qdev->perf_stat.attr | 356 | #define qperf(__qdev, __attr) ((__qdev)->perf_stat.(__attr)) |
345 | #define qperf_inc(q,attr) if (q->irq_ptr->perf_stat_enabled) \ | 357 | |
346 | q->irq_ptr->perf_stat.attr++ | 358 | #define qperf_inc(__q, __attr) \ |
359 | ({ \ | ||
360 | struct qdio_irq *qdev = (__q)->irq_ptr; \ | ||
361 | if (qdev->perf_stat_enabled) \ | ||
362 | (qdev->perf_stat.__attr)++; \ | ||
363 | }) | ||
364 | |||
365 | static inline void account_sbals_error(struct qdio_q *q, int count) | ||
366 | { | ||
367 | q->q_stats.nr_sbal_error += count; | ||
368 | q->q_stats.nr_sbal_total += count; | ||
369 | } | ||
347 | 370 | ||
348 | /* the highest iqdio queue is used for multicast */ | 371 | /* the highest iqdio queue is used for multicast */ |
349 | static inline int multicast_outbound(struct qdio_q *q) | 372 | static inline int multicast_outbound(struct qdio_q *q) |
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index f49761ff9a00..c94eb2a0fa2e 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c | |||
@@ -60,7 +60,7 @@ static int qstat_show(struct seq_file *m, void *v) | |||
60 | seq_printf(m, "ftc: %d last_move: %d\n", q->first_to_check, q->last_move); | 60 | seq_printf(m, "ftc: %d last_move: %d\n", q->first_to_check, q->last_move); |
61 | seq_printf(m, "polling: %d ack start: %d ack count: %d\n", | 61 | seq_printf(m, "polling: %d ack start: %d ack count: %d\n", |
62 | q->u.in.polling, q->u.in.ack_start, q->u.in.ack_count); | 62 | q->u.in.polling, q->u.in.ack_start, q->u.in.ack_count); |
63 | seq_printf(m, "slsb buffer states:\n"); | 63 | seq_printf(m, "SBAL states:\n"); |
64 | seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); | 64 | seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); |
65 | 65 | ||
66 | for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { | 66 | for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { |
@@ -97,6 +97,20 @@ static int qstat_show(struct seq_file *m, void *v) | |||
97 | } | 97 | } |
98 | seq_printf(m, "\n"); | 98 | seq_printf(m, "\n"); |
99 | seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n"); | 99 | seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n"); |
100 | |||
101 | seq_printf(m, "\nSBAL statistics:"); | ||
102 | if (!q->irq_ptr->perf_stat_enabled) { | ||
103 | seq_printf(m, " disabled\n"); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | seq_printf(m, "\n1 2.. 4.. 8.. " | ||
108 | "16.. 32.. 64.. 127\n"); | ||
109 | for (i = 0; i < ARRAY_SIZE(q->q_stats.nr_sbals); i++) | ||
110 | seq_printf(m, "%-10u ", q->q_stats.nr_sbals[i]); | ||
111 | seq_printf(m, "\nError NOP Total\n%-10u %-10u %-10u\n\n", | ||
112 | q->q_stats.nr_sbal_error, q->q_stats.nr_sbal_nop, | ||
113 | q->q_stats.nr_sbal_total); | ||
100 | return 0; | 114 | return 0; |
101 | } | 115 | } |
102 | 116 | ||
@@ -181,9 +195,10 @@ static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf, | |||
181 | { | 195 | { |
182 | struct seq_file *seq = file->private_data; | 196 | struct seq_file *seq = file->private_data; |
183 | struct qdio_irq *irq_ptr = seq->private; | 197 | struct qdio_irq *irq_ptr = seq->private; |
198 | struct qdio_q *q; | ||
184 | unsigned long val; | 199 | unsigned long val; |
185 | char buf[8]; | 200 | char buf[8]; |
186 | int ret; | 201 | int ret, i; |
187 | 202 | ||
188 | if (!irq_ptr) | 203 | if (!irq_ptr) |
189 | return 0; | 204 | return 0; |
@@ -201,6 +216,10 @@ static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf, | |||
201 | case 0: | 216 | case 0: |
202 | irq_ptr->perf_stat_enabled = 0; | 217 | irq_ptr->perf_stat_enabled = 0; |
203 | memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat)); | 218 | memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat)); |
219 | for_each_input_queue(irq_ptr, q, i) | ||
220 | memset(&q->q_stats, 0, sizeof(q->q_stats)); | ||
221 | for_each_output_queue(irq_ptr, q, i) | ||
222 | memset(&q->q_stats, 0, sizeof(q->q_stats)); | ||
204 | break; | 223 | break; |
205 | case 1: | 224 | case 1: |
206 | irq_ptr->perf_stat_enabled = 1; | 225 | irq_ptr->perf_stat_enabled = 1; |
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 62b654af9237..35dfc3cb2aae 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
@@ -392,6 +392,20 @@ static inline void qdio_stop_polling(struct qdio_q *q) | |||
392 | set_buf_state(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT); | 392 | set_buf_state(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT); |
393 | } | 393 | } |
394 | 394 | ||
395 | static inline void account_sbals(struct qdio_q *q, int count) | ||
396 | { | ||
397 | int pos = 0; | ||
398 | |||
399 | q->q_stats.nr_sbal_total += count; | ||
400 | if (count == QDIO_MAX_BUFFERS_MASK) { | ||
401 | q->q_stats.nr_sbals[7]++; | ||
402 | return; | ||
403 | } | ||
404 | while (count >>= 1) | ||
405 | pos++; | ||
406 | q->q_stats.nr_sbals[pos]++; | ||
407 | } | ||
408 | |||
395 | static void announce_buffer_error(struct qdio_q *q, int count) | 409 | static void announce_buffer_error(struct qdio_q *q, int count) |
396 | { | 410 | { |
397 | q->qdio_error |= QDIO_ERROR_SLSB_STATE; | 411 | q->qdio_error |= QDIO_ERROR_SLSB_STATE; |
@@ -487,16 +501,22 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) | |||
487 | q->first_to_check = add_buf(q->first_to_check, count); | 501 | q->first_to_check = add_buf(q->first_to_check, count); |
488 | if (atomic_sub(count, &q->nr_buf_used) == 0) | 502 | if (atomic_sub(count, &q->nr_buf_used) == 0) |
489 | qperf_inc(q, inbound_queue_full); | 503 | qperf_inc(q, inbound_queue_full); |
504 | if (q->irq_ptr->perf_stat_enabled) | ||
505 | account_sbals(q, count); | ||
490 | break; | 506 | break; |
491 | case SLSB_P_INPUT_ERROR: | 507 | case SLSB_P_INPUT_ERROR: |
492 | announce_buffer_error(q, count); | 508 | announce_buffer_error(q, count); |
493 | /* process the buffer, the upper layer will take care of it */ | 509 | /* process the buffer, the upper layer will take care of it */ |
494 | q->first_to_check = add_buf(q->first_to_check, count); | 510 | q->first_to_check = add_buf(q->first_to_check, count); |
495 | atomic_sub(count, &q->nr_buf_used); | 511 | atomic_sub(count, &q->nr_buf_used); |
512 | if (q->irq_ptr->perf_stat_enabled) | ||
513 | account_sbals_error(q, count); | ||
496 | break; | 514 | break; |
497 | case SLSB_CU_INPUT_EMPTY: | 515 | case SLSB_CU_INPUT_EMPTY: |
498 | case SLSB_P_INPUT_NOT_INIT: | 516 | case SLSB_P_INPUT_NOT_INIT: |
499 | case SLSB_P_INPUT_ACK: | 517 | case SLSB_P_INPUT_ACK: |
518 | if (q->irq_ptr->perf_stat_enabled) | ||
519 | q->q_stats.nr_sbal_nop++; | ||
500 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop"); | 520 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop"); |
501 | break; | 521 | break; |
502 | default: | 522 | default: |
@@ -643,15 +663,21 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) | |||
643 | 663 | ||
644 | atomic_sub(count, &q->nr_buf_used); | 664 | atomic_sub(count, &q->nr_buf_used); |
645 | q->first_to_check = add_buf(q->first_to_check, count); | 665 | q->first_to_check = add_buf(q->first_to_check, count); |
666 | if (q->irq_ptr->perf_stat_enabled) | ||
667 | account_sbals(q, count); | ||
646 | break; | 668 | break; |
647 | case SLSB_P_OUTPUT_ERROR: | 669 | case SLSB_P_OUTPUT_ERROR: |
648 | announce_buffer_error(q, count); | 670 | announce_buffer_error(q, count); |
649 | /* process the buffer, the upper layer will take care of it */ | 671 | /* process the buffer, the upper layer will take care of it */ |
650 | q->first_to_check = add_buf(q->first_to_check, count); | 672 | q->first_to_check = add_buf(q->first_to_check, count); |
651 | atomic_sub(count, &q->nr_buf_used); | 673 | atomic_sub(count, &q->nr_buf_used); |
674 | if (q->irq_ptr->perf_stat_enabled) | ||
675 | account_sbals_error(q, count); | ||
652 | break; | 676 | break; |
653 | case SLSB_CU_OUTPUT_PRIMED: | 677 | case SLSB_CU_OUTPUT_PRIMED: |
654 | /* the adapter has not fetched the output yet */ | 678 | /* the adapter has not fetched the output yet */ |
679 | if (q->irq_ptr->perf_stat_enabled) | ||
680 | q->q_stats.nr_sbal_nop++; | ||
655 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr); | 681 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr); |
656 | break; | 682 | break; |
657 | case SLSB_P_OUTPUT_NOT_INIT: | 683 | case SLSB_P_OUTPUT_NOT_INIT: |