gro: Fix frag_list merging on imprecisely split packets
[pandora-kernel.git] / net / core / dev.c
index b715a55..247f161 100644 (file)
 /* This should be increased if a protocol with a bigger head is added. */
 #define GRO_MAX_HEAD (MAX_HEADER + 128)
 
+enum {
+       GRO_MERGED,
+       GRO_MERGED_FREE,
+       GRO_HELD,
+       GRO_NORMAL,
+       GRO_DROP,
+};
+
 /*
  *     The list of packet types we will receive (as opposed to discard)
  *     and the routines to invoke.
@@ -207,6 +215,13 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
        return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)];
 }
 
+static inline void *skb_gro_mac_header(struct sk_buff *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;
+}
+
 /* Device list insertion */
 static int list_netdevice(struct net_device *dev)
 {
@@ -1534,7 +1549,19 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
        skb->mac_len = skb->network_header - skb->mac_header;
        __skb_pull(skb, skb->mac_len);
 
-       if (WARN_ON(skb->ip_summed != CHECKSUM_PARTIAL)) {
+       if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+               struct net_device *dev = skb->dev;
+               struct ethtool_drvinfo info = {};
+
+               if (dev && dev->ethtool_ops && dev->ethtool_ops->get_drvinfo)
+                       dev->ethtool_ops->get_drvinfo(dev, &info);
+
+               WARN(1, "%s: caps=(0x%lx, 0x%lx) len=%d data_len=%d "
+                       "ip_summed=%d",
+                    info.driver, dev ? dev->features : 0L,
+                    skb->sk ? skb->sk->sk_route_caps : 0L,
+                    skb->len, skb->data_len, skb->ip_summed);
+
                if (skb_header_cloned(skb) &&
                    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
                        return ERR_PTR(err);
@@ -1696,56 +1723,26 @@ out_kfree_skb:
        return 0;
 }
 
-static u32 simple_tx_hashrnd;
-static int simple_tx_hashrnd_initialized = 0;
+static u32 skb_tx_hashrnd;
+static int skb_tx_hashrnd_initialized = 0;
 
-static u16 simple_tx_hash(struct net_device *dev, struct sk_buff *skb)
+static u16 skb_tx_hash(struct net_device *dev, struct sk_buff *skb)
 {
-       u32 addr1, addr2, ports;
-       u32 hash, ihl;
-       u8 ip_proto = 0;
+       u32 hash;
 
-       if (unlikely(!simple_tx_hashrnd_initialized)) {
-               get_random_bytes(&simple_tx_hashrnd, 4);
-               simple_tx_hashrnd_initialized = 1;
+       if (unlikely(!skb_tx_hashrnd_initialized)) {
+               get_random_bytes(&skb_tx_hashrnd, 4);
+               skb_tx_hashrnd_initialized = 1;
        }
 
-       switch (skb->protocol) {
-       case htons(ETH_P_IP):
-               if (!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)))
-                       ip_proto = ip_hdr(skb)->protocol;
-               addr1 = ip_hdr(skb)->saddr;
-               addr2 = ip_hdr(skb)->daddr;
-               ihl = ip_hdr(skb)->ihl;
-               break;
-       case htons(ETH_P_IPV6):
-               ip_proto = ipv6_hdr(skb)->nexthdr;
-               addr1 = ipv6_hdr(skb)->saddr.s6_addr32[3];
-               addr2 = ipv6_hdr(skb)->daddr.s6_addr32[3];
-               ihl = (40 >> 2);
-               break;
-       default:
-               return 0;
-       }
-
-
-       switch (ip_proto) {
-       case IPPROTO_TCP:
-       case IPPROTO_UDP:
-       case IPPROTO_DCCP:
-       case IPPROTO_ESP:
-       case IPPROTO_AH:
-       case IPPROTO_SCTP:
-       case IPPROTO_UDPLITE:
-               ports = *((u32 *) (skb_network_header(skb) + (ihl * 4)));
-               break;
-
-       default:
-               ports = 0;
-               break;
-       }
+       if (skb_rx_queue_recorded(skb)) {
+               hash = skb_get_rx_queue(skb);
+       } else if (skb->sk && skb->sk->sk_hash) {
+               hash = skb->sk->sk_hash;
+       } else
+               hash = skb->protocol;
 
-       hash = jhash_3words(addr1, addr2, ports, simple_tx_hashrnd);
+       hash = jhash_1word(hash, skb_tx_hashrnd);
 
        return (u16) (((u64) hash * dev->real_num_tx_queues) >> 32);
 }
@@ -1759,7 +1756,7 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev,
        if (ops->ndo_select_queue)
                queue_index = ops->ndo_select_queue(dev, skb);
        else if (dev->real_num_tx_queues > 1)
-               queue_index = simple_tx_hash(dev, skb);
+               queue_index = skb_tx_hash(dev, skb);
 
        skb_set_queue_mapping(skb, queue_index);
        return netdev_get_tx_queue(dev, queue_index);
@@ -2291,6 +2288,8 @@ ncls:
        if (!skb)
                goto out;
 
+       skb_orphan(skb);
+
        type = skb->protocol;
        list_for_each_entry_rcu(ptype,
                        &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
@@ -2360,7 +2359,6 @@ static int napi_gro_complete(struct sk_buff *skb)
 
 out:
        skb_shinfo(skb)->gso_size = 0;
-       __skb_push(skb, -skb_network_offset(skb));
        return netif_receive_skb(skb);
 }
 
@@ -2378,6 +2376,26 @@ void napi_gro_flush(struct napi_struct *napi)
 }
 EXPORT_SYMBOL(napi_gro_flush);
 
