datapath: Add support for kernel 3.14.
[sliver-openvswitch.git] / datapath / linux / compat / flow_dissector.c
index 8592ca9..ab60daa 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/if_vlan.h>
@@ -46,9 +46,25 @@ static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *i
        memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
 }
 
+static __be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto)
+{
+       int poff = proto_ports_offset(ip_proto);
+
+       if (poff >= 0) {
+               __be32 *ports, _ports;
+
+               ports = skb_header_pointer(skb, thoff + poff,
+                               sizeof(_ports), &_ports);
+               if (ports)
+                       return *ports;
+       }
+
+       return 0;
+}
+
 static bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow)
 {
-       int poff, nhoff = skb_network_offset(skb);
+       int nhoff = skb_network_offset(skb);
        u8 ip_proto;
        __be16 proto = skb->protocol;
 
@@ -86,6 +102,7 @@ ipv6:
                nhoff += sizeof(struct ipv6hdr);
                break;
        }
+       case __constant_htons(ETH_P_8021AD):
        case __constant_htons(ETH_P_8021Q): {
                const struct vlan_hdr *vlan;
                struct vlan_hdr _vlan;
@@ -161,36 +178,33 @@ ipv6:
        }
        case IPPROTO_IPIP:
                goto again;
+       case IPPROTO_IPV6:
+               proto = htons(ETH_P_IPV6);
+               goto ipv6;
        default:
                break;
        }
 
        flow->ip_proto = ip_proto;
-       poff = proto_ports_offset(ip_proto);
-       if (poff >= 0) {
-               __be32 *ports, _ports;
-
-               nhoff += poff;
-               ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports);
-               if (ports)
-                       flow->ports = *ports;
-       }
-
+       flow->ports = skb_flow_get_ports(skb, nhoff, ip_proto);
        flow->thoff = (u16) nhoff;
 
        return true;
 }
 
 static u32 hashrnd __read_mostly;
+static __always_inline void __flow_hash_secret_init(void)
+{
+       net_get_random_once(&hashrnd, sizeof(hashrnd));
+}
 
-static void init_hashrnd(void)
+static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c)
 {
-       if (likely(hashrnd))
-               return;
-       get_random_bytes(&hashrnd, sizeof(hashrnd));
+       __flow_hash_secret_init();
+       return jhash_3words(a, b, c, hashrnd);
 }
 
-u32 __skb_get_rxhash(struct sk_buff *skb)
+u32 __skb_get_hash(struct sk_buff *skb)
 {
        struct flow_keys keys;
        u32 hash;
@@ -206,15 +220,13 @@ u32 __skb_get_rxhash(struct sk_buff *skb)
                swap(keys.port16[0], keys.port16[1]);
        }
 
-       init_hashrnd();
-
-       hash = jhash_3words((__force u32)keys.dst,
-                           (__force u32)keys.src,
-                           (__force u32)keys.ports, hashrnd);
+       hash = __flow_hash_3words((__force u32)keys.dst,
+                                 (__force u32)keys.src,
+                                 (__force u32)keys.ports);
        if (!hash)
                hash = 1;
 
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
+#ifdef HAVE_RXHASH
        skb->rxhash = hash;
 #endif
        return hash;