X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Flinux%2Fcompat%2Fvxlan.c;h=b8b8fa762d76548bcc33ec231222954952de6235;hb=e2f3178f0582eda302bdc5629189b6a56d9fbcdd;hp=6e6b945001f523b733db5e973ce1153b2141e1cd;hpb=1b7ee51f9d340aa6c9f6a5772f4e79cbfd17565d;p=sliver-openvswitch.git diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c index 6e6b94500..b8b8fa762 100644 --- a/datapath/linux/compat/vxlan.c +++ b/datapath/linux/compat/vxlan.c @@ -18,6 +18,9 @@ * This code is derived from kernel vxlan module. */ +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -51,18 +54,11 @@ #include #include -#include "checksum.h" #include "compat.h" +#include "datapath.h" #include "gso.h" #include "vlan.h" -#define PORT_HASH_BITS 8 -#define PORT_HASH_SIZE (1<sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; -} - -/* Find VXLAN socket based on network namespace and UDP port */ -static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port) -{ - struct vxlan_sock *vs; - - hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) { - if (inet_sport(vs->sock->sk) == port) - return vs; - } - return NULL; -} - /* Callback from net/ipv4/udp.c to receive packets */ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { - struct vxlan_handler *vh; struct vxlan_sock *vs; struct vxlanhdr *vxh; @@ -124,14 +91,12 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB))) goto drop; - vs = vxlan_find_port(sock_net(sk), inet_sport(sk)); + vs = rcu_dereference_sk_user_data(sk); if (!vs) goto drop; - list_for_each_entry_rcu(vh, &vs->handler_list, node) { - if (vh->rcv(vh, skb, vxh->vx_vni) == PACKET_RCVD) - return 0; - } + vs->rcv(vs, skb, vxh->vx_vni); + return 0; drop: /* Consume bad packet */ @@ -167,7 +132,7 @@ __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb) unsigned int range = (port_max - port_min) + 1; u32 hash; - hash = skb_get_rxhash(skb); + hash = skb_get_hash(skb); if (!hash) hash = jhash(skb->data, 2 * ETH_ALEN, (__force u32) skb->protocol); @@ -211,7 +176,7 @@ static int handle_offloads(struct sk_buff *skb) return 0; } -int vxlan_xmit_skb(struct net *net, struct vxlan_handler *vh, +int vxlan_xmit_skb(struct vxlan_sock *vs, struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, __be32 vni) @@ -221,8 +186,6 @@ int vxlan_xmit_skb(struct net *net, struct vxlan_handler *vh, int min_headroom; int err; - skb_reset_inner_headers(skb); - min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + VXLAN_HLEN + sizeof(struct iphdr) + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0); @@ -232,8 +195,16 @@ int vxlan_xmit_skb(struct net *net, struct vxlan_handler *vh, if (unlikely(err)) return err; - if (unlikely(vlan_deaccel_tag(skb))) - return -ENOMEM; + if (vlan_tx_tag_present(skb)) { + if (unlikely(!__vlan_put_tag(skb, + skb->vlan_proto, + vlan_tx_tag_get(skb)))) + return -ENOMEM; + + vlan_set_tci(skb, 0); + } + + skb_reset_inner_headers(skb); vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = htonl(VXLAN_FLAGS); @@ -249,17 +220,32 @@ int vxlan_xmit_skb(struct net *net, struct vxlan_handler *vh, uh->len = htons(skb->len); uh->check = 0; - vxlan_set_owner(vh->vs->sock->sk, skb); + vxlan_set_owner(vs->sock->sk, skb); err = handle_offloads(skb); if (err) return err; - return iptunnel_xmit(net, rt, skb, src, dst, - IPPROTO_UDP, tos, ttl, df); + return iptunnel_xmit(rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df, false); } -static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port) +static void rcu_free_vs(struct rcu_head *rcu) +{ + struct vxlan_sock *vs = container_of(rcu, struct vxlan_sock, rcu); + + kfree(vs); +} + +static void vxlan_del_work(struct work_struct *work) +{ + struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work); + + sk_release_kernel(vs->sock->sk); + call_rcu(&vs->rcu, rcu_free_vs); +} + +static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, + vxlan_rcv_t *rcv, void *data) { struct vxlan_sock *vs; struct sock *sk; @@ -271,8 +257,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port) int rc; vs = kmalloc(sizeof(*vs), GFP_KERNEL); - if (!vs) + if (!vs) { + pr_debug("memory alocation failure\n"); return ERR_PTR(-ENOMEM); + } + + INIT_WORK(&vs->del_work, vxlan_del_work); /* Create UDP socket for encapsulation receive. */ rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vs->sock); @@ -287,193 +277,41 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port) sk_change_net(sk, net); rc = kernel_bind(vs->sock, (struct sockaddr *) &vxlan_addr, - sizeof(vxlan_addr)); + sizeof(vxlan_addr)); if (rc < 0) { pr_debug("bind for UDP socket %pI4:%u (%d)\n", - &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); + &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); sk_release_kernel(sk); kfree(vs); return ERR_PTR(rc); } + vs->rcv = rcv; + vs->data = data; /* Disable multicast loopback */ inet_sk(sk)->mc_loop = 0; - INIT_LIST_HEAD(&vs->handler_list); - hlist_add_head_rcu(&vs->hlist, vs_head(net, port)); + rcu_assign_sk_user_data(vs->sock->sk, vs); /* Mark socket as an encapsulation socket. */ udp_sk(sk)->encap_type = 1; udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv; udp_encap_enable(); - return vs; } -static void rcu_free_vs_callback(struct rcu_head *rcu) -{ - struct vxlan_sock *vs = container_of(rcu, struct vxlan_sock, rcu); - - kfree(vs); -} - -static void vxlan_socket_del(struct vxlan_sock *vs) -{ - if (list_empty(&vs->handler_list)) { - hlist_del_rcu(&vs->hlist); - - sk_release_kernel(vs->sock->sk); - call_rcu(&vs->rcu, rcu_free_vs_callback); - } -} - -static int vxlan_init_module(void); -static void vxlan_cleanup_module(void); - -static void rcu_free_vh_callback(struct rcu_head *rcu) +struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, + vxlan_rcv_t *rcv, void *data, + bool no_share, bool ipv6) { - struct vxlan_handler *vh = container_of(rcu, struct vxlan_handler, rcu); - - kfree(vh); + return vxlan_socket_create(net, port, rcv, data); } -static void vh_del_work(struct work_struct *work) +void vxlan_sock_release(struct vxlan_sock *vs) { - struct vxlan_handler *vh = container_of(work, struct vxlan_handler, del_work); - struct vxlan_sock *vs = vh->vs; - struct net *net = sock_net(vs->sock->sk); - struct vxlan_net *vn = net_generic(net, vxlan_net_id); + ASSERT_OVSL(); + rcu_assign_sk_user_data(vs->sock->sk, NULL); - mutex_lock(&vn->sock_lock); - - list_del_rcu(&vh->node); - call_rcu(&vh->rcu, rcu_free_vh_callback); - vxlan_socket_del(vs); - - mutex_unlock(&vn->sock_lock); - - vxlan_cleanup_module(); + queue_work(system_wq, &vs->del_work); } -struct vxlan_handler *vxlan_handler_add(struct net *net, - __be16 portno, vxlan_rcv_t *rcv, - void *data, int priority, bool create) -{ - struct vxlan_net *vn; - struct vxlan_sock *vs; - struct vxlan_handler *vh; - struct vxlan_handler *new; - int err; - - err = vxlan_init_module(); - if (err) - return ERR_PTR(err); - - vn = net_generic(net, vxlan_net_id); - mutex_lock(&vn->sock_lock); - /* Look to see if can reuse socket */ - vs = vxlan_find_port(net, portno); - if (!vs) { - vs = vxlan_socket_create(net, portno); - if (IS_ERR(vs)) { - new = (void *) vs; - goto out; - } - } - - /* Try existing vxlan hanlders for this socket. */ - list_for_each_entry(vh, &vs->handler_list, node) { - if (vh->rcv == rcv) { - if (create) { - vxlan_socket_del(vs); - new = ERR_PTR(-EEXIST); - goto out; - } - atomic_inc(&vh->refcnt); - new = vh; - goto out; - } - } - - new = kzalloc(sizeof(*new), GFP_KERNEL); - if (!new) { - vxlan_socket_del(vs); - new = ERR_PTR(-ENOMEM); - goto out; - } - - new->rcv = rcv; - new->vs = vs; - atomic_set(&new->refcnt, 1); - INIT_WORK(&new->del_work, vh_del_work); - new->data = data; - new->priority = priority; - - list_for_each_entry(vh, &vs->handler_list, node) { - if (vh->priority > priority) { - list_add_tail_rcu(&new->node, &vh->node); - goto out; - } - } - - list_add_tail_rcu(&new->node, &vs->handler_list); -out: - mutex_unlock(&vn->sock_lock); - return new; -} - -void vxlan_handler_put(struct vxlan_handler *vh) -{ - BUG_ON(!vh->vs); - - if (atomic_dec_and_test(&vh->refcnt)) - queue_work(&vh->del_work); -} - -static __net_init int vxlan_init_net(struct net *net) -{ - struct vxlan_net *vn = net_generic(net, vxlan_net_id); - unsigned int h; - - mutex_init(&vn->sock_lock); - - for (h = 0; h < PORT_HASH_SIZE; ++h) - INIT_HLIST_HEAD(&vn->sock_list[h]); - - return 0; -} - -static struct pernet_operations vxlan_net_ops = { - .init = vxlan_init_net, - .id = &vxlan_net_id, - .size = sizeof(struct vxlan_net), -}; - -static int refcnt; -static DEFINE_MUTEX(init_lock); -DEFINE_COMPAT_PNET_REG_FUNC(device); - -static int vxlan_init_module(void) -{ - int err = 0; - - mutex_lock(&init_lock); - if (refcnt) - goto out; - err = register_pernet_device(&vxlan_net_ops); -out: - if (!err) - refcnt++; - mutex_unlock(&init_lock); - return err; -} - -static void vxlan_cleanup_module(void) -{ - mutex_lock(&init_lock); - refcnt--; - if (refcnt) - goto out; - unregister_pernet_device(&vxlan_net_ops); -out: - mutex_unlock(&init_lock); -} +#endif /* 3.12 */