diff options
author | Frank Blaschka <frank.blaschka@de.ibm.com> | 2010-05-11 15:34:45 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-05-16 03:50:10 -0400 |
commit | f6b85b6c42ccc20316a25f6ccbe7a984c5a1304d (patch) | |
tree | 6016fd9f624871b427819173e59f49f311ed7649 /drivers/s390 | |
parent | 2d6c9ffcca7808f42ba6b953da0ba60e19a9cbbd (diff) |
qeth: exploit HW TX checksumming
OSA supports HW TX checksumming in layer 3 mode. Enable this
feature and remove software fallback used for TSO. Cleanup
checksum bits to indicate OSA can do checksumming only for
IPv4 TCP and UDP.
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/qeth_core.h | 3 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 92 |
2 files changed, 69 insertions, 26 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index fcd005aad989..bab0febf0725 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h | |||
@@ -351,7 +351,7 @@ enum qeth_header_ids { | |||
351 | #define QETH_HDR_EXT_SRC_MAC_ADDR 0x08 | 351 | #define QETH_HDR_EXT_SRC_MAC_ADDR 0x08 |
352 | #define QETH_HDR_EXT_CSUM_HDR_REQ 0x10 | 352 | #define QETH_HDR_EXT_CSUM_HDR_REQ 0x10 |
353 | #define QETH_HDR_EXT_CSUM_TRANSP_REQ 0x20 | 353 | #define QETH_HDR_EXT_CSUM_TRANSP_REQ 0x20 |
354 | #define QETH_HDR_EXT_UDP_TSO 0x40 /*bit off for TCP*/ | 354 | #define QETH_HDR_EXT_UDP 0x40 /*bit off for TCP*/ |
355 | 355 | ||
356 | static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) | 356 | static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) |
357 | { | 357 | { |
@@ -630,6 +630,7 @@ struct qeth_card_info { | |||
630 | int unique_id; | 630 | int unique_id; |
631 | struct qeth_card_blkt blkt; | 631 | struct qeth_card_blkt blkt; |
632 | __u32 csum_mask; | 632 | __u32 csum_mask; |
633 | __u32 tx_csum_mask; | ||
633 | enum qeth_ipa_promisc_modes promisc_mode; | 634 | enum qeth_ipa_promisc_modes promisc_mode; |
634 | }; | 635 | }; |
635 | 636 | ||
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 35b6d3d2bd73..8bcad24ccf37 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c | |||
@@ -54,16 +54,16 @@ int qeth_l3_set_large_send(struct qeth_card *card, | |||
54 | if (card->options.large_send == QETH_LARGE_SEND_TSO) { | 54 | if (card->options.large_send == QETH_LARGE_SEND_TSO) { |
55 | if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { | 55 | if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { |
56 | card->dev->features |= NETIF_F_TSO | NETIF_F_SG | | 56 | card->dev->features |= NETIF_F_TSO | NETIF_F_SG | |
57 | NETIF_F_HW_CSUM; | 57 | NETIF_F_IP_CSUM; |
58 | } else { | 58 | } else { |
59 | card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | | 59 | card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | |
60 | NETIF_F_HW_CSUM); | 60 | NETIF_F_IP_CSUM); |
61 | card->options.large_send = QETH_LARGE_SEND_NO; | 61 | card->options.large_send = QETH_LARGE_SEND_NO; |
62 | rc = -EOPNOTSUPP; | 62 | rc = -EOPNOTSUPP; |
63 | } | 63 | } |
64 | } else { | 64 | } else { |
65 | card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | | 65 | card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | |
66 | NETIF_F_HW_CSUM); | 66 | NETIF_F_IP_CSUM); |
67 | card->options.large_send = QETH_LARGE_SEND_NO; | 67 | card->options.large_send = QETH_LARGE_SEND_NO; |
68 | } | 68 | } |
69 | return rc; | 69 | return rc; |
@@ -1108,6 +1108,13 @@ static int qeth_l3_default_setassparms_cb(struct qeth_card *card, | |||
1108 | card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; | 1108 | card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; |
1109 | QETH_DBF_TEXT_(TRACE, 3, "csum:%d", card->info.csum_mask); | 1109 | QETH_DBF_TEXT_(TRACE, 3, "csum:%d", card->info.csum_mask); |
1110 | } | 1110 | } |
1111 | if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM && | ||
1112 | cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { | ||
1113 | card->info.tx_csum_mask = | ||
1114 | cmd->data.setassparms.data.flags_32bit; | ||
1115 | QETH_DBF_TEXT_(TRACE, 3, "tcsu:%d", card->info.tx_csum_mask); | ||
1116 | } | ||
1117 | |||
1111 | return 0; | 1118 | return 0; |
1112 | } | 1119 | } |
1113 | 1120 | ||
@@ -1536,6 +1543,28 @@ static int qeth_l3_start_ipa_checksum(struct qeth_card *card) | |||
1536 | return rc; | 1543 | return rc; |
1537 | } | 1544 | } |
1538 | 1545 | ||
1546 | static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) | ||
1547 | { | ||
1548 | int rc = 0; | ||
1549 | |||
1550 | if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) | ||
1551 | return rc; | ||
1552 | rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, | ||
1553 | IPA_CMD_ASS_START, 0); | ||
1554 | if (rc) | ||
1555 | goto err_out; | ||
1556 | rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, | ||
1557 | IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask); | ||
1558 | if (rc) | ||
1559 | goto err_out; | ||
1560 | dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n"); | ||
1561 | return rc; | ||
1562 | err_out: | ||
1563 | dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s " | ||
1564 | "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card)); | ||
1565 | return rc; | ||
1566 | } | ||
1567 | |||
1539 | static int qeth_l3_start_ipa_tso(struct qeth_card *card) | 1568 | static int qeth_l3_start_ipa_tso(struct qeth_card *card) |
1540 | { | 1569 | { |
1541 | int rc; | 1570 | int rc; |
@@ -1578,6 +1607,7 @@ static int qeth_l3_start_ipassists(struct qeth_card *card) | |||
1578 | qeth_l3_start_ipa_ipv6(card); /* go on*/ | 1607 | qeth_l3_start_ipa_ipv6(card); /* go on*/ |
1579 | qeth_l3_start_ipa_broadcast(card); /* go on*/ | 1608 | qeth_l3_start_ipa_broadcast(card); /* go on*/ |
1580 | qeth_l3_start_ipa_checksum(card); /* go on*/ | 1609 | qeth_l3_start_ipa_checksum(card); /* go on*/ |
1610 | qeth_l3_start_ipa_tx_checksum(card); | ||
1581 | qeth_l3_start_ipa_tso(card); /* go on*/ | 1611 | qeth_l3_start_ipa_tso(card); /* go on*/ |
1582 | return 0; | 1612 | return 0; |
1583 | } | 1613 | } |
@@ -2817,6 +2847,21 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, | |||
2817 | } | 2847 | } |
2818 | } | 2848 | } |
2819 | 2849 | ||
2850 | static inline void qeth_l3_hdr_csum(struct qeth_card *card, | ||
2851 | struct qeth_hdr *hdr, struct sk_buff *skb) | ||
2852 | { | ||
2853 | struct iphdr *iph = ip_hdr(skb); | ||
2854 | |||
2855 | /* tcph->check contains already the pseudo hdr checksum | ||
2856 | * so just set the header flags | ||
2857 | */ | ||
2858 | if (iph->protocol == IPPROTO_UDP) | ||
2859 | hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP; | ||
2860 | hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; | ||
2861 | if (card->options.performance_stats) | ||
2862 | card->perf_stats.tx_csum++; | ||
2863 | } | ||
2864 | |||
2820 | static void qeth_tso_fill_header(struct qeth_card *card, | 2865 | static void qeth_tso_fill_header(struct qeth_card *card, |
2821 | struct qeth_hdr *qhdr, struct sk_buff *skb) | 2866 | struct qeth_hdr *qhdr, struct sk_buff *skb) |
2822 | { | 2867 | { |
@@ -2852,21 +2897,6 @@ static void qeth_tso_fill_header(struct qeth_card *card, | |||
2852 | } | 2897 | } |
2853 | } | 2898 | } |
2854 | 2899 | ||
2855 | static void qeth_tx_csum(struct sk_buff *skb) | ||
2856 | { | ||
2857 | __wsum csum; | ||
2858 | int offset; | ||
2859 | |||
2860 | skb_set_transport_header(skb, skb->csum_start - skb_headroom(skb)); | ||
2861 | offset = skb->csum_start - skb_headroom(skb); | ||
2862 | BUG_ON(offset >= skb_headlen(skb)); | ||
2863 | csum = skb_checksum(skb, offset, skb->len - offset, 0); | ||
2864 | |||
2865 | offset += skb->csum_offset; | ||
2866 | BUG_ON(offset + sizeof(__sum16) > skb_headlen(skb)); | ||
2867 | *(__sum16 *)(skb->data + offset) = csum_fold(csum); | ||
2868 | } | ||
2869 | |||
2870 | static inline int qeth_l3_tso_elements(struct sk_buff *skb) | 2900 | static inline int qeth_l3_tso_elements(struct sk_buff *skb) |
2871 | { | 2901 | { |
2872 | unsigned long tcpd = (unsigned long)tcp_hdr(skb) + | 2902 | unsigned long tcpd = (unsigned long)tcp_hdr(skb) + |
@@ -2923,12 +2953,6 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
2923 | 2953 | ||
2924 | if (skb_is_gso(skb)) | 2954 | if (skb_is_gso(skb)) |
2925 | large_send = card->options.large_send; | 2955 | large_send = card->options.large_send; |
2926 | else | ||
2927 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | ||
2928 | qeth_tx_csum(skb); | ||
2929 | if (card->options.performance_stats) | ||
2930 | card->perf_stats.tx_csum++; | ||
2931 | } | ||
2932 | 2956 | ||
2933 | if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && | 2957 | if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && |
2934 | (skb_shinfo(skb)->nr_frags == 0)) { | 2958 | (skb_shinfo(skb)->nr_frags == 0)) { |
@@ -3007,6 +3031,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
3007 | cast_type); | 3031 | cast_type); |
3008 | hdr->hdr.l3.length = new_skb->len - data_offset; | 3032 | hdr->hdr.l3.length = new_skb->len - data_offset; |
3009 | } | 3033 | } |
3034 | |||
3035 | if (skb->ip_summed == CHECKSUM_PARTIAL) | ||
3036 | qeth_l3_hdr_csum(card, hdr, new_skb); | ||
3010 | } | 3037 | } |
3011 | 3038 | ||
3012 | elems = qeth_get_elements_no(card, (void *)hdr, new_skb, | 3039 | elems = qeth_get_elements_no(card, (void *)hdr, new_skb, |
@@ -3132,10 +3159,25 @@ static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data) | |||
3132 | return rc; | 3159 | return rc; |
3133 | } | 3160 | } |
3134 | 3161 | ||
3162 | static int qeth_l3_ethtool_set_tx_csum(struct net_device *dev, u32 data) | ||
3163 | { | ||
3164 | struct qeth_card *card = dev->ml_priv; | ||
3165 | |||
3166 | if (data) { | ||
3167 | if (qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) | ||
3168 | dev->features |= NETIF_F_IP_CSUM; | ||
3169 | else | ||
3170 | return -EPERM; | ||
3171 | } else | ||
3172 | dev->features &= ~NETIF_F_IP_CSUM; | ||
3173 | |||
3174 | return 0; | ||
3175 | } | ||
3176 | |||
3135 | static const struct ethtool_ops qeth_l3_ethtool_ops = { | 3177 | static const struct ethtool_ops qeth_l3_ethtool_ops = { |
3136 | .get_link = ethtool_op_get_link, | 3178 | .get_link = ethtool_op_get_link, |
3137 | .get_tx_csum = ethtool_op_get_tx_csum, | 3179 | .get_tx_csum = ethtool_op_get_tx_csum, |
3138 | .set_tx_csum = ethtool_op_set_tx_hw_csum, | 3180 | .set_tx_csum = qeth_l3_ethtool_set_tx_csum, |
3139 | .get_rx_csum = qeth_l3_ethtool_get_rx_csum, | 3181 | .get_rx_csum = qeth_l3_ethtool_get_rx_csum, |
3140 | .set_rx_csum = qeth_l3_ethtool_set_rx_csum, | 3182 | .set_rx_csum = qeth_l3_ethtool_set_rx_csum, |
3141 | .get_sg = ethtool_op_get_sg, | 3183 | .get_sg = ethtool_op_get_sg, |