aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2009-01-29 09:19:50 -0500
committerDavid S. Miller <davem@davemloft.net>2009-01-29 19:33:03 -0500
commit86911732d3996a9da07914b280621450111bb6da (patch)
treee787240d5ba869ddf4d0adfc3f9c69e0372e96ef /net/ipv6
parent5d0d9be8ef456afc6c3fb5f8aad06ef19b704b05 (diff)
gro: Avoid copying headers of unmerged packets
Unfortunately simplicity isn't always the best. The fraginfo interface turned out to be suboptimal. The problem was quite obvious. For every packet, we have to copy the headers from the frags structure into skb->head, even though for 99% of the packets this part is immediately thrown away after the merge. LRO didn't have this problem because it directly read the headers from the frags structure. This patch attempts to address this by creating an interface that allows GRO to access the headers in the first frag without having to copy it. Because all drivers that use frags place the headers in the first frag this optimisation should be enough. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/af_inet6.c30
-rw-r--r--net/ipv6/tcp_ipv6.c2
2 files changed, 21 insertions, 11 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index c802bc1658a8..bd91eadcbe3f 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -799,24 +799,34 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
799 int proto; 799 int proto;
800 __wsum csum; 800 __wsum csum;
801 801
802 if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) 802 iph = skb_gro_header(skb, sizeof(*iph));
803 if (unlikely(!iph))
803 goto out; 804 goto out;
804 805
805 iph = ipv6_hdr(skb); 806 skb_gro_pull(skb, sizeof(*iph));
806 __skb_pull(skb, sizeof(*iph)); 807 skb_set_transport_header(skb, skb_gro_offset(skb));
807 808
808 flush += ntohs(iph->payload_len) != skb->len; 809 flush += ntohs(iph->payload_len) != skb_gro_len(skb);
809 810
810 rcu_read_lock(); 811 rcu_read_lock();
811 proto = ipv6_gso_pull_exthdrs(skb, iph->nexthdr); 812 proto = iph->nexthdr;
812 iph = ipv6_hdr(skb);
813 IPV6_GRO_CB(skb)->proto = proto;
814 ops = rcu_dereference(inet6_protos[proto]); 813 ops = rcu_dereference(inet6_protos[proto]);
815 if (!ops || !ops->gro_receive) 814 if (!ops || !ops->gro_receive) {
816 goto out_unlock; 815 __pskb_pull(skb, skb_gro_offset(skb));
816 proto = ipv6_gso_pull_exthdrs(skb, proto);
817 skb_gro_pull(skb, -skb_transport_offset(skb));
818 skb_reset_transport_header(skb);
819 __skb_push(skb, skb_gro_offset(skb));
820
821 if (!ops || !ops->gro_receive)
822 goto out_unlock;
823
824 iph = ipv6_hdr(skb);
825 }
826
827 IPV6_GRO_CB(skb)->proto = proto;
817 828
818 flush--; 829 flush--;
819 skb_reset_transport_header(skb);
820 nlen = skb_network_header_len(skb); 830 nlen = skb_network_header_len(skb);
821 831
822 for (p = *head; p; p = p->next) { 832 for (p = *head; p; p = p->next) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index e5b85d45bee8..00f1269e11e9 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -948,7 +948,7 @@ struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb)
948 948
949 switch (skb->ip_summed) { 949 switch (skb->ip_summed) {
950 case CHECKSUM_COMPLETE: 950 case CHECKSUM_COMPLETE:
951 if (!tcp_v6_check(skb->len, &iph->saddr, &iph->daddr, 951 if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
952 skb->csum)) { 952 skb->csum)) {
953 skb->ip_summed = CHECKSUM_UNNECESSARY; 953 skb->ip_summed = CHECKSUM_UNNECESSARY;
954 break; 954 break;