+void *skb_gro_header(struct sk_buff *skb, unsigned int hlen)
+{
+       unsigned int offset = skb_gro_offset(skb);
+
+       hlen += offset;
+       if (hlen <= skb_headlen(skb))
+               return skb->data + offset;
+
+       if (unlikely(!skb_shinfo(skb)->nr_frags ||
+                    skb_shinfo(skb)->frags[0].size <=
+                    hlen - skb_headlen(skb) ||
+                    PageHighMem(skb_shinfo(skb)->frags[0].page)))
+               return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL;
+
+       return page_address(skb_shinfo(skb)->frags[0].page) +
+              skb_shinfo(skb)->frags[0].page_offset +
+              offset - skb_headlen(skb);
+}
+EXPORT_SYMBOL(skb_gro_header);
+
 int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
        struct sk_buff **pp = NULL;
@@ -2387,19 +2405,24 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
        int count = 0;
        int same_flow;
        int mac_len;
-       int free;
+       int ret;
 
        if (!(skb->dev->features & NETIF_F_GRO))
                goto normal;
 
+       if (skb_is_gso(skb) || skb_shinfo(skb)->frag_list)
+               goto normal;
+
        rcu_read_lock();
        list_for_each_entry_rcu(ptype, head, list) {
                struct sk_buff *p;
+               void *mac;
 
                if (ptype->type != type || ptype->dev || !ptype->gro_receive)
                        continue;
 
-               skb_reset_network_header(skb);
+               skb_set_network_header(skb, skb_gro_offset(skb));
+               mac = skb_gro_mac_header(skb);
                mac_len = skb->network_header - skb->mac_header;
                skb->mac_len = mac_len;
                NAPI_GRO_CB(skb)->same_flow = 0;
@@ -2413,8 +2436,7 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
                                continue;
 
                        if (p->mac_len != mac_len ||
-                           memcmp(skb_mac_header(p), skb_mac_header(skb),
-                                  mac_len))
+                           memcmp(skb_mac_header(p), mac, mac_len))
                                NAPI_GRO_CB(p)->same_flow = 0;
                }
 
@@ -2427,7 +2449,7 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
                goto normal;
 
        same_flow = NAPI_GRO_CB(skb)->same_flow;
-       free = NAPI_GRO_CB(skb)->free;
+       ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE : GRO_MERGED;
 
        if (pp) {
                struct sk_buff *nskb = *pp;
@@ -2441,21 +2463,28 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
        if (same_flow)
                goto ok;
 
-       if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS) {
-               __skb_push(skb, -skb_network_offset(skb));
+       if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS)
                goto normal;
-       }
 
        NAPI_GRO_CB(skb)->count = 1;
-       skb_shinfo(skb)->gso_size = skb->len;
+       skb_shinfo(skb)->gso_size = skb_gro_len(skb);
        skb->next = napi->gro_list;
        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 free;
+       return ret;
 
 normal:
-       return -1;
+       ret = GRO_NORMAL;
+       goto pull;
 }
 EXPORT_SYMBOL(dev_gro_receive);
 
@@ -2471,29 +2500,37 @@ static int __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
        return dev_gro_receive(napi, skb);
 }
 
-int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
+int napi_skb_finish(int ret, struct sk_buff *skb)
 {
-       switch (__napi_gro_receive(napi, skb)) {
-       case -1:
+       int err = NET_RX_SUCCESS;
+
+       switch (ret) {
+       case GRO_NORMAL:
                return netif_receive_skb(skb);
 
-       case 1:
+       case GRO_DROP:
+               err = NET_RX_DROP;
+               /* fall through */
+
+       case GRO_MERGED_FREE:
                kfree_skb(skb);
                break;
        }
 
-       return NET_RX_SUCCESS;
+       return err;
 }
-EXPORT_SYMBOL(napi_gro_receive);
+EXPORT_SYMBOL(napi_skb_finish);
 
-void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)
+int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
-       skb_shinfo(skb)->nr_frags = 0;
+       skb_gro_reset_offset(skb);
 
-       skb->len -= skb->data_len;
-       skb->truesize -= skb->data_len;
-       skb->data_len = 0;
+       return napi_skb_finish(__napi_gro_receive(napi, skb), skb);
+}
+EXPORT_SYMBOL(napi_gro_receive);
 
