diff options
author | Klaus-Dieter Wacker <kdwacker@de.ibm.com> | 2009-08-25 22:01:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-08-26 20:34:13 -0400 |
commit | 58490f18071de525f42b6ed264a87116ec49ef0f (patch) | |
tree | 81ca009a975e34f405ced00e35cf8537d31d96ac /drivers/s390/net | |
parent | 31ffe249e5426d2648d68568fa00a7b66666a5db (diff) |
qeth: HiperSockets SIGA retry support on CC=2.
Qeth HiperSockets support now retries sending of packets when the
IBM System z signals a temporary resource shortage (e.g. target
buffer full). The packet is enqueued into the device queue.
After 3 times of unsuccessful send the packet is dropped.
Signed-off-by: Klaus-Dieter Wacker <kdwacker@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390/net')
-rw-r--r-- | drivers/s390/net/qeth_core.h | 9 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 60 |
2 files changed, 61 insertions, 8 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 447e1d19581a..03161e650b24 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h | |||
@@ -435,6 +435,7 @@ struct qeth_qdio_out_q { | |||
435 | * index of buffer to be filled by driver; state EMPTY or PACKING | 435 | * index of buffer to be filled by driver; state EMPTY or PACKING |
436 | */ | 436 | */ |
437 | int next_buf_to_fill; | 437 | int next_buf_to_fill; |
438 | int sync_iqdio_error; | ||
438 | /* | 439 | /* |
439 | * number of buffers that are currently filled (PRIMED) | 440 | * number of buffers that are currently filled (PRIMED) |
440 | * -> these buffers are hardware-owned | 441 | * -> these buffers are hardware-owned |
@@ -685,6 +686,14 @@ struct qeth_mc_mac { | |||
685 | int is_vmac; | 686 | int is_vmac; |
686 | }; | 687 | }; |
687 | 688 | ||
689 | struct qeth_skb_data { | ||
690 | __u32 magic; | ||
691 | int count; | ||
692 | }; | ||
693 | |||
694 | #define QETH_SKB_MAGIC 0x71657468 | ||
695 | #define QETH_SIGA_CC2_RETRIES 3 | ||
696 | |||
688 | struct qeth_card { | 697 | struct qeth_card { |
689 | struct list_head list; | 698 | struct list_head list; |
690 | enum qeth_card_states state; | 699 | enum qeth_card_states state; |
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d53621c4acbb..ac7270d21708 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c | |||
@@ -927,8 +927,8 @@ out: | |||
927 | return; | 927 | return; |
928 | } | 928 | } |
929 | 929 | ||
930 | static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, | 930 | static void __qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, |
931 | struct qeth_qdio_out_buffer *buf) | 931 | struct qeth_qdio_out_buffer *buf, unsigned int qeth_skip_skb) |
932 | { | 932 | { |
933 | int i; | 933 | int i; |
934 | struct sk_buff *skb; | 934 | struct sk_buff *skb; |
@@ -937,11 +937,13 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, | |||
937 | if (buf->buffer->element[0].flags & 0x40) | 937 | if (buf->buffer->element[0].flags & 0x40) |
938 | atomic_dec(&queue->set_pci_flags_count); | 938 | atomic_dec(&queue->set_pci_flags_count); |
939 | 939 | ||
940 | skb = skb_dequeue(&buf->skb_list); | 940 | if (!qeth_skip_skb) { |
941 | while (skb) { | ||
942 | atomic_dec(&skb->users); | ||
943 | dev_kfree_skb_any(skb); | ||
944 | skb = skb_dequeue(&buf->skb_list); | 941 | skb = skb_dequeue(&buf->skb_list); |
942 | while (skb) { | ||
943 | atomic_dec(&skb->users); | ||
944 | dev_kfree_skb_any(skb); | ||
945 | skb = skb_dequeue(&buf->skb_list); | ||
946 | } | ||
945 | } | 947 | } |
946 | for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { | 948 | for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { |
947 | if (buf->buffer->element[i].addr && buf->is_header[i]) | 949 | if (buf->buffer->element[i].addr && buf->is_header[i]) |
@@ -957,6 +959,12 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, | |||
957 | atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY); | 959 | atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY); |
958 | } | 960 | } |
959 | 961 | ||
962 | static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, | ||
963 | struct qeth_qdio_out_buffer *buf) | ||
964 | { | ||
965 | __qeth_clear_output_buffer(queue, buf, 0); | ||
966 | } | ||
967 | |||
960 | void qeth_clear_qdio_buffers(struct qeth_card *card) | 968 | void qeth_clear_qdio_buffers(struct qeth_card *card) |
961 | { | 969 | { |
962 | int i, j; | 970 | int i, j; |
@@ -2685,6 +2693,13 @@ static int qeth_handle_send_error(struct qeth_card *card, | |||
2685 | int sbalf15 = buffer->buffer->element[15].flags & 0xff; | 2693 | int sbalf15 = buffer->buffer->element[15].flags & 0xff; |
2686 | 2694 | ||
2687 | QETH_DBF_TEXT(TRACE, 6, "hdsnderr"); | 2695 | QETH_DBF_TEXT(TRACE, 6, "hdsnderr"); |
2696 | if (card->info.type == QETH_CARD_TYPE_IQD) { | ||
2697 | if (sbalf15 == 0) { | ||
2698 | qdio_err = 0; | ||
2699 | } else { | ||
2700 | qdio_err = 1; | ||
2701 | } | ||
2702 | } | ||
2688 | qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr"); | 2703 | qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr"); |
2689 | 2704 | ||
2690 | if (!qdio_err) | 2705 | if (!qdio_err) |
@@ -2817,6 +2832,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, | |||
2817 | } | 2832 | } |
2818 | } | 2833 | } |
2819 | 2834 | ||
2835 | queue->sync_iqdio_error = 0; | ||
2820 | queue->card->dev->trans_start = jiffies; | 2836 | queue->card->dev->trans_start = jiffies; |
2821 | if (queue->card->options.performance_stats) { | 2837 | if (queue->card->options.performance_stats) { |
2822 | queue->card->perf_stats.outbound_do_qdio_cnt++; | 2838 | queue->card->perf_stats.outbound_do_qdio_cnt++; |
@@ -2832,6 +2848,10 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, | |||
2832 | queue->card->perf_stats.outbound_do_qdio_time += | 2848 | queue->card->perf_stats.outbound_do_qdio_time += |
2833 | qeth_get_micros() - | 2849 | qeth_get_micros() - |
2834 | queue->card->perf_stats.outbound_do_qdio_start_time; | 2850 | queue->card->perf_stats.outbound_do_qdio_start_time; |
2851 | if (rc > 0) { | ||
2852 | if (!(rc & QDIO_ERROR_SIGA_BUSY)) | ||
2853 | queue->sync_iqdio_error = rc & 3; | ||
2854 | } | ||
2835 | if (rc) { | 2855 | if (rc) { |
2836 | queue->card->stats.tx_errors += count; | 2856 | queue->card->stats.tx_errors += count; |
2837 | /* ignore temporary SIGA errors without busy condition */ | 2857 | /* ignore temporary SIGA errors without busy condition */ |
@@ -2899,6 +2919,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, | |||
2899 | struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; | 2919 | struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; |
2900 | struct qeth_qdio_out_buffer *buffer; | 2920 | struct qeth_qdio_out_buffer *buffer; |
2901 | int i; | 2921 | int i; |
2922 | unsigned qeth_send_err; | ||
2902 | 2923 | ||
2903 | QETH_DBF_TEXT(TRACE, 6, "qdouhdl"); | 2924 | QETH_DBF_TEXT(TRACE, 6, "qdouhdl"); |
2904 | if (qdio_error & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) { | 2925 | if (qdio_error & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) { |
@@ -2915,8 +2936,9 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, | |||
2915 | } | 2936 | } |
2916 | for (i = first_element; i < (first_element + count); ++i) { | 2937 | for (i = first_element; i < (first_element + count); ++i) { |
2917 | buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; | 2938 | buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; |
2918 | qeth_handle_send_error(card, buffer, qdio_error); | 2939 | qeth_send_err = qeth_handle_send_error(card, buffer, qdio_error); |
2919 | qeth_clear_output_buffer(queue, buffer); | 2940 | __qeth_clear_output_buffer(queue, buffer, |
2941 | (qeth_send_err == QETH_SEND_ERROR_RETRY) ? 1 : 0); | ||
2920 | } | 2942 | } |
2921 | atomic_sub(count, &queue->used_buffers); | 2943 | atomic_sub(count, &queue->used_buffers); |
2922 | /* check if we need to do something on this outbound queue */ | 2944 | /* check if we need to do something on this outbound queue */ |
@@ -3159,7 +3181,10 @@ int qeth_do_send_packet_fast(struct qeth_card *card, | |||
3159 | int offset, int hd_len) | 3181 | int offset, int hd_len) |
3160 | { | 3182 | { |
3161 | struct qeth_qdio_out_buffer *buffer; | 3183 | struct qeth_qdio_out_buffer *buffer; |
3184 | struct sk_buff *skb1; | ||
3185 | struct qeth_skb_data *retry_ctrl; | ||
3162 | int index; | 3186 | int index; |
3187 | int rc; | ||
3163 | 3188 | ||
3164 | /* spin until we get the queue ... */ | 3189 | /* spin until we get the queue ... */ |
3165 | while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, | 3190 | while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED, |
@@ -3178,6 +3203,25 @@ int qeth_do_send_packet_fast(struct qeth_card *card, | |||
3178 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | 3203 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); |
3179 | qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); | 3204 | qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); |
3180 | qeth_flush_buffers(queue, index, 1); | 3205 | qeth_flush_buffers(queue, index, 1); |
3206 | if (queue->sync_iqdio_error == 2) { | ||
3207 | skb1 = skb_dequeue(&buffer->skb_list); | ||
3208 | while (skb1) { | ||
3209 | atomic_dec(&skb1->users); | ||
3210 | skb1 = skb_dequeue(&buffer->skb_list); | ||
3211 | } | ||
3212 | retry_ctrl = (struct qeth_skb_data *) &skb->cb[16]; | ||
3213 | if (retry_ctrl->magic != QETH_SKB_MAGIC) { | ||
3214 | retry_ctrl->magic = QETH_SKB_MAGIC; | ||
3215 | retry_ctrl->count = 0; | ||
3216 | } | ||
3217 | if (retry_ctrl->count < QETH_SIGA_CC2_RETRIES) { | ||
3218 | retry_ctrl->count++; | ||
3219 | rc = dev_queue_xmit(skb); | ||
3220 | } else { | ||
3221 | dev_kfree_skb_any(skb); | ||
3222 | QETH_DBF_TEXT(QERR, 2, "qrdrop"); | ||
3223 | } | ||
3224 | } | ||
3181 | return 0; | 3225 | return 0; |
3182 | out: | 3226 | out: |
3183 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | 3227 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); |