]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/geneve.c
fib_trie: Fix RCU bug and merge similar bits of inflate/halve
[karo-tx-linux.git] / net / ipv4 / geneve.c
index 394a200f93c1f5b5338ccab70286545acad1ebda..93e51199e44b5c4adc5dc04f8e9a4492211008e8 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/skbuff.h>
-#include <linux/rculist.h>
+#include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/in.h>
 #include <linux/ip.h>
@@ -26,8 +26,8 @@
 #include <linux/etherdevice.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
-#include <linux/hash.h>
 #include <linux/ethtool.h>
+#include <linux/mutex.h>
 #include <net/arp.h>
 #include <net/ndisc.h>
 #include <net/ip.h>
 #include <net/ip6_checksum.h>
 #endif
 
-#define PORT_HASH_BITS 8
-#define PORT_HASH_SIZE (1<<PORT_HASH_BITS)
+/* Protects sock_list and refcounts. */
+static DEFINE_MUTEX(geneve_mutex);
 
 /* per-network namespace private data for this module */
 struct geneve_net {
-       struct hlist_head       sock_list[PORT_HASH_SIZE];
-       spinlock_t              sock_lock;   /* Protects sock_list */
+       struct list_head        sock_list;
 };
 
 static int geneve_net_id;
 
-static struct workqueue_struct *geneve_wq;
-
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
        return (struct genevehdr *)(udp_hdr(skb) + 1);
 }
 
-static struct hlist_head *gs_head(struct net *net, __be16 port)
+static struct geneve_sock *geneve_find_sock(struct net *net,
+                                           sa_family_t family, __be16 port)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
-
-       return &gn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
-}
-
-/* Find geneve socket based on network namespace and UDP port */
-static struct geneve_sock *geneve_find_sock(struct net *net, __be16 port)
-{
        struct geneve_sock *gs;
 
-       hlist_for_each_entry_rcu(gs, gs_head(net, port), hlist) {
-               if (inet_sk(gs->sock->sk)->inet_sport == port)
+       list_for_each_entry(gs, &gn->sock_list, list) {
+               if (inet_sk(gs->sock->sk)->inet_sport == port &&
+                   inet_sk(gs->sock->sk)->sk.sk_family == family)
                        return gs;
        }
 
@@ -127,7 +119,7 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
 
        min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
                        + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr)
-                       + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
+                       + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0);
 
        err = skb_cow_head(skb, min_headroom);
        if (unlikely(err)) {
@@ -144,11 +136,107 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 
-       return udp_tunnel_xmit_skb(gs->sock, rt, skb, src, dst,
-                                  tos, ttl, df, src_port, dst_port, xnet);
+       return udp_tunnel_xmit_skb(rt, skb, src, dst,
+                                  tos, ttl, df, src_port, dst_port, xnet,
+                                  gs->sock->sk->sk_no_check_tx);
 }
 EXPORT_SYMBOL_GPL(geneve_xmit_skb);
 
