These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / 8021q / vlan.c
index 59555f0..d2cd9de 100644 (file)
@@ -618,6 +618,92 @@ out:
        return err;
 }
 
+static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
+                                        struct sk_buff *skb)
+{
+       struct sk_buff *p, **pp = NULL;
+       struct vlan_hdr *vhdr;
+       unsigned int hlen, off_vlan;
+       const struct packet_offload *ptype;
+       __be16 type;
+       int flush = 1;
+
+       off_vlan = skb_gro_offset(skb);
+       hlen = off_vlan + sizeof(*vhdr);
+       vhdr = skb_gro_header_fast(skb, off_vlan);
+       if (skb_gro_header_hard(skb, hlen)) {
+               vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
+               if (unlikely(!vhdr))
+                       goto out;
+       }
+
+       type = vhdr->h_vlan_encapsulated_proto;
+
+       rcu_read_lock();
+       ptype = gro_find_receive_by_type(type);
+       if (!ptype)
+               goto out_unlock;
+
+       flush = 0;
+
+       for (p = *head; p; p = p->next) {
+               struct vlan_hdr *vhdr2;
+
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
+               if (compare_vlan_header(vhdr, vhdr2))
+                       NAPI_GRO_CB(p)->same_flow = 0;
+       }
+
+       skb_gro_pull(skb, sizeof(*vhdr));
+       skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
+       pp = ptype->callbacks.gro_receive(head, skb);
+
+out_unlock:
+       rcu_read_unlock();
+out:
+       NAPI_GRO_CB(skb)->flush |= flush;
+
+       return pp;
+}
+
+static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
+{
+       struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
+       __be16 type = vhdr->h_vlan_encapsulated_proto;
+       struct packet_offload *ptype;
+       int err = -ENOENT;
+
+       rcu_read_lock();
+       ptype = gro_find_complete_by_type(type);
+       if (ptype)
+               err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
+
+       rcu_read_unlock();
+       return err;
+}
+
+static struct packet_offload vlan_packet_offloads[] __read_mostly = {
+       {
+               .type = cpu_to_be16(ETH_P_8021Q),
+               .priority = 10,
+               .callbacks = {
+                       .gro_receive = vlan_gro_receive,
+                       .gro_complete = vlan_gro_complete,
+               },
+       },
+       {
+               .type = cpu_to_be16(ETH_P_8021AD),
+               .priority = 10,
+               .callbacks = {
+                       .gro_receive = vlan_gro_receive,
+                       .gro_complete = vlan_gro_complete,
+               },
+       },
+};
+
 static int __net_init vlan_init_net(struct net *net)
 {
        struct vlan_net *vn = net_generic(net, vlan_net_id);
@@ -645,6 +731,7 @@ static struct pernet_operations vlan_net_ops = {
 static int __init vlan_proto_init(void)
 {
        int err;
+       unsigned int i;
 
        pr_info("%s v%s\n", vlan_fullname, vlan_version);
 
@@ -668,6 +755,9 @@ static int __init vlan_proto_init(void)
        if (err < 0)
                goto err5;
 
+       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
+               dev_add_offload(&vlan_packet_offloads[i]);
+
        vlan_ioctl_set(vlan_ioctl_handler);
        return 0;
 
@@ -685,7 +775,13 @@ err0:
 
 static void __exit vlan_cleanup_module(void)
 {
+       unsigned int i;
+
        vlan_ioctl_set(NULL);
+
+       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
+               dev_remove_offload(&vlan_packet_offloads[i]);
+
        vlan_netlink_fini();
 
        unregister_netdevice_notifier(&vlan_notifier_block);