diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2006-06-30 16:37:03 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-30 17:12:10 -0400 |
commit | f83ef8c0b58dac17211a4c0b6df0e2b1bd6637b1 (patch) | |
tree | 61661a587df97cb2a9f73b5d0d1cf30f09644051 | |
parent | bcd76111178ebccedd46a9b3eaff65c78e5a70af (diff) |
[IPV6]: Added GSO support for TCPv6
This patch adds GSO support for IPv6 and TCPv6. This is based on a patch
by Ananda Raju <Ananda.Raju@neterion.com>. His original description is:
This patch enables TSO over IPv6. Currently Linux network stacks
restricts TSO over IPv6 by clearing of the NETIF_F_TSO bit from
"dev->features". This patch will remove this restriction.
This patch will introduce a new flag NETIF_F_TSO6 which will be used
to check whether device supports TSO over IPv6. If device support TSO
over IPv6 then we don't clear of NETIF_F_TSO and which will make the
TCP layer to create TSO packets. Any device supporting TSO over IPv6
will set NETIF_F_TSO6 flag in "dev->features" along with NETIF_F_TSO.
In case when user disables TSO using ethtool, NETIF_F_TSO will get
cleared from "dev->features". So even if we have NETIF_F_TSO6 we don't
get TSO packets created by TCP layer.
SKB_GSO_TCPV4 renamed to SKB_GSO_TCP to make it generic GSO packet.
SKB_GSO_UDPV4 renamed to SKB_GSO_UDP as UFO is not a IPv4 feature.
UFO is supported over IPv6 also
The following table shows there is significant improvement in
throughput with normal frames and CPU usage for both normal and jumbo.
--------------------------------------------------
| | 1500 | 9600 |
| ------------------|-------------------|
| | thru CPU | thru CPU |
--------------------------------------------------
| TSO OFF | 2.00 5.5% id | 5.66 20.0% id |
--------------------------------------------------
| TSO ON | 2.63 78.0 id | 5.67 39.0% id |
--------------------------------------------------
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/s2io.c | 15 | ||||
-rw-r--r-- | include/linux/netdevice.h | 5 | ||||
-rw-r--r-- | include/linux/skbuff.h | 6 | ||||
-rw-r--r-- | include/net/ip6_route.h | 2 | ||||
-rw-r--r-- | include/net/tcp_ecn.h | 4 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 4 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 2 | ||||
-rw-r--r-- | net/ipv6/inet6_connection_sock.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 4 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 6 |
10 files changed, 24 insertions, 26 deletions
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 3defe5d4f7d3..74764694c64f 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c | |||
@@ -3960,7 +3960,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) | |||
3960 | txdp->Control_2 = 0; | 3960 | txdp->Control_2 = 0; |
3961 | #ifdef NETIF_F_TSO | 3961 | #ifdef NETIF_F_TSO |
3962 | mss = skb_shinfo(skb)->gso_size; | 3962 | mss = skb_shinfo(skb)->gso_size; |
3963 | if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4) { | 3963 | if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { |
3964 | txdp->Control_1 |= TXD_TCP_LSO_EN; | 3964 | txdp->Control_1 |= TXD_TCP_LSO_EN; |
3965 | txdp->Control_1 |= TXD_TCP_LSO_MSS(mss); | 3965 | txdp->Control_1 |= TXD_TCP_LSO_MSS(mss); |
3966 | } | 3966 | } |
@@ -3980,7 +3980,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) | |||
3980 | } | 3980 | } |
3981 | 3981 | ||
3982 | frg_len = skb->len - skb->data_len; | 3982 | frg_len = skb->len - skb->data_len; |
3983 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) { | 3983 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP) { |
3984 | int ufo_size; | 3984 | int ufo_size; |
3985 | 3985 | ||
3986 | ufo_size = skb_shinfo(skb)->gso_size; | 3986 | ufo_size = skb_shinfo(skb)->gso_size; |
@@ -4009,7 +4009,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) | |||
4009 | txdp->Host_Control = (unsigned long) skb; | 4009 | txdp->Host_Control = (unsigned long) skb; |
4010 | txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len); | 4010 | txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len); |
4011 | 4011 | ||
4012 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) | 4012 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP) |
4013 | txdp->Control_1 |= TXD_UFO_EN; | 4013 | txdp->Control_1 |= TXD_UFO_EN; |
4014 | 4014 | ||
4015 | frg_cnt = skb_shinfo(skb)->nr_frags; | 4015 | frg_cnt = skb_shinfo(skb)->nr_frags; |
@@ -4024,12 +4024,12 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) | |||
4024 | (sp->pdev, frag->page, frag->page_offset, | 4024 | (sp->pdev, frag->page, frag->page_offset, |
4025 | frag->size, PCI_DMA_TODEVICE); | 4025 | frag->size, PCI_DMA_TODEVICE); |
4026 | txdp->Control_1 = TXD_BUFFER0_SIZE(frag->size); | 4026 | txdp->Control_1 = TXD_BUFFER0_SIZE(frag->size); |
4027 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) | 4027 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP) |
4028 | txdp->Control_1 |= TXD_UFO_EN; | 4028 | txdp->Control_1 |= TXD_UFO_EN; |
4029 | } | 4029 | } |
4030 | txdp->Control_1 |= TXD_GATHER_CODE_LAST; | 4030 | txdp->Control_1 |= TXD_GATHER_CODE_LAST; |
4031 | 4031 | ||
4032 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) | 4032 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP) |
4033 | frg_cnt++; /* as Txd0 was used for inband header */ | 4033 | frg_cnt++; /* as Txd0 was used for inband header */ |
4034 | 4034 | ||
4035 | tx_fifo = mac_control->tx_FIFO_start[queue]; | 4035 | tx_fifo = mac_control->tx_FIFO_start[queue]; |
@@ -4043,7 +4043,7 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) | |||
4043 | if (mss) | 4043 | if (mss) |
4044 | val64 |= TX_FIFO_SPECIAL_FUNC; | 4044 | val64 |= TX_FIFO_SPECIAL_FUNC; |
4045 | #endif | 4045 | #endif |
4046 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) | 4046 | if (skb_shinfo(skb)->gso_type == SKB_GSO_UDP) |
4047 | val64 |= TX_FIFO_SPECIAL_FUNC; | 4047 | val64 |= TX_FIFO_SPECIAL_FUNC; |
4048 | writeq(val64, &tx_fifo->List_Control); | 4048 | writeq(val64, &tx_fifo->List_Control); |
4049 | 4049 | ||
@@ -7021,6 +7021,9 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) | |||
7021 | #ifdef NETIF_F_TSO | 7021 | #ifdef NETIF_F_TSO |
7022 | dev->features |= NETIF_F_TSO; | 7022 | dev->features |= NETIF_F_TSO; |
7023 | #endif | 7023 | #endif |
7024 | #ifdef NETIF_F_TSO6 | ||
7025 | dev->features |= NETIF_F_TSO6; | ||
7026 | #endif | ||
7024 | if (sp->device_type & XFRAME_II_DEVICE) { | 7027 | if (sp->device_type & XFRAME_II_DEVICE) { |
7025 | dev->features |= NETIF_F_UFO; | 7028 | dev->features |= NETIF_F_UFO; |
7026 | dev->features |= NETIF_F_HW_CSUM; | 7029 | dev->features |= NETIF_F_HW_CSUM; |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6db03ab7cec8..85f99f60deea 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -315,9 +315,10 @@ struct net_device | |||
315 | #define NETIF_F_GSO_SHIFT 16 | 315 | #define NETIF_F_GSO_SHIFT 16 |
316 | #define NETIF_F_GSO_MASK 0xffff0000 | 316 | #define NETIF_F_GSO_MASK 0xffff0000 |
317 | #define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) | 317 | #define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) |
318 | #define NETIF_F_UFO (SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT) | 318 | #define NETIF_F_UFO (SKB_GSO_UDP << NETIF_F_GSO_SHIFT) |
319 | #define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT) | 319 | #define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT) |
320 | #define NETIF_F_TSO_ECN (SKB_GSO_TCPV4_ECN << NETIF_F_GSO_SHIFT) | 320 | #define NETIF_F_TSO_ECN (SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT) |
321 | #define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT) | ||
321 | 322 | ||
322 | #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) | 323 | #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) |
323 | #define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) | 324 | #define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) |
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 59918be91d0a..57d7d4965f9a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -171,13 +171,15 @@ enum { | |||
171 | 171 | ||
172 | enum { | 172 | enum { |
173 | SKB_GSO_TCPV4 = 1 << 0, | 173 | SKB_GSO_TCPV4 = 1 << 0, |
174 | SKB_GSO_UDPV4 = 1 << 1, | 174 | SKB_GSO_UDP = 1 << 1, |
175 | 175 | ||
176 | /* This indicates the skb is from an untrusted source. */ | 176 | /* This indicates the skb is from an untrusted source. */ |
177 | SKB_GSO_DODGY = 1 << 2, | 177 | SKB_GSO_DODGY = 1 << 2, |
178 | 178 | ||
179 | /* This indicates the tcp segment has CWR set. */ | 179 | /* This indicates the tcp segment has CWR set. */ |
180 | SKB_GSO_TCPV4_ECN = 1 << 3, | 180 | SKB_GSO_TCP_ECN = 1 << 3, |
181 | |||
182 | SKB_GSO_TCPV6 = 1 << 4, | ||
181 | }; | 183 | }; |
182 | 184 | ||
183 | /** | 185 | /** |
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index a398ae5e30f9..ab29dafb1a6a 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h | |||
@@ -146,7 +146,7 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, | |||
146 | struct rt6_info *rt = (struct rt6_info *) dst; | 146 | struct rt6_info *rt = (struct rt6_info *) dst; |
147 | 147 | ||
148 | write_lock(&sk->sk_dst_lock); | 148 | write_lock(&sk->sk_dst_lock); |
149 | __sk_dst_set(sk, dst); | 149 | sk_setup_caps(sk, dst); |
150 | np->daddr_cache = daddr; | 150 | np->daddr_cache = daddr; |
151 | np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; | 151 | np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; |
152 | write_unlock(&sk->sk_dst_lock); | 152 | write_unlock(&sk->sk_dst_lock); |
diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h index 7bb366f70934..4629d77173f2 100644 --- a/include/net/tcp_ecn.h +++ b/include/net/tcp_ecn.h | |||
@@ -55,9 +55,7 @@ static inline void TCP_ECN_send(struct sock *sk, struct tcp_sock *tp, | |||
55 | if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) { | 55 | if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) { |
56 | tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; | 56 | tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; |
57 | skb->h.th->cwr = 1; | 57 | skb->h.th->cwr = 1; |
58 | if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) | 58 | skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; |
59 | skb_shinfo(skb)->gso_type |= | ||
60 | SKB_GSO_TCPV4_ECN; | ||
61 | } | 59 | } |
62 | } else { | 60 | } else { |
63 | /* ACK or retransmitted segment: clear ECT|CE */ | 61 | /* ACK or retransmitted segment: clear ECT|CE */ |
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 7624fd1d8f9f..243d2a763363 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -744,7 +744,7 @@ static inline int ip_ufo_append_data(struct sock *sk, | |||
744 | if (!err) { | 744 | if (!err) { |
745 | /* specify the length of each IP datagram fragment*/ | 745 | /* specify the length of each IP datagram fragment*/ |
746 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen; | 746 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen; |
747 | skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; | 747 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
748 | __skb_queue_tail(&sk->sk_write_queue, skb); | 748 | __skb_queue_tail(&sk->sk_write_queue, skb); |
749 | 749 | ||
750 | return 0; | 750 | return 0; |
@@ -1089,7 +1089,7 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, | |||
1089 | if ((sk->sk_protocol == IPPROTO_UDP) && | 1089 | if ((sk->sk_protocol == IPPROTO_UDP) && |
1090 | (rt->u.dst.dev->features & NETIF_F_UFO)) { | 1090 | (rt->u.dst.dev->features & NETIF_F_UFO)) { |
1091 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen; | 1091 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen; |
1092 | skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; | 1092 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
1093 | } | 1093 | } |
1094 | 1094 | ||
1095 | 1095 | ||
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e19457fe4f6e..0f26073117a3 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -660,8 +660,6 @@ int inet6_sk_rebuild_header(struct sock *sk) | |||
660 | } | 660 | } |
661 | 661 | ||
662 | ip6_dst_store(sk, dst, NULL); | 662 | ip6_dst_store(sk, dst, NULL); |
663 | sk->sk_route_caps = dst->dev->features & | ||
664 | ~(NETIF_F_IP_CSUM | NETIF_F_TSO); | ||
665 | } | 663 | } |
666 | 664 | ||
667 | return 0; | 665 | return 0; |
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index eb2865d5ae28..5624f373164d 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c | |||
@@ -187,8 +187,6 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok) | |||
187 | } | 187 | } |
188 | 188 | ||
189 | ip6_dst_store(sk, dst, NULL); | 189 | ip6_dst_store(sk, dst, NULL); |
190 | sk->sk_route_caps = dst->dev->features & | ||
191 | ~(NETIF_F_IP_CSUM | NETIF_F_TSO); | ||
192 | } | 190 | } |
193 | 191 | ||
194 | skb->dst = dst_clone(dst); | 192 | skb->dst = dst_clone(dst); |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index abb94de33768..11007c75ae02 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -230,7 +230,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, | |||
230 | skb->priority = sk->sk_priority; | 230 | skb->priority = sk->sk_priority; |
231 | 231 | ||
232 | mtu = dst_mtu(dst); | 232 | mtu = dst_mtu(dst); |
233 | if ((skb->len <= mtu) || ipfragok) { | 233 | if ((skb->len <= mtu) || ipfragok || skb_shinfo(skb)->gso_size) { |
234 | IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); | 234 | IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); |
235 | return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, | 235 | return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, |
236 | dst_output); | 236 | dst_output); |
@@ -835,7 +835,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, | |||
835 | /* specify the length of each IP datagram fragment*/ | 835 | /* specify the length of each IP datagram fragment*/ |
836 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen - | 836 | skb_shinfo(skb)->gso_size = mtu - fragheaderlen - |
837 | sizeof(struct frag_hdr); | 837 | sizeof(struct frag_hdr); |
838 | skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; | 838 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
839 | ipv6_select_ident(skb, &fhdr); | 839 | ipv6_select_ident(skb, &fhdr); |
840 | skb_shinfo(skb)->ip6_frag_id = fhdr.identification; | 840 | skb_shinfo(skb)->ip6_frag_id = fhdr.identification; |
841 | __skb_queue_tail(&sk->sk_write_queue, skb); | 841 | __skb_queue_tail(&sk->sk_write_queue, skb); |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index bf7f8c26c488..7ea5bea49aa9 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -270,9 +270,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
270 | ipv6_addr_copy(&np->saddr, saddr); | 270 | ipv6_addr_copy(&np->saddr, saddr); |
271 | inet->rcv_saddr = LOOPBACK4_IPV6; | 271 | inet->rcv_saddr = LOOPBACK4_IPV6; |
272 | 272 | ||
273 | sk->sk_gso_type = SKB_GSO_TCPV6; | ||
273 | ip6_dst_store(sk, dst, NULL); | 274 | ip6_dst_store(sk, dst, NULL); |
274 | sk->sk_route_caps = dst->dev->features & | ||
275 | ~(NETIF_F_IP_CSUM | NETIF_F_TSO); | ||
276 | 275 | ||
277 | icsk->icsk_ext_hdr_len = 0; | 276 | icsk->icsk_ext_hdr_len = 0; |
278 | if (np->opt) | 277 | if (np->opt) |
@@ -930,9 +929,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
930 | * comment in that function for the gory details. -acme | 929 | * comment in that function for the gory details. -acme |
931 | */ | 930 | */ |
932 | 931 | ||
932 | sk->sk_gso_type = SKB_GSO_TCPV6; | ||
933 | ip6_dst_store(newsk, dst, NULL); | 933 | ip6_dst_store(newsk, dst, NULL); |
934 | newsk->sk_route_caps = dst->dev->features & | ||
935 | ~(NETIF_F_IP_CSUM | NETIF_F_TSO); | ||
936 | 934 | ||
937 | newtcp6sk = (struct tcp6_sock *)newsk; | 935 | newtcp6sk = (struct tcp6_sock *)newsk; |
938 | inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; | 936 | inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; |