summaryrefslogtreecommitdiff
path: root/net/core/dev.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2009-02-01 12:24:55 +0300
committerDavid S. Miller <davem@davemloft.net>2009-02-01 12:24:55 +0300
commitad0f9904444de1309dedd2b9e365cae8af77d9b1 (patch)
treec60a3572d8a84aaee2b4a1dc63d69815013d2273 /net/core/dev.c
parent3efac5a0012979ae236fe1247b773317ef5f1c88 (diff)
downloadlinux-ad0f9904444de1309dedd2b9e365cae8af77d9b1.tar.xz
gro: Fix handling of imprecisely split packets
The commit 89a1b249edcf9be884e71f92df84d48355c576aa (gro: Avoid copying headers of unmerged packets) only worked for packets which are either completely linear, completely non-linear, or packets which exactly split at the boundary between headers and payload. Anything else would cause bits in the header to go missing if the packet is held by GRO. This may have broken drivers such as ixgbe. This patch fixes the places that assumed or only worked with the above cases. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r--net/core/dev.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index ec5be1c7f2f1..220f52a1001e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -217,7 +217,7 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
static inline void *skb_gro_mac_header(struct sk_buff *skb)
{
- return skb_headlen(skb) ? skb_mac_header(skb) :
+ return skb_mac_header(skb) < skb->data ? skb_mac_header(skb) :
page_address(skb_shinfo(skb)->frags[0].page) +
skb_shinfo(skb)->frags[0].page_offset;
}
@@ -2469,11 +2469,19 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
napi->gro_list = skb;
ret = GRO_HELD;
+pull:
+ if (unlikely(!pskb_may_pull(skb, skb_gro_offset(skb)))) {
+ if (napi->gro_list == skb)
+ napi->gro_list = skb->next;
+ ret = GRO_DROP;
+ }
+
ok:
return ret;
normal:
- return GRO_NORMAL;
+ ret = GRO_NORMAL;
+ goto pull;
}
EXPORT_SYMBOL(dev_gro_receive);
@@ -2589,14 +2597,10 @@ EXPORT_SYMBOL(napi_fraginfo_skb);
int napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, int ret)
{
int err = NET_RX_SUCCESS;
- int may;
switch (ret) {
case GRO_NORMAL:
case GRO_HELD:
- may = pskb_may_pull(skb, skb_gro_offset(skb));
- BUG_ON(!may);
-
skb->protocol = eth_type_trans(skb, napi->dev);
if (ret == GRO_NORMAL)