net: Add Transparent Ethernet Bridging GRO support.
authorJesse Gross <jesse@nicira.com>
Wed, 31 Dec 2014 03:10:15 +0000 (19:10 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 2 Jan 2015 20:46:41 +0000 (15:46 -0500)
Currently the only tunnel protocol that supports GRO with encapsulated
Ethernet is VXLAN. This pulls out the Ethernet code into a proper layer
so that it can be used by other tunnel protocols such as GRE and Geneve.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vxlan.c
include/linux/etherdevice.h
net/ethernet/eth.c

index 7fbd89fbe107878f5c2be4358bb13420ca6389ec..2ab0922af0b430f0cf6e6e0b88099cffba3d698f 100644 (file)
@@ -549,10 +549,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
 {
        struct sk_buff *p, **pp = NULL;
        struct vxlanhdr *vh, *vh2;
-       struct ethhdr *eh, *eh2;
-       unsigned int hlen, off_vx, off_eth;
-       const struct packet_offload *ptype;
-       __be16 type;
+       unsigned int hlen, off_vx;
        int flush = 1;
 
        off_vx = skb_gro_offset(skb);
@@ -563,17 +560,6 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
                if (unlikely(!vh))
                        goto out;
        }
-       skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
-       skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));
-
-       off_eth = skb_gro_offset(skb);
-       hlen = off_eth + sizeof(*eh);
-       eh   = skb_gro_header_fast(skb, off_eth);
-       if (skb_gro_header_hard(skb, hlen)) {
-               eh = skb_gro_header_slow(skb, hlen, off_eth);
-               if (unlikely(!eh))
-                       goto out;
-       }
 
        flush = 0;
 
@@ -582,28 +568,16 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
                        continue;
 
                vh2 = (struct vxlanhdr *)(p->data + off_vx);
-               eh2 = (struct ethhdr   *)(p->data + off_eth);
-               if (vh->vx_vni != vh2->vx_vni || compare_ether_header(eh, eh2)) {
+               if (vh->vx_vni != vh2->vx_vni) {
                        NAPI_GRO_CB(p)->same_flow = 0;
                        continue;
                }
        }
 
-       type = eh->h_proto;
-
-       rcu_read_lock();
-       ptype = gro_find_receive_by_type(type);
-       if (ptype == NULL) {
-               flush = 1;
-               goto out_unlock;
-       }
-
-       skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */
-       skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
-       pp = ptype->callbacks.gro_receive(head, skb);
+       skb_gro_pull(skb, sizeof(struct vxlanhdr));
+       skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));
+       pp = eth_gro_receive(head, skb);
 
-out_unlock:
-       rcu_read_unlock();
 out:
        NAPI_GRO_CB(skb)->flush |= flush;
 
@@ -612,24 +586,9 @@ out:
 
 static int vxlan_gro_complete(struct sk_buff *skb, int nhoff)
 {
-       struct ethhdr *eh;
-       struct packet_offload *ptype;
-       __be16 type;
-       int vxlan_len  = sizeof(struct vxlanhdr) + sizeof(struct ethhdr);
-       int err = -ENOSYS;
-
        udp_tunnel_gro_complete(skb, nhoff);
 
-       eh = (struct ethhdr *)(skb->data + nhoff + sizeof(struct vxlanhdr));
-       type = eh->h_proto;
-
-       rcu_read_lock();
-       ptype = gro_find_complete_by_type(type);
-       if (ptype != NULL)
-               err = ptype->callbacks.gro_complete(skb, nhoff + vxlan_len);
-
-       rcu_read_unlock();
-       return err;
+       return eth_gro_complete(skb, nhoff + sizeof(struct vxlanhdr));
 }
 
 /* Notify netdevs that UDP port started listening */
index 41c891d05f0434058a25bb45b069880027609401..1d869d185a0dbdb03b33de2b8c0af33c7ea865fa 100644 (file)
@@ -52,6 +52,10 @@ struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
 #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
 #define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
 
+struct sk_buff **eth_gro_receive(struct sk_buff **head,
+                                struct sk_buff *skb);
+int eth_gro_complete(struct sk_buff *skb, int nhoff);
+
 /* Reserved Ethernet Addresses per IEEE 802.1Q */
 static const u8 eth_reserved_addr_base[ETH_ALEN] __aligned(2) =
 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
index 33a140e1583449bcf2816bb91792e810f7c1734f..238f38d21641fb16071caacd90f15dcb07815a25 100644 (file)
@@ -424,3 +424,95 @@ ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len)
        return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);
 }
 EXPORT_SYMBOL(sysfs_format_mac);
+
+struct sk_buff **eth_gro_receive(struct sk_buff **head,
+                                struct sk_buff *skb)
+{
+       struct sk_buff *p, **pp = NULL;
+       struct ethhdr *eh, *eh2;
+       unsigned int hlen, off_eth;
+       const struct packet_offload *ptype;
+       __be16 type;
+       int flush = 1;
+
+       off_eth = skb_gro_offset(skb);
+       hlen = off_eth + sizeof(*eh);
+       eh = skb_gro_header_fast(skb, off_eth);
+       if (skb_gro_header_hard(skb, hlen)) {
+               eh = skb_gro_header_slow(skb, hlen, off_eth);
+               if (unlikely(!eh))
+                       goto out;
+       }
+
+       flush = 0;
+
+       for (p = *head; p; p = p->next) {
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               eh2 = (struct ethhdr *)(p->data + off_eth);
+               if (compare_ether_header(eh, eh2)) {
+                       NAPI_GRO_CB(p)->same_flow = 0;
+                       continue;
+               }
+       }
+
+       type = eh->h_proto;
+
+       rcu_read_lock();
+       ptype = gro_find_receive_by_type(type);
+       if (ptype == NULL) {
+               flush = 1;
+               goto out_unlock;
+       }
+
+       skb_gro_pull(skb, sizeof(*eh));
+       skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
+       pp = ptype->callbacks.gro_receive(head, skb);
+
+out_unlock:
+       rcu_read_unlock();
+out:
+       NAPI_GRO_CB(skb)->flush |= flush;
+
+       return pp;
+}
+EXPORT_SYMBOL(eth_gro_receive);
+
+int eth_gro_complete(struct sk_buff *skb, int nhoff)
+{
+       struct ethhdr *eh = (struct ethhdr *)(skb->data + nhoff);
+       __be16 type = eh->h_proto;
+       struct packet_offload *ptype;
+       int err = -ENOSYS;
+
+       if (skb->encapsulation)
+               skb_set_inner_mac_header(skb, nhoff);
+
+       rcu_read_lock();
+       ptype = gro_find_complete_by_type(type);
+       if (ptype != NULL)
+               err = ptype->callbacks.gro_complete(skb, nhoff +
+                                                   sizeof(struct ethhdr));
+
+       rcu_read_unlock();
+       return err;
+}
+EXPORT_SYMBOL(eth_gro_complete);
+
+static struct packet_offload eth_packet_offload __read_mostly = {
+       .type = cpu_to_be16(ETH_P_TEB),
+       .callbacks = {
+               .gro_receive = eth_gro_receive,
+               .gro_complete = eth_gro_complete,
+       },
+};
+
+static int __init eth_offload_init(void)
+{
+       dev_add_offload(&eth_packet_offload);
+
+       return 0;
+}
+
+fs_initcall(eth_offload_init);