+static int geneve_hlen(struct genevehdr *gh)
+{
+       return sizeof(*gh) + gh->opt_len * 4;
+}
+
+static struct sk_buff **geneve_gro_receive(struct sk_buff **head,
+                                          struct sk_buff *skb,
+                                          struct udp_offload *uoff)
+{
+       struct sk_buff *p, **pp = NULL;
+       struct genevehdr *gh, *gh2;
+       unsigned int hlen, gh_len, off_gnv;
+       const struct packet_offload *ptype;
+       __be16 type;
+       int flush = 1;
+
+       off_gnv = skb_gro_offset(skb);
+       hlen = off_gnv + sizeof(*gh);
+       gh = skb_gro_header_fast(skb, off_gnv);
+       if (skb_gro_header_hard(skb, hlen)) {
+               gh = skb_gro_header_slow(skb, hlen, off_gnv);
+               if (unlikely(!gh))
+                       goto out;
+       }
+
+       if (gh->ver != GENEVE_VER || gh->oam)
+               goto out;
+       gh_len = geneve_hlen(gh);
+
+       hlen = off_gnv + gh_len;
+       if (skb_gro_header_hard(skb, hlen)) {
+               gh = skb_gro_header_slow(skb, hlen, off_gnv);
+               if (unlikely(!gh))
+                       goto out;
+       }
+
+       flush = 0;
+
+       for (p = *head; p; p = p->next) {
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               gh2 = (struct genevehdr *)(p->data + off_gnv);
+               if (gh->opt_len != gh2->opt_len ||
+                   memcmp(gh, gh2, gh_len)) {
+                       NAPI_GRO_CB(p)->same_flow = 0;
+                       continue;
+               }
+       }
+
+       type = gh->proto_type;
+
+       rcu_read_lock();
+       ptype = gro_find_receive_by_type(type);
+       if (ptype == NULL) {
+               flush = 1;
+               goto out_unlock;
+       }
+
+       skb_gro_pull(skb, gh_len);
+       skb_gro_postpull_rcsum(skb, gh, gh_len);
+       pp = ptype->callbacks.gro_receive(head, skb);
+
+out_unlock:
+       rcu_read_unlock();
+out:
+       NAPI_GRO_CB(skb)->flush |= flush;
+
+       return pp;
+}
+
+static int geneve_gro_complete(struct sk_buff *skb, int nhoff,
+                              struct udp_offload *uoff)
+{
+       struct genevehdr *gh;
+       struct packet_offload *ptype;
+       __be16 type;
+       int gh_len;
+       int err = -ENOSYS;
+
+       udp_tunnel_gro_complete(skb, nhoff);
+
+       gh = (struct genevehdr *)(skb->data + nhoff);
+       gh_len = geneve_hlen(gh);
+       type = gh->proto_type;
+
+       rcu_read_lock();
+       ptype = gro_find_complete_by_type(type);
+       if (ptype != NULL)
+               err = ptype->callbacks.gro_complete(skb, nhoff + gh_len);
+
+       rcu_read_unlock();
+       return err;
+}
+
 static void geneve_notify_add_rx_port(struct geneve_sock *gs)
 {
        struct sock *sk = gs->sock->sk;
@@ -214,15 +302,6 @@ error:
        return 1;
 }
 
-static void geneve_del_work(struct work_struct *work)
-{
-       struct geneve_sock *gs = container_of(work, struct geneve_sock,
-                                             del_work);
-
-       udp_tunnel_sock_release(gs->sock);
-       kfree_rcu(gs, rcu);
-}
-
 static struct socket *geneve_create_sock(struct net *net, bool ipv6,
                                         __be16 port)
 {
@@ -263,8 +342,6 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
        if (!gs)
                return ERR_PTR(-ENOMEM);
 
-       INIT_WORK(&gs->del_work, geneve_del_work);
-
        sock = geneve_create_sock(net, ipv6, port);
        if (IS_ERR(sock)) {
                kfree(gs);
@@ -272,19 +349,15 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
        }
 
        gs->sock = sock;
-       atomic_set(&gs->refcnt, 1);
+       gs->refcnt = 1;
        gs->rcv = rcv;
        gs->rcv_data = data;
 
        /* Initialize the geneve udp offloads structure */
        gs->udp_offloads.port = port;
-       gs->udp_offloads.callbacks.gro_receive = NULL;
-       gs->udp_offloads.callbacks.gro_complete = NULL;
-
-       spin_lock(&gn->sock_lock);
-       hlist_add_head_rcu(&gs->hlist, gs_head(net, port));
+       gs->udp_offloads.callbacks.gro_receive  = geneve_gro_receive;
+       gs->udp_offloads.callbacks.gro_complete = geneve_gro_complete;
        geneve_notify_add_rx_port(gs);
-       spin_unlock(&gn->sock_lock);
 
        /* Mark socket as an encapsulation socket */
        tunnel_cfg.sk_user_data = gs;
@@ -293,6 +366,8 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
        tunnel_cfg.encap_destroy = NULL;
        setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
 
+       list_add(&gs->list, &gn->sock_list);
+
        return gs;
 }
 
