datapath lisp: Use ovs offload compat functionality.
authorPravin B Shelar <pshelar@nicira.com>
Fri, 6 Sep 2013 18:16:23 +0000 (11:16 -0700)
committerPravin B Shelar <pshelar@nicira.com>
Sat, 7 Sep 2013 20:47:52 +0000 (13:47 -0700)
OVS already has compat functions to handle GSO packets.
Following patch get rid of GSO packet handling in lisp
and use ovs iptunnel_xmit() function for same.

CC: Lori Jakab <lojakab@cisco.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
datapath/vport-lisp.c

index 2cec115..b1581ef 100644 (file)
@@ -34,6 +34,7 @@
 #include <net/xfrm.h>
 
 #include "datapath.h"
+#include "gso.h"
 #include "vport.h"
 
 /*
@@ -162,7 +163,7 @@ static __be64 instance_id_to_tunnel_id(__u8 *iid)
 /* Compute source UDP port for outgoing packet.
  * Currently we use the flow hash.
  */
-static u16 ovs_tnl_get_src_port(struct sk_buff *skb)
+static u16 get_src_port(struct sk_buff *skb)
 {
        int low;
        int high;
@@ -177,8 +178,7 @@ static u16 ovs_tnl_get_src_port(struct sk_buff *skb)
 }
 
 static void lisp_build_header(const struct vport *vport,
-                             struct sk_buff *skb,
-                             int tunnel_hlen)
+                             struct sk_buff *skb)
 {
        struct lisp_port *lisp_port = lisp_vport(vport);
        struct udphdr *udph = udp_hdr(skb);
@@ -186,7 +186,7 @@ static void lisp_build_header(const struct vport *vport,
        const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
 
        udph->dest = lisp_port->dst_port;
-       udph->source = htons(ovs_tnl_get_src_port(skb));
+       udph->source = htons(get_src_port(skb));
        udph->check = 0;
        udph->len = htons(skb->len - skb_transport_offset(skb));
 
@@ -376,90 +376,47 @@ error:
        return ERR_PTR(err);
 }
 
-static bool need_linearize(const struct sk_buff *skb)
+static void lisp_fix_segment(struct sk_buff *skb)
 {
-       int i;
-
-       if (unlikely(skb_shinfo(skb)->frag_list))
-               return true;
-
-       /*
-        * Generally speaking we should linearize if there are paged frags.
-        * However, if all of the refcounts are 1 we know nobody else can
-        * change them from underneath us and we can skip the linearization.
-        */
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
-               if (unlikely(page_count(skb_frag_page(&skb_shinfo(skb)->frags[i])) > 1))
-                       return true;
+       struct udphdr *udph = udp_hdr(skb);
 
-       return false;
+       udph->len = htons(skb->len - skb_transport_offset(skb));
 }
 
-static struct sk_buff *handle_offloads(struct sk_buff *skb)
+static void handle_offloads(struct sk_buff *skb)
 {
-       int err;
-
-       if (skb_is_gso(skb)) {
-               struct sk_buff *nskb;
-               char cb[sizeof(skb->cb)];
-
-               memcpy(cb, skb->cb, sizeof(cb));
-
-               nskb = __skb_gso_segment(skb, 0, false);
-               if (IS_ERR(nskb)) {
-                       err = PTR_ERR(nskb);
-                       goto error;
-               }
-
-               consume_skb(skb);
-               skb = nskb;
-               while (nskb) {
-                       memcpy(nskb->cb, cb, sizeof(cb));
-                       nskb = nskb->next;
-               }
-       } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               /* Pages aren't locked and could change at any time.
-                * If this happens after we compute the checksum, the
-                * checksum will be wrong.  We linearize now to avoid
-                * this problem.
-                */
-               if (unlikely(need_linearize(skb))) {
-                       err = __skb_linearize(skb);
-                       if (unlikely(err))
-                               goto error;
-               }
-
-               err = skb_checksum_help(skb);
-               if (unlikely(err))
-                       goto error;
-       }
-
-       skb->ip_summed = CHECKSUM_NONE;
-       return skb;
-
-error:
-       return ERR_PTR(err);
+       if (skb_is_gso(skb))
+               OVS_GSO_CB(skb)->fix_segment = lisp_fix_segment;
+       else if (skb->ip_summed != CHECKSUM_PARTIAL)
+               skb->ip_summed = CHECKSUM_NONE;
 }
 
