diff options
author | Frank Blaschka <frank.blaschka@de.ibm.com> | 2010-09-07 17:14:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-08 17:31:02 -0400 |
commit | a1c3ed4c9ca01dded8d511a1d1daf271fbae8d89 (patch) | |
tree | 1ff0b2887e550f127fe844222b6833b831a4b036 /drivers/s390/net/qeth_l2_main.c | |
parent | 81d53749416995538f830c8e4d3fbaf1769b9375 (diff) |
qeth: NAPI support for l2 and l3 discipline
This patch adds NAPI support to the qeth layer 2 and layer 3
discipline. It is important to understand that we can not enable/disable
IRQs as usual, we have to use the corresponding new QDIO interface.
Also to not overdraw the budget we have to stop and restart buffer
processing at any point during processing a bulk of QDIO buffers.
Having the driver NAPI enabled it is possible to turn on GRO for the
layer 3 discipline.
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390/net/qeth_l2_main.c')
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 173 |
1 files changed, 108 insertions, 65 deletions
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 830d63524d61..01c3c1f77879 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c | |||
@@ -407,29 +407,25 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) | |||
407 | return rc; | 407 | return rc; |
408 | } | 408 | } |
409 | 409 | ||
410 | static void qeth_l2_process_inbound_buffer(struct qeth_card *card, | 410 | static int qeth_l2_process_inbound_buffer(struct qeth_card *card, |
411 | struct qeth_qdio_buffer *buf, int index) | 411 | int budget, int *done) |
412 | { | 412 | { |
413 | struct qdio_buffer_element *element; | 413 | int work_done = 0; |
414 | struct sk_buff *skb; | 414 | struct sk_buff *skb; |
415 | struct qeth_hdr *hdr; | 415 | struct qeth_hdr *hdr; |
416 | int offset; | ||
417 | unsigned int len; | 416 | unsigned int len; |
418 | 417 | ||
419 | /* get first element of current buffer */ | 418 | *done = 0; |
420 | element = (struct qdio_buffer_element *)&buf->buffer->element[0]; | 419 | BUG_ON(!budget); |
421 | offset = 0; | 420 | while (budget) { |
422 | if (card->options.performance_stats) | 421 | skb = qeth_core_get_next_skb(card, |
423 | card->perf_stats.bufs_rec++; | 422 | card->qdio.in_q->bufs[card->rx.b_index].buffer, |
424 | while ((skb = qeth_core_get_next_skb(card, buf->buffer, &element, | 423 | &card->rx.b_element, &card->rx.e_offset, &hdr); |
425 | &offset, &hdr))) { | 424 | if (!skb) { |
426 | skb->dev = card->dev; | 425 | *done = 1; |
427 | /* is device UP ? */ | 426 | break; |
428 | if (!(card->dev->flags & IFF_UP)) { | ||
429 | dev_kfree_skb_any(skb); | ||
430 | continue; | ||
431 | } | 427 | } |
432 | 428 | skb->dev = card->dev; | |
433 | switch (hdr->hdr.l2.id) { | 429 | switch (hdr->hdr.l2.id) { |
434 | case QETH_HEADER_TYPE_LAYER2: | 430 | case QETH_HEADER_TYPE_LAYER2: |
435 | skb->pkt_type = PACKET_HOST; | 431 | skb->pkt_type = PACKET_HOST; |
@@ -441,7 +437,7 @@ static void qeth_l2_process_inbound_buffer(struct qeth_card *card, | |||
441 | if (skb->protocol == htons(ETH_P_802_2)) | 437 | if (skb->protocol == htons(ETH_P_802_2)) |
442 | *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; | 438 | *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; |
443 | len = skb->len; | 439 | len = skb->len; |
444 | netif_rx(skb); | 440 | netif_receive_skb(skb); |
445 | break; | 441 | break; |
446 | case QETH_HEADER_TYPE_OSN: | 442 | case QETH_HEADER_TYPE_OSN: |
447 | if (card->info.type == QETH_CARD_TYPE_OSN) { | 443 | if (card->info.type == QETH_CARD_TYPE_OSN) { |
@@ -459,9 +455,87 @@ static void qeth_l2_process_inbound_buffer(struct qeth_card *card, | |||
459 | QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN); | 455 | QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN); |
460 | continue; | 456 | continue; |
461 | } | 457 | } |
458 | work_done++; | ||
459 | budget--; | ||
462 | card->stats.rx_packets++; | 460 | card->stats.rx_packets++; |
463 | card->stats.rx_bytes += len; | 461 | card->stats.rx_bytes += len; |
464 | } | 462 | } |
463 | return work_done; | ||
464 | } | ||
465 | |||
466 | static int qeth_l2_poll(struct napi_struct *napi, int budget) | ||
467 | { | ||
468 | struct qeth_card *card = container_of(napi, struct qeth_card, napi); | ||
469 | int work_done = 0; | ||
470 | struct qeth_qdio_buffer *buffer; | ||
471 | int done; | ||
472 | int new_budget = budget; | ||
473 | |||
474 | if (card->options.performance_stats) { | ||
475 | card->perf_stats.inbound_cnt++; | ||
476 | card->perf_stats.inbound_start_time = qeth_get_micros(); | ||
477 | } | ||
478 | |||
479 | while (1) { | ||
480 | if (!card->rx.b_count) { | ||
481 | card->rx.qdio_err = 0; | ||
482 | card->rx.b_count = qdio_get_next_buffers( | ||
483 | card->data.ccwdev, 0, &card->rx.b_index, | ||
484 | &card->rx.qdio_err); | ||
485 | if (card->rx.b_count <= 0) { | ||
486 | card->rx.b_count = 0; | ||
487 | break; | ||
488 | } | ||
489 | card->rx.b_element = | ||
490 | &card->qdio.in_q->bufs[card->rx.b_index] | ||
491 | .buffer->element[0]; | ||
492 | card->rx.e_offset = 0; | ||
493 | } | ||
494 | |||
495 | while (card->rx.b_count) { | ||
496 | buffer = &card->qdio.in_q->bufs[card->rx.b_index]; | ||
497 | if (!(card->rx.qdio_err && | ||
498 | qeth_check_qdio_errors(card, buffer->buffer, | ||
499 | card->rx.qdio_err, "qinerr"))) | ||
500 | work_done += qeth_l2_process_inbound_buffer( | ||
501 | card, new_budget, &done); | ||
502 | else | ||
503 | done = 1; | ||
504 | |||
505 | if (done) { | ||
506 | if (card->options.performance_stats) | ||
507 | card->perf_stats.bufs_rec++; | ||
508 | qeth_put_buffer_pool_entry(card, | ||
509 | buffer->pool_entry); | ||
510 | qeth_queue_input_buffer(card, card->rx.b_index); | ||
511 | card->rx.b_count--; | ||
512 | if (card->rx.b_count) { | ||
513 | card->rx.b_index = | ||
514 | (card->rx.b_index + 1) % | ||
515 | QDIO_MAX_BUFFERS_PER_Q; | ||
516 | card->rx.b_element = | ||
517 | &card->qdio.in_q | ||
518 | ->bufs[card->rx.b_index] | ||
519 | .buffer->element[0]; | ||
520 | card->rx.e_offset = 0; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if (work_done >= budget) | ||
525 | goto out; | ||
526 | else | ||
527 | new_budget = budget - work_done; | ||
528 | } | ||
529 | } | ||
530 | |||
531 | napi_complete(napi); | ||
532 | if (qdio_start_irq(card->data.ccwdev, 0)) | ||
533 | napi_schedule(&card->napi); | ||
534 | out: | ||
535 | if (card->options.performance_stats) | ||
536 | card->perf_stats.inbound_time += qeth_get_micros() - | ||
537 | card->perf_stats.inbound_start_time; | ||
538 | return work_done; | ||
465 | } | 539 | } |
466 | 540 | ||
467 | static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, | 541 | static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, |
@@ -755,49 +829,10 @@ tx_drop: | |||
755 | return NETDEV_TX_OK; | 829 | return NETDEV_TX_OK; |
756 | } | 830 | } |
757 | 831 | ||
758 | static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev, | ||
759 | unsigned int qdio_err, unsigned int queue, | ||
760 | int first_element, int count, unsigned long card_ptr) | ||
761 | { | ||
762 | struct net_device *net_dev; | ||
763 | struct qeth_card *card; | ||
764 | struct qeth_qdio_buffer *buffer; | ||
765 | int index; | ||
766 | int i; | ||
767 | |||
768 | card = (struct qeth_card *) card_ptr; | ||
769 | net_dev = card->dev; | ||
770 | if (card->options.performance_stats) { | ||
771 | card->perf_stats.inbound_cnt++; | ||
772 | card->perf_stats.inbound_start_time = qeth_get_micros(); | ||
773 | } | ||
774 | if (qdio_err & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) { | ||
775 | QETH_CARD_TEXT(card, 1, "qdinchk"); | ||
776 | QETH_CARD_TEXT_(card, 1, "%04X%04X", first_element, | ||
777 | count); | ||
778 | QETH_CARD_TEXT_(card, 1, "%04X", queue); | ||
779 | qeth_schedule_recovery(card); | ||
780 | return; | ||
781 | } | ||
782 | for (i = first_element; i < (first_element + count); ++i) { | ||
783 | index = i % QDIO_MAX_BUFFERS_PER_Q; | ||
784 | buffer = &card->qdio.in_q->bufs[index]; | ||
785 | if (!(qdio_err && | ||
786 | qeth_check_qdio_errors(card, buffer->buffer, qdio_err, | ||
787 | "qinerr"))) | ||
788 | qeth_l2_process_inbound_buffer(card, buffer, index); | ||
789 | /* clear buffer and give back to hardware */ | ||
790 | qeth_put_buffer_pool_entry(card, buffer->pool_entry); | ||
791 | qeth_queue_input_buffer(card, index); | ||
792 | } | ||
793 | if (card->options.performance_stats) | ||
794 | card->perf_stats.inbound_time += qeth_get_micros() - | ||
795 | card->perf_stats.inbound_start_time; | ||
796 | } | ||
797 | |||
798 | static int qeth_l2_open(struct net_device *dev) | 832 | static int qeth_l2_open(struct net_device *dev) |
799 | { | 833 | { |
800 | struct qeth_card *card = dev->ml_priv; | 834 | struct qeth_card *card = dev->ml_priv; |
835 | int rc = 0; | ||
801 | 836 | ||
802 | QETH_CARD_TEXT(card, 4, "qethopen"); | 837 | QETH_CARD_TEXT(card, 4, "qethopen"); |
803 | if (card->state != CARD_STATE_SOFTSETUP) | 838 | if (card->state != CARD_STATE_SOFTSETUP) |
@@ -814,18 +849,24 @@ static int qeth_l2_open(struct net_device *dev) | |||
814 | 849 | ||
815 | if (!card->lan_online && netif_carrier_ok(dev)) | 850 | if (!card->lan_online && netif_carrier_ok(dev)) |
816 | netif_carrier_off(dev); | 851 | netif_carrier_off(dev); |
817 | return 0; | 852 | if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) { |
853 | napi_enable(&card->napi); | ||
854 | napi_schedule(&card->napi); | ||
855 | } else | ||
856 | rc = -EIO; | ||
857 | return rc; | ||
818 | } | 858 | } |
819 | 859 | ||
820 | |||
821 | static int qeth_l2_stop(struct net_device *dev) | 860 | static int qeth_l2_stop(struct net_device *dev) |
822 | { | 861 | { |
823 | struct qeth_card *card = dev->ml_priv; | 862 | struct qeth_card *card = dev->ml_priv; |
824 | 863 | ||
825 | QETH_CARD_TEXT(card, 4, "qethstop"); | 864 | QETH_CARD_TEXT(card, 4, "qethstop"); |
826 | netif_tx_disable(dev); | 865 | netif_tx_disable(dev); |
827 | if (card->state == CARD_STATE_UP) | 866 | if (card->state == CARD_STATE_UP) { |
828 | card->state = CARD_STATE_SOFTSETUP; | 867 | card->state = CARD_STATE_SOFTSETUP; |
868 | napi_disable(&card->napi); | ||
869 | } | ||
829 | return 0; | 870 | return 0; |
830 | } | 871 | } |
831 | 872 | ||
@@ -836,8 +877,9 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) | |||
836 | INIT_LIST_HEAD(&card->vid_list); | 877 | INIT_LIST_HEAD(&card->vid_list); |
837 | INIT_LIST_HEAD(&card->mc_list); | 878 | INIT_LIST_HEAD(&card->mc_list); |
838 | card->options.layer2 = 1; | 879 | card->options.layer2 = 1; |
880 | card->discipline.start_poll = qeth_qdio_start_poll; | ||
839 | card->discipline.input_handler = (qdio_handler_t *) | 881 | card->discipline.input_handler = (qdio_handler_t *) |
840 | qeth_l2_qdio_input_handler; | 882 | qeth_qdio_input_handler; |
841 | card->discipline.output_handler = (qdio_handler_t *) | 883 | card->discipline.output_handler = (qdio_handler_t *) |
842 | qeth_qdio_output_handler; | 884 | qeth_qdio_output_handler; |
843 | card->discipline.recover = qeth_l2_recover; | 885 | card->discipline.recover = qeth_l2_recover; |
@@ -923,6 +965,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) | |||
923 | card->info.broadcast_capable = 1; | 965 | card->info.broadcast_capable = 1; |
924 | qeth_l2_request_initial_mac(card); | 966 | qeth_l2_request_initial_mac(card); |
925 | SET_NETDEV_DEV(card->dev, &card->gdev->dev); | 967 | SET_NETDEV_DEV(card->dev, &card->gdev->dev); |
968 | netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT); | ||
926 | return register_netdev(card->dev); | 969 | return register_netdev(card->dev); |
927 | } | 970 | } |
928 | 971 | ||
@@ -955,6 +998,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) | |||
955 | qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); | 998 | qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); |
956 | 999 | ||
957 | card->state = CARD_STATE_HARDSETUP; | 1000 | card->state = CARD_STATE_HARDSETUP; |
1001 | memset(&card->rx, 0, sizeof(struct qeth_rx)); | ||
958 | qeth_print_status_message(card); | 1002 | qeth_print_status_message(card); |
959 | 1003 | ||
960 | /* softsetup */ | 1004 | /* softsetup */ |
@@ -1086,9 +1130,6 @@ static int qeth_l2_recover(void *ptr) | |||
1086 | card->use_hard_stop = 1; | 1130 | card->use_hard_stop = 1; |
1087 | __qeth_l2_set_offline(card->gdev, 1); | 1131 | __qeth_l2_set_offline(card->gdev, 1); |
1088 | rc = __qeth_l2_set_online(card->gdev, 1); | 1132 | rc = __qeth_l2_set_online(card->gdev, 1); |
1089 | /* don't run another scheduled recovery */ | ||
1090 | qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); | ||
1091 | qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD); | ||
1092 | if (!rc) | 1133 | if (!rc) |
1093 | dev_info(&card->gdev->dev, | 1134 | dev_info(&card->gdev->dev, |
1094 | "Device successfully recovered!\n"); | 1135 | "Device successfully recovered!\n"); |
@@ -1099,6 +1140,8 @@ static int qeth_l2_recover(void *ptr) | |||
1099 | dev_warn(&card->gdev->dev, "The qeth device driver " | 1140 | dev_warn(&card->gdev->dev, "The qeth device driver " |
1100 | "failed to recover an error on the device\n"); | 1141 | "failed to recover an error on the device\n"); |
1101 | } | 1142 | } |
1143 | qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); | ||
1144 | qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD); | ||
1102 | return 0; | 1145 | return 0; |
1103 | } | 1146 | } |
1104 | 1147 | ||