aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2009-11-29 10:16:05 -0500
committerDavid S. Miller <davem@davemloft.net>2009-11-29 20:23:57 -0500
commit738a8f4b0c0e6ce7260e1514d41c764f334982e4 (patch)
treeef24785fe6f168be25a97e00f7619eb7ec9e0d89 /drivers/net/sfc
parent8880f4ec21e668dcab3c6d387524a887e5bcbf73 (diff)
sfc: Implement TSO for TCP/IPv6
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r--drivers/net/sfc/efx.c2
-rw-r--r--drivers/net/sfc/ethtool.c20
-rw-r--r--drivers/net/sfc/tx.c52
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
474static 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
474static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) 491static 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 */
575static void efx_tso_check_safe(struct sk_buff *skb) 580static __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