diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2008-12-25 07:38:47 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 07:38:59 -0500 |
commit | 50f769df1c4bea766c4eb927eae35728fb93e305 (patch) | |
tree | c86228e6965de0ea84bddc2104d7dfa339e2a187 /drivers/s390/cio | |
parent | 22f9934767f49012ffbae753b28b8055bd28348f (diff) |
[S390] qdio: improve inbound buffer acknowledgement
- Use automatic acknowledgement of incoming buffers in QEBSM mode
- Move ACK for non-QEBSM mode always to the newest buffer to prevent
a race with qdio_stop_polling
- Remove the polling spinlock, the upper layer drivers return new buffers
in the same code path and could not run in parallel
- Don't flood the error log in case of no-target-buffer-empty
- In handle_inbound we check if we would overwrite an ACK'ed buffer, if so
advance the pointer to the oldest ACK'ed buffer so we don't overwrite an
empty buffer in qdio_stop_polling
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/qdio.h | 19 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_debug.c | 7 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 181 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_perf.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_perf.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_setup.c | 1 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 2 |
7 files changed, 139 insertions, 74 deletions
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 7b50882577d5..c60f2566d28c 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h | |||
@@ -112,12 +112,12 @@ static inline int do_sqbs(u64 token, unsigned char state, int queue, | |||
112 | } | 112 | } |
113 | 113 | ||
114 | static inline int do_eqbs(u64 token, unsigned char *state, int queue, | 114 | static inline int do_eqbs(u64 token, unsigned char *state, int queue, |
115 | int *start, int *count) | 115 | int *start, int *count, int ack) |
116 | { | 116 | { |
117 | register unsigned long _ccq asm ("0") = *count; | 117 | register unsigned long _ccq asm ("0") = *count; |
118 | register unsigned long _token asm ("1") = token; | 118 | register unsigned long _token asm ("1") = token; |
119 | unsigned long _queuestart = ((unsigned long)queue << 32) | *start; | 119 | unsigned long _queuestart = ((unsigned long)queue << 32) | *start; |
120 | unsigned long _state = 0; | 120 | unsigned long _state = (unsigned long)ack << 63; |
121 | 121 | ||
122 | asm volatile( | 122 | asm volatile( |
123 | " .insn rrf,0xB99c0000,%1,%2,0,0" | 123 | " .insn rrf,0xB99c0000,%1,%2,0,0" |
@@ -134,7 +134,7 @@ static inline int do_eqbs(u64 token, unsigned char *state, int queue, | |||
134 | static inline int do_sqbs(u64 token, unsigned char state, int queue, | 134 | static inline int do_sqbs(u64 token, unsigned char state, int queue, |
135 | int *start, int *count) { return 0; } | 135 | int *start, int *count) { return 0; } |
136 | static inline int do_eqbs(u64 token, unsigned char *state, int queue, | 136 | static inline int do_eqbs(u64 token, unsigned char *state, int queue, |
137 | int *start, int *count) { return 0; } | 137 | int *start, int *count, int ack) { return 0; } |
138 | #endif /* CONFIG_64BIT */ | 138 | #endif /* CONFIG_64BIT */ |
139 | 139 | ||
140 | struct qdio_irq; | 140 | struct qdio_irq; |
@@ -187,11 +187,11 @@ struct qdio_input_q { | |||
187 | /* input buffer acknowledgement flag */ | 187 | /* input buffer acknowledgement flag */ |
188 | int polling; | 188 | int polling; |
189 | 189 | ||
190 | /* how much sbals are acknowledged with qebsm */ | ||
191 | int ack_count; | ||
192 | |||
190 | /* last time of noticing incoming data */ | 193 | /* last time of noticing incoming data */ |
191 | u64 timestamp; | 194 | u64 timestamp; |
192 | |||
193 | /* lock for clearing the acknowledgement */ | ||
194 | spinlock_t lock; | ||
195 | }; | 195 | }; |
196 | 196 | ||
197 | struct qdio_output_q { | 197 | struct qdio_output_q { |
@@ -351,10 +351,13 @@ static inline unsigned long long get_usecs(void) | |||
351 | ((bufnr + 1) & QDIO_MAX_BUFFERS_MASK) | 351 | ((bufnr + 1) & QDIO_MAX_BUFFERS_MASK) |
352 | #define add_buf(bufnr, inc) \ | 352 | #define add_buf(bufnr, inc) \ |
353 | ((bufnr + inc) & QDIO_MAX_BUFFERS_MASK) | 353 | ((bufnr + inc) & QDIO_MAX_BUFFERS_MASK) |
354 | #define sub_buf(bufnr, dec) \ | ||
355 | ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK) | ||
354 | 356 | ||
355 | /* prototypes for thin interrupt */ | 357 | /* prototypes for thin interrupt */ |
356 | void qdio_sync_after_thinint(struct qdio_q *q); | 358 | void qdio_sync_after_thinint(struct qdio_q *q); |
357 | int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state); | 359 | int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state, |
360 | int auto_ack); | ||
358 | void qdio_check_outbound_after_thinint(struct qdio_q *q); | 361 | void qdio_check_outbound_after_thinint(struct qdio_q *q); |
359 | int qdio_inbound_q_moved(struct qdio_q *q); | 362 | int qdio_inbound_q_moved(struct qdio_q *q); |
360 | void qdio_kick_inbound_handler(struct qdio_q *q); | 363 | void qdio_kick_inbound_handler(struct qdio_q *q); |
@@ -388,6 +391,8 @@ int qdio_setup_irq(struct qdio_initialize *init_data); | |||
388 | void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, | 391 | void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, |
389 | struct ccw_device *cdev); | 392 | struct ccw_device *cdev); |
390 | void qdio_release_memory(struct qdio_irq *irq_ptr); | 393 | void qdio_release_memory(struct qdio_irq *irq_ptr); |
394 | int qdio_setup_create_sysfs(struct ccw_device *cdev); | ||
395 | void qdio_setup_destroy_sysfs(struct ccw_device *cdev); | ||
391 | int qdio_setup_init(void); | 396 | int qdio_setup_init(void); |
392 | void qdio_setup_exit(void); | 397 | void qdio_setup_exit(void); |
393 | 398 | ||
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 0add07c1b5d2..f8a3b6967f69 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c | |||
@@ -60,16 +60,18 @@ static int qstat_show(struct seq_file *m, void *v) | |||
60 | if (!q) | 60 | if (!q) |
61 | return 0; | 61 | return 0; |
62 | 62 | ||
63 | seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci); | 63 | seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci); |
64 | seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used)); | 64 | seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used)); |
65 | seq_printf(m, "ftc: %d\n", q->first_to_check); | 65 | seq_printf(m, "ftc: %d\n", q->first_to_check); |
66 | seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc); | 66 | seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc); |
67 | seq_printf(m, "polling: %d\n", q->u.in.polling); | 67 | seq_printf(m, "polling: %d\n", q->u.in.polling); |
68 | seq_printf(m, "ack count: %d\n", q->u.in.ack_count); | ||
68 | seq_printf(m, "slsb buffer states:\n"); | 69 | seq_printf(m, "slsb buffer states:\n"); |
70 | seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); | ||
69 | 71 | ||
70 | qdio_siga_sync_q(q); | 72 | qdio_siga_sync_q(q); |
71 | for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { | 73 | for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { |
72 | get_buf_state(q, i, &state); | 74 | get_buf_state(q, i, &state, 0); |
73 | switch (state) { | 75 | switch (state) { |
74 | case SLSB_P_INPUT_NOT_INIT: | 76 | case SLSB_P_INPUT_NOT_INIT: |
75 | case SLSB_P_OUTPUT_NOT_INIT: | 77 | case SLSB_P_OUTPUT_NOT_INIT: |
@@ -101,6 +103,7 @@ static int qstat_show(struct seq_file *m, void *v) | |||
101 | seq_printf(m, "\n"); | 103 | seq_printf(m, "\n"); |
102 | } | 104 | } |
103 | seq_printf(m, "\n"); | 105 | seq_printf(m, "\n"); |
106 | seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n"); | ||
104 | return 0; | 107 | return 0; |
105 | } | 108 | } |
106 | 109 | ||
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index c810214d3d08..0b4c09cf6a47 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
@@ -112,12 +112,13 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) | |||
112 | * @state: state of the extracted buffers | 112 | * @state: state of the extracted buffers |
113 | * @start: buffer number to start at | 113 | * @start: buffer number to start at |
114 | * @count: count of buffers to examine | 114 | * @count: count of buffers to examine |
115 | * @auto_ack: automatically acknowledge buffers | ||
115 | * | 116 | * |
116 | * Returns the number of successfull extracted equal buffer states. | 117 | * Returns the number of successfull extracted equal buffer states. |
117 | * Stops processing if a state is different from the last buffers state. | 118 | * Stops processing if a state is different from the last buffers state. |
118 | */ | 119 | */ |
119 | static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, | 120 | static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, |
120 | int start, int count) | 121 | int start, int count, int auto_ack) |
121 | { | 122 | { |
122 | unsigned int ccq = 0; | 123 | unsigned int ccq = 0; |
123 | int tmp_count = count, tmp_start = start; | 124 | int tmp_count = count, tmp_start = start; |
@@ -130,7 +131,8 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, | |||
130 | if (!q->is_input_q) | 131 | if (!q->is_input_q) |
131 | nr += q->irq_ptr->nr_input_qs; | 132 | nr += q->irq_ptr->nr_input_qs; |
132 | again: | 133 | again: |
133 | ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); | 134 | ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count, |
135 | auto_ack); | ||
134 | rc = qdio_check_ccq(q, ccq); | 136 | rc = qdio_check_ccq(q, ccq); |
135 | 137 | ||
136 | /* At least one buffer was processed, return and extract the remaining | 138 | /* At least one buffer was processed, return and extract the remaining |
@@ -176,6 +178,9 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, | |||
176 | int nr = q->nr; | 178 | int nr = q->nr; |
177 | int rc; | 179 | int rc; |
178 | 180 | ||
181 | if (!count) | ||
182 | return 0; | ||
183 | |||
179 | BUG_ON(!q->irq_ptr->sch_token); | 184 | BUG_ON(!q->irq_ptr->sch_token); |
180 | qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); | 185 | qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); |
181 | 186 | ||
@@ -203,7 +208,8 @@ again: | |||
203 | 208 | ||
204 | /* returns number of examined buffers and their common state in *state */ | 209 | /* returns number of examined buffers and their common state in *state */ |
205 | static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, | 210 | static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, |
206 | unsigned char *state, unsigned int count) | 211 | unsigned char *state, unsigned int count, |
212 | int auto_ack) | ||
207 | { | 213 | { |
208 | unsigned char __state = 0; | 214 | unsigned char __state = 0; |
209 | int i; | 215 | int i; |
@@ -212,7 +218,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, | |||
212 | BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q); | 218 | BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q); |
213 | 219 | ||
214 | if (is_qebsm(q)) | 220 | if (is_qebsm(q)) |
215 | return qdio_do_eqbs(q, state, bufnr, count); | 221 | return qdio_do_eqbs(q, state, bufnr, count, auto_ack); |
216 | 222 | ||
217 | for (i = 0; i < count; i++) { | 223 | for (i = 0; i < count; i++) { |
218 | if (!__state) | 224 | if (!__state) |
@@ -226,9 +232,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, | |||
226 | } | 232 | } |
227 | 233 | ||
228 | inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, | 234 | inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, |
229 | unsigned char *state) | 235 | unsigned char *state, int auto_ack) |
230 | { | 236 | { |
231 | return get_buf_states(q, bufnr, state, 1); | 237 | return get_buf_states(q, bufnr, state, 1, auto_ack); |
232 | } | 238 | } |
233 | 239 | ||
234 | /* wrap-around safe setting of slsb states, returns number of changed buffers */ | 240 | /* wrap-around safe setting of slsb states, returns number of changed buffers */ |
@@ -376,29 +382,91 @@ void qdio_sync_after_thinint(struct qdio_q *q) | |||
376 | 382 | ||
377 | inline void qdio_stop_polling(struct qdio_q *q) | 383 | inline void qdio_stop_polling(struct qdio_q *q) |
378 | { | 384 | { |
379 | spin_lock_bh(&q->u.in.lock); | 385 | if (!q->u.in.polling) |
380 | if (!q->u.in.polling) { | ||
381 | spin_unlock_bh(&q->u.in.lock); | ||
382 | return; | 386 | return; |
383 | } | 387 | |
384 | q->u.in.polling = 0; | 388 | q->u.in.polling = 0; |
385 | qdio_perf_stat_inc(&perf_stats.debug_stop_polling); | 389 | qdio_perf_stat_inc(&perf_stats.debug_stop_polling); |
386 | 390 | ||
387 | /* show the card that we are not polling anymore */ | 391 | /* show the card that we are not polling anymore */ |
388 | set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); | 392 | if (is_qebsm(q)) { |
389 | spin_unlock_bh(&q->u.in.lock); | 393 | set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT, |
394 | q->u.in.ack_count); | ||
395 | q->u.in.ack_count = 0; | ||
396 | } else | ||
397 | set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); | ||
390 | } | 398 | } |
391 | 399 | ||
392 | static void announce_buffer_error(struct qdio_q *q) | 400 | static void announce_buffer_error(struct qdio_q *q, int count) |
393 | { | 401 | { |
402 | q->qdio_error = QDIO_ERROR_SLSB_STATE; | ||
403 | |||
404 | /* special handling for no target buffer empty */ | ||
405 | if ((!q->is_input_q && | ||
406 | (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) { | ||
407 | qdio_perf_stat_inc(&perf_stats.outbound_target_full); | ||
408 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d", | ||
409 | q->first_to_check); | ||
410 | return; | ||
411 | } | ||
412 | |||
394 | DBF_ERROR("%4x BUF ERROR", SCH_NO(q)); | 413 | DBF_ERROR("%4x BUF ERROR", SCH_NO(q)); |
395 | DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr); | 414 | DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr); |
396 | DBF_ERROR("FTC:%3d", q->first_to_check); | 415 | DBF_ERROR("FTC:%3d C:%3d", q->first_to_check, count); |
397 | DBF_ERROR("F14:%2x F15:%2x", | 416 | DBF_ERROR("F14:%2x F15:%2x", |
398 | q->sbal[q->first_to_check]->element[14].flags & 0xff, | 417 | q->sbal[q->first_to_check]->element[14].flags & 0xff, |
399 | q->sbal[q->first_to_check]->element[15].flags & 0xff); | 418 | q->sbal[q->first_to_check]->element[15].flags & 0xff); |
419 | } | ||
400 | 420 | ||
401 | q->qdio_error = QDIO_ERROR_SLSB_STATE; | 421 | static inline void inbound_primed(struct qdio_q *q, int count) |
422 | { | ||
423 | int new; | ||
424 | |||
425 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); | ||
426 | |||
427 | /* for QEBSM the ACK was already set by EQBS */ | ||
428 | if (is_qebsm(q)) { | ||
429 | if (!q->u.in.polling) { | ||
430 | q->u.in.polling = 1; | ||
431 | q->u.in.ack_count = count; | ||
432 | q->last_move_ftc = q->first_to_check; | ||
433 | return; | ||
434 | } | ||
435 | |||
436 | /* delete the previous ACK's */ | ||
437 | set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT, | ||
438 | q->u.in.ack_count); | ||
439 | q->u.in.ack_count = count; | ||
440 | q->last_move_ftc = q->first_to_check; | ||
441 | return; | ||
442 | } | ||
443 | |||
444 | /* | ||
445 | * ACK the newest buffer. The ACK will be removed in qdio_stop_polling | ||
446 | * or by the next inbound run. | ||
447 | */ | ||
448 | new = add_buf(q->first_to_check, count - 1); | ||
449 | if (q->u.in.polling) { | ||
450 | /* reset the previous ACK but first set the new one */ | ||
451 | set_buf_state(q, new, SLSB_P_INPUT_ACK); | ||
452 | set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); | ||
453 | } | ||
454 | else { | ||
455 | q->u.in.polling = 1; | ||
456 | set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK); | ||
457 | } | ||
458 | |||
459 | q->last_move_ftc = new; | ||
460 | count--; | ||
461 | if (!count) | ||
462 | return; | ||
463 | |||
464 | /* | ||
465 | * Need to change all PRIMED buffers to NOT_INIT, otherwise | ||
466 | * we're loosing initiative in the thinint code. | ||
467 | */ | ||
468 | set_buf_states(q, next_buf(q->first_to_check), SLSB_P_INPUT_NOT_INIT, | ||
469 | count); | ||
402 | } | 470 | } |
403 | 471 | ||
404 | static int get_inbound_buffer_frontier(struct qdio_q *q) | 472 | static int get_inbound_buffer_frontier(struct qdio_q *q) |
@@ -407,13 +475,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) | |||
407 | unsigned char state; | 475 | unsigned char state; |
408 | 476 | ||
409 | /* | 477 | /* |
410 | * If we still poll don't update last_move_ftc, keep the | ||
411 | * previously ACK buffer there. | ||
412 | */ | ||
413 | if (!q->u.in.polling) | ||
414 | q->last_move_ftc = q->first_to_check; | ||
415 | |||
416 | /* | ||
417 | * Don't check 128 buffers, as otherwise qdio_inbound_q_moved | 478 | * Don't check 128 buffers, as otherwise qdio_inbound_q_moved |
418 | * would return 0. | 479 | * would return 0. |
419 | */ | 480 | */ |
@@ -433,34 +494,13 @@ check_next: | |||
433 | if (q->first_to_check == stop) | 494 | if (q->first_to_check == stop) |
434 | goto out; | 495 | goto out; |
435 | 496 | ||
436 | count = get_buf_states(q, q->first_to_check, &state, count); | 497 | count = get_buf_states(q, q->first_to_check, &state, count, 1); |
437 | if (!count) | 498 | if (!count) |
438 | goto out; | 499 | goto out; |
439 | 500 | ||
440 | switch (state) { | 501 | switch (state) { |
441 | case SLSB_P_INPUT_PRIMED: | 502 | case SLSB_P_INPUT_PRIMED: |
442 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); | 503 | inbound_primed(q, count); |
443 | |||
444 | /* | ||
445 | * Only ACK the first buffer. The ACK will be removed in | ||
446 | * qdio_stop_polling. | ||
447 | */ | ||
448 | if (q->u.in.polling) | ||
449 | state = SLSB_P_INPUT_NOT_INIT; | ||
450 | else { | ||
451 | q->u.in.polling = 1; | ||
452 | state = SLSB_P_INPUT_ACK; | ||
453 | } | ||
454 | set_buf_state(q, q->first_to_check, state); | ||
455 | |||
456 | /* | ||
457 | * Need to change all PRIMED buffers to NOT_INIT, otherwise | ||
458 | * we're loosing initiative in the thinint code. | ||
459 | */ | ||
460 | if (count > 1) | ||
461 | set_buf_states(q, next_buf(q->first_to_check), | ||
462 | SLSB_P_INPUT_NOT_INIT, count - 1); | ||
463 | |||
464 | /* | 504 | /* |
465 | * No siga-sync needed for non-qebsm here, as the inbound queue | 505 | * No siga-sync needed for non-qebsm here, as the inbound queue |
466 | * will be synced on the next siga-r, resp. | 506 | * will be synced on the next siga-r, resp. |
@@ -470,7 +510,7 @@ check_next: | |||
470 | atomic_sub(count, &q->nr_buf_used); | 510 | atomic_sub(count, &q->nr_buf_used); |
471 | goto check_next; | 511 | goto check_next; |
472 | case SLSB_P_INPUT_ERROR: | 512 | case SLSB_P_INPUT_ERROR: |
473 | announce_buffer_error(q); | 513 | announce_buffer_error(q, count); |
474 | /* process the buffer, the upper layer will take care of it */ | 514 | /* process the buffer, the upper layer will take care of it */ |
475 | q->first_to_check = add_buf(q->first_to_check, count); | 515 | q->first_to_check = add_buf(q->first_to_check, count); |
476 | atomic_sub(count, &q->nr_buf_used); | 516 | atomic_sub(count, &q->nr_buf_used); |
@@ -516,7 +556,7 @@ static int qdio_inbound_q_done(struct qdio_q *q) | |||
516 | */ | 556 | */ |
517 | qdio_siga_sync_q(q); | 557 | qdio_siga_sync_q(q); |
518 | 558 | ||
519 | get_buf_state(q, q->first_to_check, &state); | 559 | get_buf_state(q, q->first_to_check, &state, 0); |
520 | if (state == SLSB_P_INPUT_PRIMED) | 560 | if (state == SLSB_P_INPUT_PRIMED) |
521 | /* we got something to do */ | 561 | /* we got something to do */ |
522 | return 0; | 562 | return 0; |
@@ -619,7 +659,7 @@ check_next: | |||
619 | if (q->first_to_check == stop) | 659 | if (q->first_to_check == stop) |
620 | return q->first_to_check; | 660 | return q->first_to_check; |
621 | 661 | ||
622 | count = get_buf_states(q, q->first_to_check, &state, count); | 662 | count = get_buf_states(q, q->first_to_check, &state, count, 0); |
623 | if (!count) | 663 | if (!count) |
624 | return q->first_to_check; | 664 | return q->first_to_check; |
625 | 665 | ||
@@ -638,7 +678,7 @@ check_next: | |||
638 | break; | 678 | break; |
639 | goto check_next; | 679 | goto check_next; |
640 | case SLSB_P_OUTPUT_ERROR: | 680 | case SLSB_P_OUTPUT_ERROR: |
641 | announce_buffer_error(q); | 681 | announce_buffer_error(q, count); |
642 | /* process the buffer, the upper layer will take care of it */ | 682 | /* process the buffer, the upper layer will take care of it */ |
643 | q->first_to_check = add_buf(q->first_to_check, count); | 683 | q->first_to_check = add_buf(q->first_to_check, count); |
644 | atomic_sub(count, &q->nr_buf_used); | 684 | atomic_sub(count, &q->nr_buf_used); |
@@ -1451,23 +1491,38 @@ static inline int buf_in_between(int bufnr, int start, int count) | |||
1451 | static void handle_inbound(struct qdio_q *q, unsigned int callflags, | 1491 | static void handle_inbound(struct qdio_q *q, unsigned int callflags, |
1452 | int bufnr, int count) | 1492 | int bufnr, int count) |
1453 | { | 1493 | { |
1454 | unsigned long flags; | 1494 | int used, rc, diff; |
1455 | int used, rc; | ||
1456 | 1495 | ||
1457 | /* | 1496 | if (!q->u.in.polling) |
1458 | * do_QDIO could run in parallel with the queue tasklet so the | 1497 | goto set; |
1459 | * upper-layer programm could empty the ACK'ed buffer here. | 1498 | |
1460 | * If that happens we must clear the polling flag, otherwise | 1499 | /* protect against stop polling setting an ACK for an emptied slsb */ |
1461 | * qdio_stop_polling() could set the buffer to NOT_INIT after | 1500 | if (count == QDIO_MAX_BUFFERS_PER_Q) { |
1462 | * it was set to EMPTY which would kill us. | 1501 | /* overwriting everything, just delete polling status */ |
1463 | */ | 1502 | q->u.in.polling = 0; |
1464 | spin_lock_irqsave(&q->u.in.lock, flags); | 1503 | q->u.in.ack_count = 0; |
1465 | if (q->u.in.polling) | 1504 | goto set; |
1466 | if (buf_in_between(q->last_move_ftc, bufnr, count)) | 1505 | } else if (buf_in_between(q->last_move_ftc, bufnr, count)) { |
1506 | if (is_qebsm(q)) { | ||
1507 | /* partial overwrite, just update last_move_ftc */ | ||
1508 | diff = add_buf(bufnr, count); | ||
1509 | diff = sub_buf(diff, q->last_move_ftc); | ||
1510 | q->u.in.ack_count -= diff; | ||
1511 | if (q->u.in.ack_count <= 0) { | ||
1512 | q->u.in.polling = 0; | ||
1513 | q->u.in.ack_count = 0; | ||
1514 | /* TODO: must we set last_move_ftc to something meaningful? */ | ||
1515 | goto set; | ||
1516 | } | ||
1517 | q->last_move_ftc = add_buf(q->last_move_ftc, diff); | ||
1518 | } | ||
1519 | else | ||
1520 | /* the only ACK will be deleted, so stop polling */ | ||
1467 | q->u.in.polling = 0; | 1521 | q->u.in.polling = 0; |
1522 | } | ||
1468 | 1523 | ||
1524 | set: | ||
1469 | count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count); | 1525 | count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count); |
1470 | spin_unlock_irqrestore(&q->u.in.lock, flags); | ||
1471 | 1526 | ||
1472 | used = atomic_add_return(count, &q->nr_buf_used) - count; | 1527 | used = atomic_add_return(count, &q->nr_buf_used) - count; |
1473 | BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q); | 1528 | BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q); |
@@ -1535,7 +1590,7 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, | |||
1535 | } | 1590 | } |
1536 | 1591 | ||
1537 | /* try to fast requeue buffers */ | 1592 | /* try to fast requeue buffers */ |
1538 | get_buf_state(q, prev_buf(bufnr), &state); | 1593 | get_buf_state(q, prev_buf(bufnr), &state, 0); |
1539 | if (state != SLSB_CU_OUTPUT_PRIMED) | 1594 | if (state != SLSB_CU_OUTPUT_PRIMED) |
1540 | qdio_kick_outbound_q(q); | 1595 | qdio_kick_outbound_q(q); |
1541 | else { | 1596 | else { |
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c index bec01100f8e8..136d0f0b1e93 100644 --- a/drivers/s390/cio/qdio_perf.c +++ b/drivers/s390/cio/qdio_perf.c | |||
@@ -74,6 +74,8 @@ static int qdio_perf_proc_show(struct seq_file *m, void *v) | |||
74 | seq_printf(m, "\n"); | 74 | seq_printf(m, "\n"); |
75 | seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n", | 75 | seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n", |
76 | (long)atomic_long_read(&perf_stats.fast_requeue)); | 76 | (long)atomic_long_read(&perf_stats.fast_requeue)); |
77 | seq_printf(m, "Number of outbound target full condition\t: %li\n", | ||
78 | (long)atomic_long_read(&perf_stats.outbound_target_full)); | ||
77 | seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n", | 79 | seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n", |
78 | (long)atomic_long_read(&perf_stats.debug_tl_out_timer)); | 80 | (long)atomic_long_read(&perf_stats.debug_tl_out_timer)); |
79 | seq_printf(m, "Number of stop polling calls\t\t\t: %li\n", | 81 | seq_printf(m, "Number of stop polling calls\t\t\t: %li\n", |
diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h index d16c1c67160f..7821ac4fa517 100644 --- a/drivers/s390/cio/qdio_perf.h +++ b/drivers/s390/cio/qdio_perf.h | |||
@@ -36,6 +36,7 @@ struct qdio_perf_stats { | |||
36 | atomic_long_t inbound_handler; | 36 | atomic_long_t inbound_handler; |
37 | atomic_long_t outbound_handler; | 37 | atomic_long_t outbound_handler; |
38 | atomic_long_t fast_requeue; | 38 | atomic_long_t fast_requeue; |
39 | atomic_long_t outbound_target_full; | ||
39 | 40 | ||
40 | /* for debugging */ | 41 | /* for debugging */ |
41 | atomic_long_t debug_tl_out_timer; | 42 | atomic_long_t debug_tl_out_timer; |
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 7323799b2ce3..18d54fc21ce9 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c | |||
@@ -167,7 +167,6 @@ static void setup_queues(struct qdio_irq *irq_ptr, | |||
167 | setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i); | 167 | setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i); |
168 | 168 | ||
169 | q->is_input_q = 1; | 169 | q->is_input_q = 1; |
170 | spin_lock_init(&q->u.in.lock); | ||
171 | setup_storage_lists(q, irq_ptr, input_sbal_array, i); | 170 | setup_storage_lists(q, irq_ptr, input_sbal_array, i); |
172 | input_sbal_array += QDIO_MAX_BUFFERS_PER_Q; | 171 | input_sbal_array += QDIO_MAX_BUFFERS_PER_Q; |
173 | 172 | ||
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 47ee741d65b2..8e90e147b746 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -131,7 +131,7 @@ static inline int tiqdio_inbound_q_done(struct qdio_q *q) | |||
131 | return 1; | 131 | return 1; |
132 | 132 | ||
133 | qdio_siga_sync_q(q); | 133 | qdio_siga_sync_q(q); |
134 | get_buf_state(q, q->first_to_check, &state); | 134 | get_buf_state(q, q->first_to_check, &state, 0); |
135 | 135 | ||
136 | if (state == SLSB_P_INPUT_PRIMED) | 136 | if (state == SLSB_P_INPUT_PRIMED) |
137 | /* more work coming */ | 137 | /* more work coming */ |