diff options
| -rw-r--r-- | drivers/s390/cio/qdio.h | 8 | ||||
| -rw-r--r-- | drivers/s390/cio/qdio_main.c | 150 | ||||
| -rw-r--r-- | drivers/s390/cio/qdio_setup.c | 1 |
3 files changed, 56 insertions, 103 deletions
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index c60f2566d28c..42f2b09631b6 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h | |||
| @@ -14,7 +14,6 @@ | |||
| 14 | #include "chsc.h" | 14 | #include "chsc.h" |
| 15 | 15 | ||
| 16 | #define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */ | 16 | #define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */ |
| 17 | #define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */ | ||
| 18 | #define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */ | 17 | #define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */ |
| 19 | 18 | ||
| 20 | /* | 19 | /* |
| @@ -195,12 +194,6 @@ struct qdio_input_q { | |||
| 195 | }; | 194 | }; |
| 196 | 195 | ||
| 197 | struct qdio_output_q { | 196 | struct qdio_output_q { |
| 198 | /* failed siga-w attempts*/ | ||
| 199 | atomic_t busy_siga_counter; | ||
| 200 | |||
| 201 | /* start time of busy condition */ | ||
| 202 | u64 timestamp; | ||
| 203 | |||
| 204 | /* PCIs are enabled for the queue */ | 197 | /* PCIs are enabled for the queue */ |
| 205 | int pci_out_enabled; | 198 | int pci_out_enabled; |
| 206 | 199 | ||
| @@ -251,6 +244,7 @@ struct qdio_q { | |||
| 251 | 244 | ||
| 252 | struct qdio_irq *irq_ptr; | 245 | struct qdio_irq *irq_ptr; |
| 253 | struct tasklet_struct tasklet; | 246 | struct tasklet_struct tasklet; |
| 247 | spinlock_t lock; | ||
| 254 | 248 | ||
| 255 | /* error condition during a data transfer */ | 249 | /* error condition during a data transfer */ |
| 256 | unsigned int qdio_error; | 250 | unsigned int qdio_error; |
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 0b4c09cf6a47..744f928a59ea 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
| @@ -74,7 +74,7 @@ static inline int do_siga_input(struct subchannel_id schid, unsigned int mask) | |||
| 74 | * Note: For IQDC unicast queues only the highest priority queue is processed. | 74 | * Note: For IQDC unicast queues only the highest priority queue is processed. |
| 75 | */ | 75 | */ |
| 76 | static inline int do_siga_output(unsigned long schid, unsigned long mask, | 76 | static inline int do_siga_output(unsigned long schid, unsigned long mask, |
| 77 | u32 *bb, unsigned int fc) | 77 | unsigned int *bb, unsigned int fc) |
| 78 | { | 78 | { |
| 79 | register unsigned long __fc asm("0") = fc; | 79 | register unsigned long __fc asm("0") = fc; |
| 80 | register unsigned long __schid asm("1") = schid; | 80 | register unsigned long __schid asm("1") = schid; |
| @@ -284,8 +284,7 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output, | |||
| 284 | if (!need_siga_sync(q)) | 284 | if (!need_siga_sync(q)) |
| 285 | return 0; | 285 | return 0; |
| 286 | 286 | ||
| 287 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:"); | 287 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); |
| 288 | DBF_DEV_HEX(DBF_INFO, q->irq_ptr, q, sizeof(void *)); | ||
| 289 | qdio_perf_stat_inc(&perf_stats.siga_sync); | 288 | qdio_perf_stat_inc(&perf_stats.siga_sync); |
| 290 | 289 | ||
| 291 | cc = do_siga_sync(q->irq_ptr->schid, output, input); | 290 | cc = do_siga_sync(q->irq_ptr->schid, output, input); |
| @@ -312,46 +311,37 @@ static inline int qdio_siga_sync_all(struct qdio_q *q) | |||
| 312 | return qdio_siga_sync(q, ~0U, ~0U); | 311 | return qdio_siga_sync(q, ~0U, ~0U); |
| 313 | } | 312 | } |
| 314 | 313 | ||
| 315 | static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit) | 314 | static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) |
| 316 | { | 315 | { |
| 317 | unsigned int fc = 0; | ||
| 318 | unsigned long schid; | 316 | unsigned long schid; |
| 317 | unsigned int fc = 0; | ||
| 318 | u64 start_time = 0; | ||
| 319 | int cc; | ||
| 319 | 320 | ||
| 320 | if (q->u.out.use_enh_siga) { | 321 | if (q->u.out.use_enh_siga) |
| 321 | fc = 3; | 322 | fc = 3; |
| 322 | } | 323 | |
| 323 | if (!is_qebsm(q)) | 324 | if (is_qebsm(q)) { |
| 324 | schid = *((u32 *)&q->irq_ptr->schid); | ||
| 325 | else { | ||
| 326 | schid = q->irq_ptr->sch_token; | 325 | schid = q->irq_ptr->sch_token; |
| 327 | fc |= 0x80; | 326 | fc |= 0x80; |
| 328 | } | 327 | } |
| 329 | return do_siga_output(schid, q->mask, busy_bit, fc); | 328 | else |
| 330 | } | 329 | schid = *((u32 *)&q->irq_ptr->schid); |
| 331 | |||
| 332 | static int qdio_siga_output(struct qdio_q *q) | ||
| 333 | { | ||
| 334 | int cc; | ||
| 335 | u32 busy_bit; | ||
| 336 | u64 start_time = 0; | ||
| 337 | 330 | ||
| 338 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); | ||
| 339 | qdio_perf_stat_inc(&perf_stats.siga_out); | ||
| 340 | again: | 331 | again: |
| 341 | cc = qdio_do_siga_output(q, &busy_bit); | 332 | cc = do_siga_output(schid, q->mask, busy_bit, fc); |
| 342 | if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) { | 333 | |
| 343 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w bb:%2d", q->nr); | 334 | /* hipersocket busy condition */ |
| 335 | if (*busy_bit) { | ||
| 336 | WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); | ||
| 344 | 337 | ||
| 345 | if (!start_time) | 338 | if (!start_time) { |
| 346 | start_time = get_usecs(); | 339 | start_time = get_usecs(); |
| 347 | else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE) | 340 | goto again; |
| 341 | } | ||
| 342 | if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE) | ||
| 348 | goto again; | 343 | goto again; |
| 349 | } | 344 | } |
| 350 | |||
| 351 | if (cc == 2 && busy_bit) | ||
| 352 | cc |= QDIO_ERROR_SIGA_BUSY; | ||
| 353 | if (cc) | ||
| 354 | DBF_ERROR("%4x SIGA-W:%2d", SCH_NO(q), cc); | ||
| 355 | return cc; | 345 | return cc; |
| 356 | } | 346 | } |
| 357 | 347 | ||
| @@ -399,7 +389,7 @@ inline void qdio_stop_polling(struct qdio_q *q) | |||
| 399 | 389 | ||
| 400 | static void announce_buffer_error(struct qdio_q *q, int count) | 390 | static void announce_buffer_error(struct qdio_q *q, int count) |
| 401 | { | 391 | { |
| 402 | q->qdio_error = QDIO_ERROR_SLSB_STATE; | 392 | q->qdio_error |= QDIO_ERROR_SLSB_STATE; |
| 403 | 393 | ||
| 404 | /* special handling for no target buffer empty */ | 394 | /* special handling for no target buffer empty */ |
| 405 | if ((!q->is_input_q && | 395 | if ((!q->is_input_q && |
| @@ -716,68 +706,36 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) | |||
| 716 | return 0; | 706 | return 0; |
| 717 | } | 707 | } |
| 718 | 708 | ||
| 719 | /* | ||
| 720 | * VM could present us cc=2 and busy bit set on SIGA-write | ||
| 721 | * during reconfiguration of their Guest LAN (only in iqdio mode, | ||
| 722 | * otherwise qdio is asynchronous and cc=2 and busy bit there will take | ||
| 723 | * the queues down immediately). | ||
| 724 | * | ||
| 725 | * Therefore qdio_siga_output will try for a short time constantly, | ||
| 726 | * if such a condition occurs. If it doesn't change, it will | ||
| 727 | * increase the busy_siga_counter and save the timestamp, and | ||
| 728 | * schedule the queue for later processing. qdio_outbound_processing | ||
| 729 | * will check out the counter. If non-zero, it will call qdio_kick_outbound_q | ||
| 730 | * as often as the value of the counter. This will attempt further SIGA | ||
| 731 | * instructions. For each successful SIGA, the counter is | ||
| 732 | * decreased, for failing SIGAs the counter remains the same, after | ||
| 733 | * all. After some time of no movement, qdio_kick_outbound_q will | ||
| 734 | * finally fail and reflect corresponding error codes to call | ||
| 735 | * the upper layer module and have it take the queues down. | ||
| 736 | * | ||
| 737 | * Note that this is a change from the original HiperSockets design | ||
| 738 | * (saying cc=2 and busy bit means take the queues down), but in | ||
| 739 | * these days Guest LAN didn't exist... excessive cc=2 with busy bit | ||
| 740 | * conditions will still take the queues down, but the threshold is | ||
| 741 | * higher due to the Guest LAN environment. | ||
| 742 | * | ||
| 743 | * Called from outbound tasklet and do_QDIO handler. | ||
| 744 | */ | ||
| 745 | static void qdio_kick_outbound_q(struct qdio_q *q) | 709 | static void qdio_kick_outbound_q(struct qdio_q *q) |
| 746 | { | 710 | { |
| 747 | int rc; | 711 | unsigned int busy_bit; |
| 748 | 712 | int cc; | |
| 749 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickoutq:%1d", q->nr); | ||
| 750 | 713 | ||
| 751 | if (!need_siga_out(q)) | 714 | if (!need_siga_out(q)) |
| 752 | return; | 715 | return; |
| 753 | 716 | ||
| 754 | rc = qdio_siga_output(q); | 717 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); |
| 755 | switch (rc) { | 718 | qdio_perf_stat_inc(&perf_stats.siga_out); |
| 719 | |||
| 720 | cc = qdio_siga_output(q, &busy_bit); | ||
| 721 | switch (cc) { | ||
| 756 | case 0: | 722 | case 0: |
| 757 | /* TODO: improve error handling for CC=0 case */ | ||
| 758 | if (q->u.out.timestamp) | ||
| 759 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "cc2 rslv:%4x", | ||
| 760 | atomic_read(&q->u.out.busy_siga_counter)); | ||
| 761 | /* went smooth this time, reset timestamp */ | ||
| 762 | q->u.out.timestamp = 0; | ||
| 763 | break; | 723 | break; |
| 764 | /* cc=2 and busy bit */ | 724 | case 2: |
| 765 | case (2 | QDIO_ERROR_SIGA_BUSY): | 725 | if (busy_bit) { |
| 766 | atomic_inc(&q->u.out.busy_siga_counter); | 726 | DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); |
| 767 | 727 | q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY; | |
| 768 | /* if the last siga was successful, save timestamp here */ | 728 | } else { |
| 769 | if (!q->u.out.timestamp) | 729 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", |
| 770 | q->u.out.timestamp = get_usecs(); | 730 | q->nr); |
| 771 | 731 | q->qdio_error = cc; | |
| 772 | /* if we're in time, don't touch qdio_error */ | ||
| 773 | if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) { | ||
| 774 | tasklet_schedule(&q->tasklet); | ||
| 775 | break; | ||
| 776 | } | 732 | } |
| 777 | DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); | 733 | break; |
| 778 | default: | 734 | case 1: |
| 779 | /* for plain cc=1, 2 or 3 */ | 735 | case 3: |
| 780 | q->qdio_error = rc; | 736 | DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc); |
| 737 | q->qdio_error = cc; | ||
| 738 | break; | ||
| 781 | } | 739 | } |
| 782 | } | 740 | } |
| 783 | 741 | ||
| @@ -808,22 +766,18 @@ static void qdio_kick_outbound_handler(struct qdio_q *q) | |||
| 808 | 766 | ||
| 809 | static void __qdio_outbound_processing(struct qdio_q *q) | 767 | static void __qdio_outbound_processing(struct qdio_q *q) |
| 810 | { | 768 | { |
| 811 | int siga_attempts; | 769 | unsigned long flags; |
| 812 | 770 | ||
| 813 | qdio_perf_stat_inc(&perf_stats.tasklet_outbound); | 771 | qdio_perf_stat_inc(&perf_stats.tasklet_outbound); |
| 814 | 772 | spin_lock_irqsave(&q->lock, flags); | |
| 815 | /* see comment in qdio_kick_outbound_q */ | ||
| 816 | siga_attempts = atomic_read(&q->u.out.busy_siga_counter); | ||
| 817 | while (siga_attempts--) { | ||
| 818 | atomic_dec(&q->u.out.busy_siga_counter); | ||
| 819 | qdio_kick_outbound_q(q); | ||
| 820 | } | ||
| 821 | 773 | ||
| 822 | BUG_ON(atomic_read(&q->nr_buf_used) < 0); | 774 | BUG_ON(atomic_read(&q->nr_buf_used) < 0); |
| 823 | 775 | ||
| 824 | if (qdio_outbound_q_moved(q)) | 776 | if (qdio_outbound_q_moved(q)) |
| 825 | qdio_kick_outbound_handler(q); | 777 | qdio_kick_outbound_handler(q); |
| 826 | 778 | ||
| 779 | spin_unlock_irqrestore(&q->lock, flags); | ||
| 780 | |||
| 827 | if (queue_type(q) == QDIO_ZFCP_QFMT) { | 781 | if (queue_type(q) == QDIO_ZFCP_QFMT) { |
| 828 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) | 782 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) |
| 829 | tasklet_schedule(&q->tasklet); | 783 | tasklet_schedule(&q->tasklet); |
| @@ -1491,7 +1445,7 @@ static inline int buf_in_between(int bufnr, int start, int count) | |||
| 1491 | static void handle_inbound(struct qdio_q *q, unsigned int callflags, | 1445 | static void handle_inbound(struct qdio_q *q, unsigned int callflags, |
| 1492 | int bufnr, int count) | 1446 | int bufnr, int count) |
| 1493 | { | 1447 | { |
| 1494 | int used, rc, diff; | 1448 | int used, cc, diff; |
| 1495 | 1449 | ||
| 1496 | if (!q->u.in.polling) | 1450 | if (!q->u.in.polling) |
| 1497 | goto set; | 1451 | goto set; |
| @@ -1532,9 +1486,9 @@ set: | |||
| 1532 | return; | 1486 | return; |
| 1533 | 1487 | ||
| 1534 | if (need_siga_in(q)) { | 1488 | if (need_siga_in(q)) { |
| 1535 | rc = qdio_siga_input(q); | 1489 | cc = qdio_siga_input(q); |
| 1536 | if (rc) | 1490 | if (cc) |
| 1537 | q->qdio_error = rc; | 1491 | q->qdio_error = cc; |
| 1538 | } | 1492 | } |
| 1539 | } | 1493 | } |
| 1540 | 1494 | ||
| @@ -1581,6 +1535,10 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, | |||
| 1581 | while (count--) | 1535 | while (count--) |
| 1582 | qdio_kick_outbound_q(q); | 1536 | qdio_kick_outbound_q(q); |
| 1583 | } | 1537 | } |
| 1538 | |||
| 1539 | /* report CC=2 conditions synchronously */ | ||
| 1540 | if (q->qdio_error) | ||
| 1541 | __qdio_outbound_processing(q); | ||
| 1584 | goto out; | 1542 | goto out; |
| 1585 | } | 1543 | } |
| 1586 | 1544 | ||
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 18d54fc21ce9..c08356b95bf5 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c | |||
| @@ -117,6 +117,7 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr, | |||
| 117 | q->mask = 1 << (31 - i); | 117 | q->mask = 1 << (31 - i); |
| 118 | q->nr = i; | 118 | q->nr = i; |
| 119 | q->handler = handler; | 119 | q->handler = handler; |
| 120 | spin_lock_init(&q->lock); | ||
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, | 123 | static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, |
