diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2009-11-29 10:16:05 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-29 20:23:57 -0500 |
commit | 738a8f4b0c0e6ce7260e1514d41c764f334982e4 (patch) | |
tree | ef24785fe6f168be25a97e00f7619eb7ec9e0d89 | |
parent | 8880f4ec21e668dcab3c6d387524a887e5bcbf73 (diff) |
sfc: Implement TSO for TCP/IPv6
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/sfc/efx.c | 2 | ||||
-rw-r--r-- | drivers/net/sfc/ethtool.c | 20 | ||||
-rw-r--r-- | drivers/net/sfc/tx.c | 52 |
3 files changed, 60 insertions, 14 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 4b5c786f0e2c..3c0d6bea126e 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c | |||
@@ -2208,6 +2208,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, | |||
2208 | net_dev->features |= (type->offload_features | NETIF_F_SG | | 2208 | net_dev->features |= (type->offload_features | NETIF_F_SG | |
2209 | NETIF_F_HIGHDMA | NETIF_F_TSO | | 2209 | NETIF_F_HIGHDMA | NETIF_F_TSO | |
2210 | NETIF_F_GRO); | 2210 | NETIF_F_GRO); |
2211 | if (type->offload_features & NETIF_F_V6_CSUM) | ||
2212 | net_dev->features |= NETIF_F_TSO6; | ||
2211 | /* Mask for features that also apply to VLAN devices */ | 2213 | /* Mask for features that also apply to VLAN devices */ |
2212 | net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG | | 2214 | net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG | |
2213 | NETIF_F_HIGHDMA | NETIF_F_TSO); | 2215 | NETIF_F_HIGHDMA | NETIF_F_TSO); |
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 5492b6336602..f6981216f1fc 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c | |||
@@ -471,6 +471,23 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, | |||
471 | } | 471 | } |
472 | } | 472 | } |
473 | 473 | ||
474 | static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable) | ||
475 | { | ||
476 | struct efx_nic *efx __attribute__ ((unused)) = netdev_priv(net_dev); | ||
477 | unsigned long features; | ||
478 | |||
479 | features = NETIF_F_TSO; | ||
480 | if (efx->type->offload_features & NETIF_F_V6_CSUM) | ||
481 | features |= NETIF_F_TSO6; | ||
482 | |||
483 | if (enable) | ||
484 | net_dev->features |= features; | ||
485 | else | ||
486 | net_dev->features &= ~features; | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
474 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) | 491 | static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) |
475 | { | 492 | { |
476 | struct efx_nic *efx = netdev_priv(net_dev); | 493 | struct efx_nic *efx = netdev_priv(net_dev); |
@@ -834,7 +851,8 @@ const struct ethtool_ops efx_ethtool_ops = { | |||
834 | .get_sg = ethtool_op_get_sg, | 851 | .get_sg = ethtool_op_get_sg, |
835 | .set_sg = ethtool_op_set_sg, | 852 | .set_sg = ethtool_op_set_sg, |
836 | .get_tso = ethtool_op_get_tso, | 853 | .get_tso = ethtool_op_get_tso, |
837 | .set_tso = ethtool_op_set_tso, | 854 | /* Need to enable/disable TSO-IPv6 too */ |
855 | .set_tso = efx_ethtool_set_tso, | ||
838 | .get_flags = ethtool_op_get_flags, | 856 | .get_flags = ethtool_op_get_flags, |
839 | .set_flags = ethtool_op_set_flags, | 857 | .set_flags = ethtool_op_set_flags, |
840 | .get_sset_count = efx_ethtool_get_sset_count, | 858 | .get_sset_count = efx_ethtool_get_sset_count, |
diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index 389ede43e34a..582fc752da90 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c | |||
@@ -12,6 +12,8 @@ | |||
12 | #include <linux/tcp.h> | 12 | #include <linux/tcp.h> |
13 | #include <linux/ip.h> | 13 | #include <linux/ip.h> |
14 | #include <linux/in.h> | 14 | #include <linux/in.h> |
15 | #include <linux/ipv6.h> | ||
16 | #include <net/ipv6.h> | ||
15 | #include <linux/if_ether.h> | 17 | #include <linux/if_ether.h> |
16 | #include <linux/highmem.h> | 18 | #include <linux/highmem.h> |
17 | #include "net_driver.h" | 19 | #include "net_driver.h" |
@@ -531,6 +533,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) | |||
531 | #define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data) | 533 | #define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data) |
532 | #define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data) | 534 | #define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data) |
533 | #define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data) | 535 | #define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data) |
536 | #define SKB_IPV6_OFF(skb) PTR_DIFF(ipv6_hdr(skb), (skb)->data) | ||
534 | 537 | ||
535 | /** | 538 | /** |
536 | * struct tso_state - TSO state for an SKB | 539 | * struct tso_state - TSO state for an SKB |
@@ -543,6 +546,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) | |||
543 | * @unmap_len: Length of SKB fragment | 546 | * @unmap_len: Length of SKB fragment |
544 | * @unmap_addr: DMA address of SKB fragment | 547 | * @unmap_addr: DMA address of SKB fragment |
545 | * @unmap_single: DMA single vs page mapping flag | 548 | * @unmap_single: DMA single vs page mapping flag |
549 | * @protocol: Network protocol (after any VLAN header) | ||
546 | * @header_len: Number of bytes of header | 550 | * @header_len: Number of bytes of header |
547 | * @full_packet_size: Number of bytes to put in each outgoing segment | 551 | * @full_packet_size: Number of bytes to put in each outgoing segment |
548 | * | 552 | * |
@@ -563,6 +567,7 @@ struct tso_state { | |||
563 | dma_addr_t unmap_addr; | 567 | dma_addr_t unmap_addr; |
564 | bool unmap_single; | 568 | bool unmap_single; |
565 | 569 | ||
570 | __be16 protocol; | ||
566 | unsigned header_len; | 571 | unsigned header_len; |
567 | int full_packet_size; | 572 | int full_packet_size; |
568 | }; | 573 | }; |
@@ -570,9 +575,9 @@ struct tso_state { | |||
570 | 575 | ||
571 | /* | 576 | /* |
572 | * Verify that our various assumptions about sk_buffs and the conditions | 577 | * Verify that our various assumptions about sk_buffs and the conditions |
573 | * under which TSO will be attempted hold true. | 578 | * under which TSO will be attempted hold true. Return the protocol number. |
574 | */ | 579 | */ |
575 | static void efx_tso_check_safe(struct sk_buff *skb) | 580 | static __be16 efx_tso_check_protocol(struct sk_buff *skb) |
576 | { | 581 | { |
577 | __be16 protocol = skb->protocol; | 582 | __be16 protocol = skb->protocol; |
578 | 583 | ||
@@ -587,13 +592,22 @@ static void efx_tso_check_safe(struct sk_buff *skb) | |||
587 | if (protocol == htons(ETH_P_IP)) | 592 | if (protocol == htons(ETH_P_IP)) |
588 | skb_set_transport_header(skb, sizeof(*veh) + | 593 | skb_set_transport_header(skb, sizeof(*veh) + |
589 | 4 * ip_hdr(skb)->ihl); | 594 | 4 * ip_hdr(skb)->ihl); |
595 | else if (protocol == htons(ETH_P_IPV6)) | ||
596 | skb_set_transport_header(skb, sizeof(*veh) + | ||
597 | sizeof(struct ipv6hdr)); | ||
590 | } | 598 | } |
591 | 599 | ||
592 | EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IP)); | 600 | if (protocol == htons(ETH_P_IP)) { |
593 | EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); | 601 | EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); |
602 | } else { | ||
603 | EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); | ||
604 | EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); | ||
605 | } | ||
594 | EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) | 606 | EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) |
595 | + (tcp_hdr(skb)->doff << 2u)) > | 607 | + (tcp_hdr(skb)->doff << 2u)) > |
596 | skb_headlen(skb)); | 608 | skb_headlen(skb)); |
609 | |||
610 | return protocol; | ||
597 | } | 611 | } |
598 | 612 | ||
599 | 613 | ||
@@ -836,7 +850,10 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb) | |||
836 | + PTR_DIFF(tcp_hdr(skb), skb->data)); | 850 | + PTR_DIFF(tcp_hdr(skb), skb->data)); |
837 | st->full_packet_size = st->header_len + skb_shinfo(skb)->gso_size; | 851 | st->full_packet_size = st->header_len + skb_shinfo(skb)->gso_size; |
838 | 852 | ||
839 | st->ipv4_id = ntohs(ip_hdr(skb)->id); | 853 | if (st->protocol == htons(ETH_P_IP)) |
854 | st->ipv4_id = ntohs(ip_hdr(skb)->id); | ||
855 | else | ||
856 | st->ipv4_id = 0; | ||
840 | st->seqnum = ntohl(tcp_hdr(skb)->seq); | 857 | st->seqnum = ntohl(tcp_hdr(skb)->seq); |
841 | 858 | ||
842 | EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); | 859 | EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); |
@@ -951,7 +968,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, | |||
951 | struct tso_state *st) | 968 | struct tso_state *st) |
952 | { | 969 | { |
953 | struct efx_tso_header *tsoh; | 970 | struct efx_tso_header *tsoh; |
954 | struct iphdr *tsoh_iph; | ||
955 | struct tcphdr *tsoh_th; | 971 | struct tcphdr *tsoh_th; |
956 | unsigned ip_length; | 972 | unsigned ip_length; |
957 | u8 *header; | 973 | u8 *header; |
@@ -975,7 +991,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, | |||
975 | 991 | ||
976 | header = TSOH_BUFFER(tsoh); | 992 | header = TSOH_BUFFER(tsoh); |
977 | tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb)); | 993 | tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb)); |
978 | tsoh_iph = (struct iphdr *)(header + SKB_IPV4_OFF(skb)); | ||
979 | 994 | ||
980 | /* Copy and update the headers. */ | 995 | /* Copy and update the headers. */ |
981 | memcpy(header, skb->data, st->header_len); | 996 | memcpy(header, skb->data, st->header_len); |
@@ -993,11 +1008,22 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, | |||
993 | tsoh_th->fin = tcp_hdr(skb)->fin; | 1008 | tsoh_th->fin = tcp_hdr(skb)->fin; |
994 | tsoh_th->psh = tcp_hdr(skb)->psh; | 1009 | tsoh_th->psh = tcp_hdr(skb)->psh; |
995 | } | 1010 | } |
996 | tsoh_iph->tot_len = htons(ip_length); | ||
997 | 1011 | ||
998 | /* Linux leaves suitable gaps in the IP ID space for us to fill. */ | 1012 | if (st->protocol == htons(ETH_P_IP)) { |
999 | tsoh_iph->id = htons(st->ipv4_id); | 1013 | struct iphdr *tsoh_iph = |
1000 | st->ipv4_id++; | 1014 | (struct iphdr *)(header + SKB_IPV4_OFF(skb)); |
1015 | |||
1016 | tsoh_iph->tot_len = htons(ip_length); | ||
1017 | |||
1018 | /* Linux leaves suitable gaps in the IP ID space for us to fill. */ | ||
1019 | tsoh_iph->id = htons(st->ipv4_id); | ||
1020 | st->ipv4_id++; | ||
1021 | } else { | ||
1022 | struct ipv6hdr *tsoh_iph = | ||
1023 | (struct ipv6hdr *)(header + SKB_IPV6_OFF(skb)); | ||
1024 | |||
1025 | tsoh_iph->payload_len = htons(ip_length - sizeof(*tsoh_iph)); | ||
1026 | } | ||
1001 | 1027 | ||
1002 | st->packet_space = skb_shinfo(skb)->gso_size; | 1028 | st->packet_space = skb_shinfo(skb)->gso_size; |
1003 | ++tx_queue->tso_packets; | 1029 | ++tx_queue->tso_packets; |
@@ -1027,8 +1053,8 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, | |||
1027 | int frag_i, rc, rc2 = NETDEV_TX_OK; | 1053 | int frag_i, rc, rc2 = NETDEV_TX_OK; |
1028 | struct tso_state state; | 1054 | struct tso_state state; |
1029 | 1055 | ||
1030 | /* Verify TSO is safe - these checks should never fail. */ | 1056 | /* Find the packet protocol and sanity-check it */ |
1031 | efx_tso_check_safe(skb); | 1057 | state.protocol = efx_tso_check_protocol(skb); |
1032 | 1058 | ||
1033 | EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); | 1059 | EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); |
1034 | 1060 | ||