-static int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
-                       u8 ipproto, int tunnel_hlen,
-                       void (*build_header)(const struct vport *,
-                                            struct sk_buff *,
-                                            int tunnel_hlen))
+static int lisp_send(struct vport *vport, struct sk_buff *skb)
 {
-       int min_headroom;
+       struct net *net = ovs_dp_get_net(vport->dp);
+       int network_offset = skb_network_offset(skb);
        struct rtable *rt;
+       int min_headroom;
        __be32 saddr;
-       int sent_len = 0;
+       __be16 df;
+       int sent_len;
        int err;
-       struct sk_buff *nskb;
+
+       if (unlikely(!OVS_CB(skb)->tun_key))
+               return -EINVAL;
+
+       if (skb->protocol != htons(ETH_P_IP) &&
+           skb->protocol != htons(ETH_P_IPV6)) {
+               kfree_skb(skb);
+               return 0;
+       }
 
        /* Route lookup */
        saddr = OVS_CB(skb)->tun_key->ipv4_src;
        rt = find_route(ovs_dp_get_net(vport->dp),
                        &saddr,
                        OVS_CB(skb)->tun_key->ipv4_dst,
-                       ipproto,
+                       IPPROTO_UDP,
                        OVS_CB(skb)->tun_key->ipv4_tos,
                        skb->mark);
        if (IS_ERR(rt)) {
@@ -467,11 +424,8 @@ static int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                goto error;
        }
 
-       tunnel_hlen += sizeof(struct iphdr);
-
        min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len
-                       + tunnel_hlen
-                       + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
+                       + sizeof(struct iphdr) + LISP_HLEN;
 
        if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
                int head_delta = SKB_DATA_ALIGN(min_headroom -
@@ -484,83 +438,27 @@ static int ovs_tnl_send(struct vport *vport, struct sk_buff *skb,
                        goto err_free_rt;
        }
 
+       skb_pull(skb, network_offset);
+       skb_reset_mac_header(skb);
+       skb_reset_inner_headers(skb);
+
+       __skb_push(skb, LISP_HLEN);
+       skb_reset_transport_header(skb);
+
+       lisp_build_header(vport, skb);
+
        /* Offloading */
-       nskb = handle_offloads(skb);
-       if (IS_ERR(nskb)) {
-               err = PTR_ERR(nskb);
-               goto err_free_rt;
-       }
-       skb = nskb;
-
-       /* Reset SKB */
-       nf_reset(skb);
-       secpath_reset(skb);
-       skb_dst_drop(skb);
-       skb_clear_rxhash(skb);
-
-       while (skb) {
-               struct sk_buff *next_skb = skb->next;
-               struct iphdr *iph;
-               int frag_len;
-
-               skb->next = NULL;
-
-               if (vlan_tx_tag_present(skb)) {
-                       if (unlikely(!__vlan_put_tag(skb,
-                                                       skb->vlan_proto,
-                                                       vlan_tx_tag_get(skb))))
-                               goto next;
-
-                       vlan_set_tci(skb, 0);
-               }
-
-               frag_len = skb->len;
-               skb_push(skb, tunnel_hlen);
-               skb_reset_network_header(skb);
-               skb_set_transport_header(skb, sizeof(struct iphdr));
-
-               if (next_skb)
-                       skb_dst_set(skb, dst_clone(&rt_dst(rt)));
-               else
-                       skb_dst_set(skb, &rt_dst(rt));
-
-               /* Push Tunnel header. */
-               build_header(vport, skb, tunnel_hlen);
-
-               /* Push IP header. */
-               iph = ip_hdr(skb);
-               iph->version    = 4;
-               iph->ihl        = sizeof(struct iphdr) >> 2;
-               iph->protocol   = ipproto;
-               iph->daddr      = OVS_CB(skb)->tun_key->ipv4_dst;
-               iph->saddr      = saddr;
-               iph->tos        = OVS_CB(skb)->tun_key->ipv4_tos;
-               iph->ttl        = OVS_CB(skb)->tun_key->ipv4_ttl;
-               iph->frag_off   = OVS_CB(skb)->tun_key->tun_flags &
+       handle_offloads(skb);
+       skb->local_df = 1;
+
+       df = OVS_CB(skb)->tun_key->tun_flags &
                                  TUNNEL_DONT_FRAGMENT ?  htons(IP_DF) : 0;
-               /*
-                * Allow our local IP stack to fragment the outer packet even
-                * if the DF bit is set as a last resort.  We also need to
-                * force selection of an IP ID here with __ip_select_ident(),
-                * as ip_select_ident() assumes a proper ID is not needed when
-                * when the DF bit is set.
-                */
-               skb->local_df = 1;
-               __ip_select_ident(iph, skb_dst(skb), 0);
-
-               memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
-
-               err = ip_local_out(skb);
-               if (unlikely(net_xmit_eval(err)))
-                       goto next;
-
-               sent_len += frag_len;
-
-next:
-               skb = next_skb;
-       }
+       sent_len = iptunnel_xmit(net, rt, skb,
+                            saddr, OVS_CB(skb)->tun_key->ipv4_dst,
+                            IPPROTO_UDP, OVS_CB(skb)->tun_key->ipv4_tos,
+                            OVS_CB(skb)->tun_key->ipv4_ttl, df);
 
-       return sent_len;
+       return sent_len > 0 ? sent_len + network_offset : sent_len;
 
 err_free_rt:
        ip_rt_put(rt);
@@ -568,29 +466,6 @@ error:
        return err;
 }
 
-static int lisp_tnl_send(struct vport *vport, struct sk_buff *skb)
-{
-       int tnl_len;
-       int network_offset = skb_network_offset(skb);
-
-       if (unlikely(!OVS_CB(skb)->tun_key))
-               return -EINVAL;
-
-       /* We only encapsulate IPv4 and IPv6 packets */
-       switch (skb->protocol) {
-       case htons(ETH_P_IP):
-       case htons(ETH_P_IPV6):
-               /* Pop off "inner" Ethernet header */
-               skb_pull(skb, network_offset);
-               tnl_len = ovs_tnl_send(vport, skb, IPPROTO_UDP,
-                               LISP_HLEN, lisp_build_header);
-               return tnl_len > 0 ? tnl_len + network_offset : tnl_len;
-       default:
-               kfree_skb(skb);
-               return 0;
-       }
-}
-
 static const char *lisp_get_name(const struct vport *vport)
 {
        struct lisp_port *lisp_port = lisp_vport(vport);
@@ -603,5 +478,5 @@ const struct vport_ops ovs_lisp_vport_ops = {
        .destroy        = lisp_tnl_destroy,
        .get_name       = lisp_get_name,
        .get_options    = lisp_get_options,
-       .send           = lisp_tnl_send,
+       .send           = lisp_send,
 };