@@ -300,25 +375,21 @@ struct geneve_sock *geneve_sock_add(struct net *net, __be16 port,
                                    geneve_rcv_t *rcv, void *data,
                                    bool no_share, bool ipv6)
 {
-       struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_sock *gs;
 
-       gs = geneve_socket_create(net, port, rcv, data, ipv6);
-       if (!IS_ERR(gs))
-               return gs;
-
-       if (no_share)   /* Return error if sharing is not allowed. */
-               return ERR_PTR(-EINVAL);
+       mutex_lock(&geneve_mutex);
 
-       spin_lock(&gn->sock_lock);
-       gs = geneve_find_sock(net, port);
-       if (gs && ((gs->rcv != rcv) ||
-                  !atomic_add_unless(&gs->refcnt, 1, 0)))
+       gs = geneve_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port);
+       if (gs) {
+               if (!no_share && gs->rcv == rcv)
+                       gs->refcnt++;
+               else
                        gs = ERR_PTR(-EBUSY);
-       spin_unlock(&gn->sock_lock);
+       } else {
+               gs = geneve_socket_create(net, port, rcv, data, ipv6);
+       }
 
-       if (!gs)
-               gs = ERR_PTR(-EINVAL);
+       mutex_unlock(&geneve_mutex);
 
        return gs;
 }
@@ -326,37 +397,32 @@ EXPORT_SYMBOL_GPL(geneve_sock_add);
 
 void geneve_sock_release(struct geneve_sock *gs)
 {
-       struct net *net = sock_net(gs->sock->sk);
-       struct geneve_net *gn = net_generic(net, geneve_net_id);
+       mutex_lock(&geneve_mutex);
 
-       if (!atomic_dec_and_test(&gs->refcnt))
-               return;
+       if (--gs->refcnt)
+               goto unlock;
 
-       spin_lock(&gn->sock_lock);
-       hlist_del_rcu(&gs->hlist);
+       list_del(&gs->list);
        geneve_notify_del_rx_port(gs);
-       spin_unlock(&gn->sock_lock);
+       udp_tunnel_sock_release(gs->sock);
+       kfree_rcu(gs, rcu);
 
-       queue_work(geneve_wq, &gs->del_work);
+unlock:
+       mutex_unlock(&geneve_mutex);
 }
 EXPORT_SYMBOL_GPL(geneve_sock_release);
 
 static __net_init int geneve_init_net(struct net *net)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
-       unsigned int h;
 
-       spin_lock_init(&gn->sock_lock);
-
-       for (h = 0; h < PORT_HASH_SIZE; ++h)
-               INIT_HLIST_HEAD(&gn->sock_list[h]);
+       INIT_LIST_HEAD(&gn->sock_list);
 
        return 0;
 }
 
 static struct pernet_operations geneve_net_ops = {
        .init = geneve_init_net,
-       .exit = NULL,
        .id   = &geneve_net_id,
        .size = sizeof(struct geneve_net),
 };
@@ -365,10 +431,6 @@ static int __init geneve_init_module(void)
 {
        int rc;
 
-       geneve_wq = alloc_workqueue("geneve", 0, 0);
-       if (!geneve_wq)
-               return -ENOMEM;
-
        rc = register_pernet_subsys(&geneve_net_ops);
        if (rc)
                return rc;
@@ -377,11 +439,10 @@ static int __init geneve_init_module(void)
 
        return 0;
 }
-late_initcall(geneve_init_module);
+module_init(geneve_init_module);
 
 static void __exit geneve_cleanup_module(void)
 {
-       destroy_workqueue(geneve_wq);
        unregister_pernet_subsys(&geneve_net_ops);
 }
 module_exit(geneve_cleanup_module);