diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2009-03-26 10:24:31 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-03-26 10:24:22 -0400 |
commit | d303b6fd858370c22d5c70c313669e3521a5f758 (patch) | |
tree | 3c8dd573bc3ea48af8f12c41e5f00358c09a579a /drivers/s390 | |
parent | 9e890ad880be1dd98483313b2ec0e23fbd4e3792 (diff) |
[S390] qdio: report SIGA errors directly
Errors from SIGA instructions are stored in the per queue qdio_error
and reported back when the queue handler is called. That opens a race
when multiple error conditions occur simultanously.
Report SIGA errors immediately in the return value of do_QDIO so the
upper layer can react and SIGA errors no longer interfere with other
errors.
Move the SIGA error handling in qeth from the outbound handler to
qeth_flush_buffers.
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/qdio.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 73 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_setup.c | 1 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 55 |
4 files changed, 47 insertions, 83 deletions
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 57807f5ffe84..41171d741f38 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h | |||
@@ -247,7 +247,6 @@ struct qdio_q { | |||
247 | 247 | ||
248 | struct qdio_irq *irq_ptr; | 248 | struct qdio_irq *irq_ptr; |
249 | struct tasklet_struct tasklet; | 249 | struct tasklet_struct tasklet; |
250 | spinlock_t lock; | ||
251 | 250 | ||
252 | /* error condition during a data transfer */ | 251 | /* error condition during a data transfer */ |
253 | unsigned int qdio_error; | 252 | unsigned int qdio_error; |
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 31b9318149ba..e53ac67e1e48 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
@@ -706,13 +706,13 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) | |||
706 | return 0; | 706 | return 0; |
707 | } | 707 | } |
708 | 708 | ||
709 | static void qdio_kick_outbound_q(struct qdio_q *q) | 709 | static int qdio_kick_outbound_q(struct qdio_q *q) |
710 | { | 710 | { |
711 | unsigned int busy_bit; | 711 | unsigned int busy_bit; |
712 | int cc; | 712 | int cc; |
713 | 713 | ||
714 | if (!need_siga_out(q)) | 714 | if (!need_siga_out(q)) |
715 | return; | 715 | return 0; |
716 | 716 | ||
717 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); | 717 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); |
718 | qdio_perf_stat_inc(&perf_stats.siga_out); | 718 | qdio_perf_stat_inc(&perf_stats.siga_out); |
@@ -724,19 +724,16 @@ static void qdio_kick_outbound_q(struct qdio_q *q) | |||
724 | case 2: | 724 | case 2: |
725 | if (busy_bit) { | 725 | if (busy_bit) { |
726 | DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); | 726 | DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); |
727 | q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY; | 727 | cc |= QDIO_ERROR_SIGA_BUSY; |
728 | } else { | 728 | } else |
729 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", | 729 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr); |
730 | q->nr); | ||
731 | q->qdio_error = cc; | ||
732 | } | ||
733 | break; | 730 | break; |
734 | case 1: | 731 | case 1: |
735 | case 3: | 732 | case 3: |
736 | DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc); | 733 | DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc); |
737 | q->qdio_error = cc; | ||
738 | break; | 734 | break; |
739 | } | 735 | } |
736 | return cc; | ||
740 | } | 737 | } |
741 | 738 | ||
742 | static void qdio_kick_outbound_handler(struct qdio_q *q) | 739 | static void qdio_kick_outbound_handler(struct qdio_q *q) |
@@ -766,18 +763,12 @@ static void qdio_kick_outbound_handler(struct qdio_q *q) | |||
766 | 763 | ||
767 | static void __qdio_outbound_processing(struct qdio_q *q) | 764 | static void __qdio_outbound_processing(struct qdio_q *q) |
768 | { | 765 | { |
769 | unsigned long flags; | ||
770 | |||
771 | qdio_perf_stat_inc(&perf_stats.tasklet_outbound); | 766 | qdio_perf_stat_inc(&perf_stats.tasklet_outbound); |
772 | spin_lock_irqsave(&q->lock, flags); | ||
773 | |||
774 | BUG_ON(atomic_read(&q->nr_buf_used) < 0); | 767 | BUG_ON(atomic_read(&q->nr_buf_used) < 0); |
775 | 768 | ||
776 | if (qdio_outbound_q_moved(q)) | 769 | if (qdio_outbound_q_moved(q)) |
777 | qdio_kick_outbound_handler(q); | 770 | qdio_kick_outbound_handler(q); |
778 | 771 | ||
779 | spin_unlock_irqrestore(&q->lock, flags); | ||
780 | |||
781 | if (queue_type(q) == QDIO_ZFCP_QFMT) | 772 | if (queue_type(q) == QDIO_ZFCP_QFMT) |
782 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) | 773 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) |
783 | goto sched; | 774 | goto sched; |
@@ -1457,10 +1448,10 @@ static inline int buf_in_between(int bufnr, int start, int count) | |||
1457 | * @bufnr: first buffer to process | 1448 | * @bufnr: first buffer to process |
1458 | * @count: how many buffers are emptied | 1449 | * @count: how many buffers are emptied |
1459 | */ | 1450 | */ |
1460 | static void handle_inbound(struct qdio_q *q, unsigned int callflags, | 1451 | static int handle_inbound(struct qdio_q *q, unsigned int callflags, |
1461 | int bufnr, int count) | 1452 | int bufnr, int count) |
1462 | { | 1453 | { |
1463 | int used, cc, diff; | 1454 | int used, diff; |
1464 | 1455 | ||
1465 | if (!q->u.in.polling) | 1456 | if (!q->u.in.polling) |
1466 | goto set; | 1457 | goto set; |
@@ -1497,13 +1488,11 @@ set: | |||
1497 | 1488 | ||
1498 | /* no need to signal as long as the adapter had free buffers */ | 1489 | /* no need to signal as long as the adapter had free buffers */ |
1499 | if (used) | 1490 | if (used) |
1500 | return; | 1491 | return 0; |
1501 | 1492 | ||
1502 | if (need_siga_in(q)) { | 1493 | if (need_siga_in(q)) |
1503 | cc = qdio_siga_input(q); | 1494 | return qdio_siga_input(q); |
1504 | if (cc) | 1495 | return 0; |
1505 | q->qdio_error = cc; | ||
1506 | } | ||
1507 | } | 1496 | } |
1508 | 1497 | ||
1509 | /** | 1498 | /** |
@@ -1513,11 +1502,11 @@ set: | |||
1513 | * @bufnr: first buffer to process | 1502 | * @bufnr: first buffer to process |
1514 | * @count: how many buffers are filled | 1503 | * @count: how many buffers are filled |
1515 | */ | 1504 | */ |
1516 | static void handle_outbound(struct qdio_q *q, unsigned int callflags, | 1505 | static int handle_outbound(struct qdio_q *q, unsigned int callflags, |
1517 | int bufnr, int count) | 1506 | int bufnr, int count) |
1518 | { | 1507 | { |
1519 | unsigned char state; | 1508 | unsigned char state; |
1520 | int used; | 1509 | int used, rc = 0; |
1521 | 1510 | ||
1522 | qdio_perf_stat_inc(&perf_stats.outbound_handler); | 1511 | qdio_perf_stat_inc(&perf_stats.outbound_handler); |
1523 | 1512 | ||
@@ -1532,27 +1521,26 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, | |||
1532 | 1521 | ||
1533 | if (queue_type(q) == QDIO_IQDIO_QFMT) { | 1522 | if (queue_type(q) == QDIO_IQDIO_QFMT) { |
1534 | if (multicast_outbound(q)) | 1523 | if (multicast_outbound(q)) |
1535 | qdio_kick_outbound_q(q); | 1524 | rc = qdio_kick_outbound_q(q); |
1536 | else | 1525 | else |
1537 | if ((q->irq_ptr->ssqd_desc.mmwc > 1) && | 1526 | if ((q->irq_ptr->ssqd_desc.mmwc > 1) && |
1538 | (count > 1) && | 1527 | (count > 1) && |
1539 | (count <= q->irq_ptr->ssqd_desc.mmwc)) { | 1528 | (count <= q->irq_ptr->ssqd_desc.mmwc)) { |
1540 | /* exploit enhanced SIGA */ | 1529 | /* exploit enhanced SIGA */ |
1541 | q->u.out.use_enh_siga = 1; | 1530 | q->u.out.use_enh_siga = 1; |
1542 | qdio_kick_outbound_q(q); | 1531 | rc = qdio_kick_outbound_q(q); |
1543 | } else { | 1532 | } else { |
1544 | /* | 1533 | /* |
1545 | * One siga-w per buffer required for unicast | 1534 | * One siga-w per buffer required for unicast |
1546 | * HiperSockets. | 1535 | * HiperSockets. |
1547 | */ | 1536 | */ |
1548 | q->u.out.use_enh_siga = 0; | 1537 | q->u.out.use_enh_siga = 0; |
1549 | while (count--) | 1538 | while (count--) { |
1550 | qdio_kick_outbound_q(q); | 1539 | rc = qdio_kick_outbound_q(q); |
1540 | if (rc) | ||
1541 | goto out; | ||
1542 | } | ||
1551 | } | 1543 | } |
1552 | |||
1553 | /* report CC=2 conditions synchronously */ | ||
1554 | if (q->qdio_error) | ||
1555 | __qdio_outbound_processing(q); | ||
1556 | goto out; | 1544 | goto out; |
1557 | } | 1545 | } |
1558 | 1546 | ||
@@ -1564,13 +1552,14 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, | |||
1564 | /* try to fast requeue buffers */ | 1552 | /* try to fast requeue buffers */ |
1565 | get_buf_state(q, prev_buf(bufnr), &state, 0); | 1553 | get_buf_state(q, prev_buf(bufnr), &state, 0); |
1566 | if (state != SLSB_CU_OUTPUT_PRIMED) | 1554 | if (state != SLSB_CU_OUTPUT_PRIMED) |
1567 | qdio_kick_outbound_q(q); | 1555 | rc = qdio_kick_outbound_q(q); |
1568 | else { | 1556 | else { |
1569 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); | 1557 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); |
1570 | qdio_perf_stat_inc(&perf_stats.fast_requeue); | 1558 | qdio_perf_stat_inc(&perf_stats.fast_requeue); |
1571 | } | 1559 | } |
1572 | out: | 1560 | out: |
1573 | tasklet_schedule(&q->tasklet); | 1561 | tasklet_schedule(&q->tasklet); |
1562 | return rc; | ||
1574 | } | 1563 | } |
1575 | 1564 | ||
1576 | /** | 1565 | /** |
@@ -1609,14 +1598,12 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, | |||
1609 | return -EBUSY; | 1598 | return -EBUSY; |
1610 | 1599 | ||
1611 | if (callflags & QDIO_FLAG_SYNC_INPUT) | 1600 | if (callflags & QDIO_FLAG_SYNC_INPUT) |
1612 | handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr, | 1601 | return handle_inbound(irq_ptr->input_qs[q_nr], |
1613 | count); | 1602 | callflags, bufnr, count); |
1614 | else if (callflags & QDIO_FLAG_SYNC_OUTPUT) | 1603 | else if (callflags & QDIO_FLAG_SYNC_OUTPUT) |
1615 | handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr, | 1604 | return handle_outbound(irq_ptr->output_qs[q_nr], |
1616 | count); | 1605 | callflags, bufnr, count); |
1617 | else | 1606 | return -EINVAL; |
1618 | return -EINVAL; | ||
1619 | return 0; | ||
1620 | } | 1607 | } |
1621 | EXPORT_SYMBOL_GPL(do_QDIO); | 1608 | EXPORT_SYMBOL_GPL(do_QDIO); |
1622 | 1609 | ||
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index c08356b95bf5..18d54fc21ce9 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c | |||
@@ -117,7 +117,6 @@ 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); | ||
121 | } | 120 | } |
122 | 121 | ||
123 | static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, | 122 | static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, |
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d1b5bebea7fb..2489bcebf5ec 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c | |||
@@ -2693,40 +2693,21 @@ static int qeth_handle_send_error(struct qeth_card *card, | |||
2693 | struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) | 2693 | struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) |
2694 | { | 2694 | { |
2695 | int sbalf15 = buffer->buffer->element[15].flags & 0xff; | 2695 | int sbalf15 = buffer->buffer->element[15].flags & 0xff; |
2696 | int cc = qdio_err & 3; | ||
2697 | 2696 | ||
2698 | QETH_DBF_TEXT(TRACE, 6, "hdsnderr"); | 2697 | QETH_DBF_TEXT(TRACE, 6, "hdsnderr"); |
2699 | qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr"); | 2698 | qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr"); |
2700 | switch (cc) { | 2699 | |
2701 | case 0: | 2700 | if (!qdio_err) |
2702 | if (qdio_err) { | ||
2703 | QETH_DBF_TEXT(TRACE, 1, "lnkfail"); | ||
2704 | QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card)); | ||
2705 | QETH_DBF_TEXT_(TRACE, 1, "%04x %02x", | ||
2706 | (u16)qdio_err, (u8)sbalf15); | ||
2707 | return QETH_SEND_ERROR_LINK_FAILURE; | ||
2708 | } | ||
2709 | return QETH_SEND_ERROR_NONE; | 2701 | return QETH_SEND_ERROR_NONE; |
2710 | case 2: | 2702 | |
2711 | if (qdio_err & QDIO_ERROR_SIGA_BUSY) { | 2703 | if ((sbalf15 >= 15) && (sbalf15 <= 31)) |
2712 | QETH_DBF_TEXT(TRACE, 1, "SIGAcc2B"); | 2704 | return QETH_SEND_ERROR_RETRY; |
2713 | QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card)); | 2705 | |
2714 | return QETH_SEND_ERROR_KICK_IT; | 2706 | QETH_DBF_TEXT(TRACE, 1, "lnkfail"); |
2715 | } | 2707 | QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card)); |
2716 | if ((sbalf15 >= 15) && (sbalf15 <= 31)) | 2708 | QETH_DBF_TEXT_(TRACE, 1, "%04x %02x", |
2717 | return QETH_SEND_ERROR_RETRY; | 2709 | (u16)qdio_err, (u8)sbalf15); |
2718 | return QETH_SEND_ERROR_LINK_FAILURE; | 2710 | return QETH_SEND_ERROR_LINK_FAILURE; |
2719 | /* look at qdio_error and sbalf 15 */ | ||
2720 | case 1: | ||
2721 | QETH_DBF_TEXT(TRACE, 1, "SIGAcc1"); | ||
2722 | QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card)); | ||
2723 | return QETH_SEND_ERROR_LINK_FAILURE; | ||
2724 | case 3: | ||
2725 | default: | ||
2726 | QETH_DBF_TEXT(TRACE, 1, "SIGAcc3"); | ||
2727 | QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card)); | ||
2728 | return QETH_SEND_ERROR_KICK_IT; | ||
2729 | } | ||
2730 | } | 2711 | } |
2731 | 2712 | ||
2732 | /* | 2713 | /* |
@@ -2862,10 +2843,14 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, | |||
2862 | qeth_get_micros() - | 2843 | qeth_get_micros() - |
2863 | queue->card->perf_stats.outbound_do_qdio_start_time; | 2844 | queue->card->perf_stats.outbound_do_qdio_start_time; |
2864 | if (rc) { | 2845 | if (rc) { |
2846 | queue->card->stats.tx_errors += count; | ||
2847 | /* ignore temporary SIGA errors without busy condition */ | ||
2848 | if (rc == QDIO_ERROR_SIGA_TARGET) | ||
2849 | return; | ||
2865 | QETH_DBF_TEXT(TRACE, 2, "flushbuf"); | 2850 | QETH_DBF_TEXT(TRACE, 2, "flushbuf"); |
2866 | QETH_DBF_TEXT_(TRACE, 2, " err%d", rc); | 2851 | QETH_DBF_TEXT_(TRACE, 2, " err%d", rc); |
2867 | QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_DDEV_ID(queue->card)); | 2852 | QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_DDEV_ID(queue->card)); |
2868 | queue->card->stats.tx_errors += count; | 2853 | |
2869 | /* this must not happen under normal circumstances. if it | 2854 | /* this must not happen under normal circumstances. if it |
2870 | * happens something is really wrong -> recover */ | 2855 | * happens something is really wrong -> recover */ |
2871 | qeth_schedule_recovery(queue->card); | 2856 | qeth_schedule_recovery(queue->card); |
@@ -2940,13 +2925,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, | |||
2940 | } | 2925 | } |
2941 | for (i = first_element; i < (first_element + count); ++i) { | 2926 | for (i = first_element; i < (first_element + count); ++i) { |
2942 | buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; | 2927 | buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; |
2943 | /*we only handle the KICK_IT error by doing a recovery */ | 2928 | qeth_handle_send_error(card, buffer, qdio_error); |
2944 | if (qeth_handle_send_error(card, buffer, qdio_error) | ||
2945 | == QETH_SEND_ERROR_KICK_IT){ | ||
2946 | netif_stop_queue(card->dev); | ||
2947 | qeth_schedule_recovery(card); | ||
2948 | return; | ||
2949 | } | ||
2950 | qeth_clear_output_buffer(queue, buffer); | 2929 | qeth_clear_output_buffer(queue, buffer); |
2951 | } | 2930 | } |
2952 | atomic_sub(count, &queue->used_buffers); | 2931 | atomic_sub(count, &queue->used_buffers); |