+void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)
+{
        __skb_pull(skb, skb_headlen(skb));
        skb_reserve(skb, NET_IP_ALIGN - skb_headroom(skb));
 
@@ -2506,6 +2543,9 @@ struct sk_buff *napi_fraginfo_skb(struct napi_struct *napi,
 {
        struct net_device *dev = napi->dev;
        struct sk_buff *skb = napi->skb;
+       struct ethhdr *eth;
+       skb_frag_t *frag;
+       int i;
 
        napi->skb = NULL;
 
@@ -2518,19 +2558,36 @@ struct sk_buff *napi_fraginfo_skb(struct napi_struct *napi,
        }
 
        BUG_ON(info->nr_frags > MAX_SKB_FRAGS);
+       frag = &info->frags[info->nr_frags - 1];
+
+       for (i = skb_shinfo(skb)->nr_frags; i < info->nr_frags; i++) {
+               skb_fill_page_desc(skb, i, frag->page, frag->page_offset,
+                                  frag->size);
+               frag++;
+       }
        skb_shinfo(skb)->nr_frags = info->nr_frags;
-       memcpy(skb_shinfo(skb)->frags, info->frags, sizeof(info->frags));
 
        skb->data_len = info->len;
        skb->len += info->len;
        skb->truesize += info->len;
 
-       if (!pskb_may_pull(skb, ETH_HLEN)) {
+       skb_reset_mac_header(skb);
+       skb_gro_reset_offset(skb);
+
+       eth = skb_gro_header(skb, sizeof(*eth));
+       if (!eth) {
                napi_reuse_skb(napi, skb);
+               skb = NULL;
                goto out;
        }
 
-       skb->protocol = eth_type_trans(skb, dev);
+       skb_gro_pull(skb, sizeof(*eth));
+
+       /*
+        * This works because the only protocols we care about don't require
+        * special handling.  We'll fix it up properly at the end.
+        */
+       skb->protocol = eth->h_proto;
 
        skb->ip_summed = info->ip_summed;
        skb->csum = info->csum;
@@ -2540,29 +2597,43 @@ out:
 }
 EXPORT_SYMBOL(napi_fraginfo_skb);
 
-int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info)
+int napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, int ret)
 {
-       struct sk_buff *skb = napi_fraginfo_skb(napi, info);
-       int err = NET_RX_DROP;
+       int err = NET_RX_SUCCESS;
 
-       if (!skb)
-               goto out;
+       switch (ret) {
+       case GRO_NORMAL:
+       case GRO_HELD:
+               skb->protocol = eth_type_trans(skb, napi->dev);
 
-       err = NET_RX_SUCCESS;
+               if (ret == GRO_NORMAL)
+                       return netif_receive_skb(skb);
 
-       switch (__napi_gro_receive(napi, skb)) {
-       case -1:
-               return netif_receive_skb(skb);
+               skb_gro_pull(skb, -ETH_HLEN);
+               break;
 
-       case 0:
-               goto out;
-       }
+       case GRO_DROP:
+               err = NET_RX_DROP;
+               /* fall through */
 
-       napi_reuse_skb(napi, skb);
+       case GRO_MERGED_FREE:
+               napi_reuse_skb(napi, skb);
+               break;
+       }
 
-out:
        return err;
 }
+EXPORT_SYMBOL(napi_frags_finish);
+
+int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info)
+{
+       struct sk_buff *skb = napi_fraginfo_skb(napi, info);
+
+       if (!skb)
+               return NET_RX_DROP;
+
+       return napi_frags_finish(napi, skb, __napi_gro_receive(napi, skb));
+}
 EXPORT_SYMBOL(napi_gro_frags);
 
 static int process_backlog(struct napi_struct *napi, int quota)
@@ -4433,6 +4504,45 @@ err_uninit:
        goto out;
 }
 
+/**
+ *     init_dummy_netdev       - init a dummy network device for NAPI
+ *     @dev: device to init
+ *
+ *     This takes a network device structure and initialize the minimum
+ *     amount of fields so it can be used to schedule NAPI polls without
+ *     registering a full blown interface. This is to be used by drivers
+ *     that need to tie several hardware interfaces to a single NAPI
+ *     poll scheduler due to HW limitations.
+ */
+int init_dummy_netdev(struct net_device *dev)
+{
+       /* Clear everything. Note we don't initialize spinlocks
+        * are they aren't supposed to be taken by any of the
+        * NAPI code and this dummy netdev is supposed to be
+        * only ever used for NAPI polls
+        */
+       memset(dev, 0, sizeof(struct net_device));
+
+       /* make sure we BUG if trying to hit standard
+        * register/unregister code path
+        */
+       dev->reg_state = NETREG_DUMMY;
+
+       /* initialize the ref count */
+       atomic_set(&dev->refcnt, 1);
+
+       /* NAPI wants this */
+       INIT_LIST_HEAD(&dev->napi_list);
+
+       /* a dummy interface is started by default */
+       set_bit(__LINK_STATE_PRESENT, &dev->state);
+       set_bit(__LINK_STATE_START, &dev->state);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(init_dummy_netdev);
+
+
 /**
  *     register_netdev - register a network device
  *     @dev: device to register