From cb25142c50cd3a92e779ca1ce6f61380ffc75927 Mon Sep 17 00:00:00 2001
From: Pravin Shelar <pshelar@nicira.com>
Date: Tue, 1 Apr 2014 20:55:21 -0700
Subject: [PATCH] datapath: Add support for kernels 3.13

Add support for building the in-tree kernel datapath for
Linux kernels up to 3.13. There were some changes in the
netlink area which required adding new compatibility code
for this layer. Also, some new per-cpu stats initialization
code was added.

Based on patch from Kyle Mestery.

Signed-off-by: Kyle Mestery <mestery@noironetworks.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Acked-by: Kyle Mestery <mestery@noironetworks.com>
---
 FAQ                                           |   2 +-
 NEWS                                          |   4 +-
 acinclude.m4                                  |   4 +-
 datapath/compat.h                             |   6 +-
 datapath/datapath.c                           | 222 +++++++++---------
 datapath/datapath.h                           |   1 +
 datapath/dp_notify.c                          |  14 +-
 datapath/linux/compat/genetlink-openvswitch.c |  48 +++-
 .../compat/include/linux/u64_stats_sync.h     |  11 +
 datapath/linux/compat/include/net/genetlink.h |  80 ++++++-
 datapath/linux/compat/include/net/ip.h        |  10 +
 datapath/linux/compat/utils.c                 |   3 +
 datapath/vport-lisp.c                         |   7 +-
 datapath/vport-vxlan.c                        |   3 +-
 datapath/vport.c                              |   7 +
 15 files changed, 284 insertions(+), 138 deletions(-)

diff --git a/FAQ b/FAQ
index eec2d4f37..6b4be439d 100644
--- a/FAQ
+++ b/FAQ
@@ -149,7 +149,7 @@ A: The following table lists the Linux kernel versions against which the
        1.11.x     2.6.18 to 3.8
        2.0.x      2.6.32 to 3.10
        2.1.x      2.6.32 to 3.11
-       2.2.x      2.6.32 to 3.12
+       2.2.x      2.6.32 to 3.13
 
    Open vSwitch userspace should also work with the Linux kernel module
    built into Linux 3.3 and later.
diff --git a/NEWS b/NEWS
index 92557785d..79255980d 100644
--- a/NEWS
+++ b/NEWS
@@ -13,8 +13,8 @@ Post-v2.1.0
    - Upon the receipt of a SIGHUP signal, ovs-vswitchd no longer reopens its
      log file (it will terminate instead). Please use 'ovs-appctl vlog/reopen'
      instead.
-   - Support for Linux kernels up to 3.12. On Kernel 3.12 OVS uses tunnel
-     API for GRE and VXLAN.
+   - Support for Linux kernels up to 3.13. From Kernel 3.12 onwards OVS uses
+     tunnel API for GRE and VXLAN.
    - Added DPDK support.
 
 
diff --git a/acinclude.m4 b/acinclude.m4
index 1f52cf142..4269620a0 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -134,10 +134,10 @@ AC_DEFUN([OVS_CHECK_LINUX], [
     AC_MSG_RESULT([$kversion])
 
     if test "$version" -ge 3; then
-       if test "$version" = 3 && test "$patchlevel" -le 12; then
+       if test "$version" = 3 && test "$patchlevel" -le 13; then
           : # Linux 3.x
        else
-         AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version newer than 3.12.x is not supported])
+         AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version newer than 3.13.x is not supported])
        fi
     else
        if test "$version" -le 1 || test "$patchlevel" -le 5 || test "$sublevel" -le 31; then
diff --git a/datapath/compat.h b/datapath/compat.h
index bc7e88055..f8f04697c 100644
--- a/datapath/compat.h
+++ b/datapath/compat.h
@@ -32,10 +32,10 @@ static inline void skb_clear_rxhash(struct sk_buff *skb)
 #endif
 }
 
-#ifdef HAVE_PARALLEL_OPS
-#define SET_PARALLEL_OPS	.parallel_ops = true,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
+#define GROUP_ID(grp)	0
 #else
-#define SET_PARALLEL_OPS
+#define GROUP_ID(grp)	((grp)->id)
 #endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 52c76b413..c6d42db38 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -64,20 +64,36 @@
 
 int ovs_net_id __read_mostly;
 
+static struct genl_family dp_packet_genl_family;
+static struct genl_family dp_flow_genl_family;
+static struct genl_family dp_datapath_genl_family;
+
+static struct genl_multicast_group ovs_dp_flow_multicast_group = {
+	.name = OVS_FLOW_MCGROUP
+};
+
+static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
+	.name = OVS_DATAPATH_MCGROUP
+};
+
+struct genl_multicast_group ovs_dp_vport_multicast_group = {
+	.name = OVS_VPORT_MCGROUP
+};
+
 /* Check if need to build a reply message.
  * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */
 static bool ovs_must_notify(struct genl_info *info,
 			    const struct genl_multicast_group *grp)
 {
 	return info->nlhdr->nlmsg_flags & NLM_F_ECHO ||
-		netlink_has_listeners(genl_info_net(info)->genl_sock, grp->id);
+		netlink_has_listeners(genl_info_net(info)->genl_sock, GROUP_ID(grp));
 }
 
-static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
-		       struct genl_multicast_group *grp)
+static void ovs_notify(struct genl_family *family, struct genl_multicast_group *grp,
+		       struct sk_buff *skb, struct genl_info *info)
 {
-	genl_notify(skb, genl_info_net(info), info->snd_portid,
-		    grp->id, info->nlhdr, GFP_KERNEL);
+	genl_notify(family, skb, genl_info_net(info),
+		    info->snd_portid, GROUP_ID(grp), info->nlhdr, GFP_KERNEL);
 }
 
 /**
@@ -274,16 +290,6 @@ out:
 	u64_stats_update_end(&stats->sync);
 }
 
-static struct genl_family dp_packet_genl_family = {
-	.id = GENL_ID_GENERATE,
-	.hdrsize = sizeof(struct ovs_header),
-	.name = OVS_PACKET_FAMILY,
-	.version = OVS_PACKET_VERSION,
-	.maxattr = OVS_PACKET_ATTR_MAX,
-	.netnsok = true,
-	 SET_PARALLEL_OPS
-};
-
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
 		  const struct dp_upcall_info *upcall_info)
 {
@@ -601,6 +607,18 @@ static struct genl_ops dp_packet_genl_ops[] = {
 	}
 };
 
+static struct genl_family dp_packet_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = sizeof(struct ovs_header),
+	.name = OVS_PACKET_FAMILY,
+	.version = OVS_PACKET_VERSION,
+	.maxattr = OVS_PACKET_ATTR_MAX,
+	.netnsok = true,
+	.parallel_ops = true,
+	.ops = dp_packet_genl_ops,
+	.n_ops = ARRAY_SIZE(dp_packet_genl_ops),
+};
+
 static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
 			 struct ovs_dp_megaflow_stats *mega_stats)
 {
@@ -632,26 +650,6 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
 	}
 }
 
-static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
-	[OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
-	[OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
-	[OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
-};
-
-static struct genl_family dp_flow_genl_family = {
-	.id = GENL_ID_GENERATE,
-	.hdrsize = sizeof(struct ovs_header),
-	.name = OVS_FLOW_FAMILY,
-	.version = OVS_FLOW_VERSION,
-	.maxattr = OVS_FLOW_ATTR_MAX,
-	.netnsok = true,
-	 SET_PARALLEL_OPS
-};
-
-static struct genl_multicast_group ovs_dp_flow_multicast_group = {
-	.name = OVS_FLOW_MCGROUP
-};
-
 static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 {
 	return NLMSG_ALIGN(sizeof(struct ovs_header))
@@ -914,7 +912,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 	}
 
 	if (reply)
-		ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
+		ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info);
 	return 0;
 
 err_unlock_ovs:
@@ -1023,7 +1021,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
 	ovs_unlock();
 
 	if (reply)
-		ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
+		ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info);
 	if (old_acts)
 		ovs_nla_free_flow_actions(old_acts);
 	return 0;
@@ -1135,11 +1133,11 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 						     OVS_FLOW_CMD_DEL);
 			rcu_read_unlock();
 			BUG_ON(err < 0);
-			ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
+			ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info);
 		} else {
-			netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
-					ovs_dp_flow_multicast_group.id,
-					PTR_ERR(reply));
+			genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0,
+				     GROUP_ID(&ovs_dp_flow_multicast_group), PTR_ERR(reply));
+
 		}
 	}
 
@@ -1187,6 +1185,12 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 	return skb->len;
 }
 
+static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
+	[OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
+	[OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
+	[OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
+};
+
 static struct genl_ops dp_flow_genl_ops[] = {
 	{ .cmd = OVS_FLOW_CMD_NEW,
 	  .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
@@ -1211,24 +1215,18 @@ static struct genl_ops dp_flow_genl_ops[] = {
 	},
 };
 
-static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
-	[OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-	[OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
-	[OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
-};
-
-static struct genl_family dp_datapath_genl_family = {
+static struct genl_family dp_flow_genl_family = {
 	.id = GENL_ID_GENERATE,
 	.hdrsize = sizeof(struct ovs_header),
-	.name = OVS_DATAPATH_FAMILY,
-	.version = OVS_DATAPATH_VERSION,
-	.maxattr = OVS_DP_ATTR_MAX,
+	.name = OVS_FLOW_FAMILY,
+	.version = OVS_FLOW_VERSION,
+	.maxattr = OVS_FLOW_ATTR_MAX,
 	.netnsok = true,
-	 SET_PARALLEL_OPS
-};
-
-static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
-	.name = OVS_DATAPATH_MCGROUP
+	.parallel_ops = true,
+	.ops = dp_flow_genl_ops,
+	.n_ops = ARRAY_SIZE(dp_flow_genl_ops),
+	.mcgrps = &ovs_dp_flow_multicast_group,
+	.n_mcgrps = 1,
 };
 
 static size_t ovs_dp_cmd_msg_size(void)
@@ -1361,6 +1359,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 		goto err_destroy_table;
 	}
 
+	for_each_possible_cpu(i) {
+		struct dp_stats_percpu *dpath_stats;
+		dpath_stats = per_cpu_ptr(dp->stats_percpu, i);
+		u64_stats_init(&dpath_stats->sync);
+	}
+
 	dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
 			    GFP_KERNEL);
 	if (!dp->ports) {
@@ -1411,7 +1415,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
 	ovs_unlock();
 
-	ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
+	ovs_notify(&dp_datapath_genl_family, &ovs_dp_datapath_multicast_group, reply, info);
 	return 0;
 
 err_destroy_ports_array:
@@ -1480,7 +1484,7 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	__dp_destroy(dp);
 
 	ovs_unlock();
-	ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
+	ovs_notify(&dp_datapath_genl_family, &ovs_dp_datapath_multicast_group, reply, info);
 	return 0;
 
 err_unlock_free:
@@ -1512,7 +1516,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
 	BUG_ON(err < 0);
 
 	ovs_unlock();
-	ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
+	ovs_notify(&dp_datapath_genl_family, &ovs_dp_datapath_multicast_group, reply, info);
 	return 0;
 
 err_unlock_free:
@@ -1573,6 +1577,12 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 	return skb->len;
 }
 
+static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
+	[OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+	[OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+	[OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
+};
+
 static struct genl_ops dp_datapath_genl_ops[] = {
 	{ .cmd = OVS_DP_CMD_NEW,
 	  .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
@@ -1597,27 +1607,18 @@ static struct genl_ops dp_datapath_genl_ops[] = {
 	},
 };
 
-static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
-	[OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-	[OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
-	[OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
-	[OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
-	[OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
-	[OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
-};
-
-static struct genl_family dp_vport_genl_family = {
+static struct genl_family dp_datapath_genl_family = {
 	.id = GENL_ID_GENERATE,
 	.hdrsize = sizeof(struct ovs_header),
-	.name = OVS_VPORT_FAMILY,
-	.version = OVS_VPORT_VERSION,
-	.maxattr = OVS_VPORT_ATTR_MAX,
+	.name = OVS_DATAPATH_FAMILY,
+	.version = OVS_DATAPATH_VERSION,
+	.maxattr = OVS_DP_ATTR_MAX,
 	.netnsok = true,
-	 SET_PARALLEL_OPS
-};
-
-struct genl_multicast_group ovs_dp_vport_multicast_group = {
-	.name = OVS_VPORT_MCGROUP
+	.parallel_ops = true,
+	.ops = dp_datapath_genl_ops,
+	.n_ops = ARRAY_SIZE(dp_datapath_genl_ops),
+	.mcgrps = &ovs_dp_datapath_multicast_group,
+	.n_mcgrps = 1,
 };
 
 /* Called with ovs_mutex or RCU read lock. */
@@ -1783,7 +1784,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 	BUG_ON(err < 0);
 	ovs_unlock();
 
-	ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+	ovs_notify(&dp_vport_genl_family, &ovs_dp_vport_multicast_group, reply, info);
 	return 0;
 
 exit_unlock_free:
@@ -1832,7 +1833,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 	BUG_ON(err < 0);
 	ovs_unlock();
 
-	ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+	ovs_notify(&dp_vport_genl_family, &ovs_dp_vport_multicast_group, reply, info);
 	return 0;
 
 exit_unlock_free:
@@ -1869,7 +1870,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	ovs_dp_detach_port(vport);
 	ovs_unlock();
 
-	ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+	ovs_notify(&dp_vport_genl_family, &ovs_dp_vport_multicast_group, reply, info);
 	return 0;
 
 exit_unlock_free:
@@ -1947,6 +1948,15 @@ out:
 	return skb->len;
 }
 
+static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
+	[OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+	[OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
+	[OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
+	[OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
+	[OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+	[OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
+};
+
 static struct genl_ops dp_vport_genl_ops[] = {
 	{ .cmd = OVS_VPORT_CMD_NEW,
 	  .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
@@ -1971,26 +1981,25 @@ static struct genl_ops dp_vport_genl_ops[] = {
 	},
 };
 
-struct genl_family_and_ops {
-	struct genl_family *family;
-	struct genl_ops *ops;
-	int n_ops;
-	struct genl_multicast_group *group;
+struct genl_family dp_vport_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = sizeof(struct ovs_header),
+	.name = OVS_VPORT_FAMILY,
+	.version = OVS_VPORT_VERSION,
+	.maxattr = OVS_VPORT_ATTR_MAX,
+	.netnsok = true,
+	.parallel_ops = true,
+	.ops = dp_vport_genl_ops,
+	.n_ops = ARRAY_SIZE(dp_vport_genl_ops),
+	.mcgrps = &ovs_dp_vport_multicast_group,
+	.n_mcgrps = 1,
 };
 
-static const struct genl_family_and_ops dp_genl_families[] = {
-	{ &dp_datapath_genl_family,
-	  dp_datapath_genl_ops, ARRAY_SIZE(dp_datapath_genl_ops),
-	  &ovs_dp_datapath_multicast_group },
-	{ &dp_vport_genl_family,
-	  dp_vport_genl_ops, ARRAY_SIZE(dp_vport_genl_ops),
-	  &ovs_dp_vport_multicast_group },
-	{ &dp_flow_genl_family,
-	  dp_flow_genl_ops, ARRAY_SIZE(dp_flow_genl_ops),
-	  &ovs_dp_flow_multicast_group },
-	{ &dp_packet_genl_family,
-	  dp_packet_genl_ops, ARRAY_SIZE(dp_packet_genl_ops),
-	  NULL },
+static struct genl_family *dp_genl_families[] = {
+	&dp_datapath_genl_family,
+	&dp_vport_genl_family,
+	&dp_flow_genl_family,
+	&dp_packet_genl_family,
 };
 
 static void dp_unregister_genl(int n_families)
@@ -1998,36 +2007,25 @@ static void dp_unregister_genl(int n_families)
 	int i;
 
 	for (i = 0; i < n_families; i++)
-		genl_unregister_family(dp_genl_families[i].family);
+		genl_unregister_family(dp_genl_families[i]);
 }
 
 static int dp_register_genl(void)
 {
-	int n_registered;
 	int err;
 	int i;
 
-	n_registered = 0;
 	for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) {
-		const struct genl_family_and_ops *f = &dp_genl_families[i];
 
-		err = genl_register_family_with_ops(f->family, f->ops,
-						    f->n_ops);
+		err = genl_register_family(dp_genl_families[i]);
 		if (err)
 			goto error;
-		n_registered++;
-
-		if (f->group) {
-			err = genl_register_mc_group(f->family, f->group);
-			if (err)
-				goto error;
-		}
 	}
 
 	return 0;
 
 error:
-	dp_unregister_genl(n_registered);
+	dp_unregister_genl(i);
 	return err;
 }
 
diff --git a/datapath/datapath.h b/datapath/datapath.h
index d81e05c59..40e0f90b8 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -184,6 +184,7 @@ static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_n
 }
 
 extern struct notifier_block ovs_dp_device_notifier;
+extern struct genl_family dp_vport_genl_family;
 extern struct genl_multicast_group ovs_dp_vport_multicast_group;
 
 void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
diff --git a/datapath/dp_notify.c b/datapath/dp_notify.c
index 0b22d0c19..f9a037510 100644
--- a/datapath/dp_notify.c
+++ b/datapath/dp_notify.c
@@ -31,18 +31,18 @@ static void dp_detach_port_notify(struct vport *vport)
 	struct datapath *dp;
 
 	dp = vport->dp;
-	notify = ovs_vport_cmd_build_info(vport, 0, 0,
-					  OVS_VPORT_CMD_DEL);
+	notify = ovs_vport_cmd_build_info(vport, 0, 0, OVS_VPORT_CMD_DEL);
 	ovs_dp_detach_port(vport);
 	if (IS_ERR(notify)) {
-		netlink_set_err(ovs_dp_get_net(dp)->genl_sock, 0,
-				ovs_dp_vport_multicast_group.id,
-				PTR_ERR(notify));
+		genl_set_err(&dp_vport_genl_family, ovs_dp_get_net(dp), 0,
+			     GROUP_ID(&ovs_dp_vport_multicast_group),
+			     PTR_ERR(notify));
 		return;
 	}
 
-	genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
-				ovs_dp_vport_multicast_group.id,
+	genlmsg_multicast_netns(&dp_vport_genl_family,
+				ovs_dp_get_net(dp), notify, 0,
+				GROUP_ID(&ovs_dp_vport_multicast_group),
 				GFP_KERNEL);
 }
 
diff --git a/datapath/linux/compat/genetlink-openvswitch.c b/datapath/linux/compat/genetlink-openvswitch.c
index 359f916dc..08f0fab8b 100644
--- a/datapath/linux/compat/genetlink-openvswitch.c
+++ b/datapath/linux/compat/genetlink-openvswitch.c
@@ -1,12 +1,15 @@
 #include <net/genetlink.h>
 #include <linux/version.h>
 
-/* This is analogous to rtnl_notify() but uses genl_sock instead of rtnl.
- *
- * This is not (yet) in any upstream kernel. */
-void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group,
-		 struct nlmsghdr *nlh, gfp_t flags)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+
+#undef genl_notify
+
+void rpl_genl_notify(struct rpl_genl_family *family, struct sk_buff *skb,
+		     struct net *net, u32 portid, u32 group,
+		     struct nlmsghdr *nlh, gfp_t flags)
 {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
 	struct sock *sk = net->genl_sock;
 	int report = 0;
 
@@ -14,4 +17,39 @@ void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, u32 group,
 		report = nlmsg_report(nlh);
 
 	nlmsg_notify(sk, skb, portid, group, report, flags);
+#else
+	genl_notify(skb, net, portid, group, nlh, flags);
+#endif
+}
+
+int rpl___genl_register_family(struct rpl_genl_family *f)
+{
+	int err;
+
+	f->compat_family.id = f->id;
+	f->compat_family.hdrsize = f->hdrsize;
+	strncpy(f->compat_family.name, f->name, GENL_NAMSIZ);
+	f->compat_family.version = f->version;
+	f->compat_family.maxattr = f->maxattr;
+	f->compat_family.netnsok = f->netnsok;
+#ifdef HAVE_PARALLEL_OPS
+	f->compat_family.parallel_ops = f->parallel_ops;
+#endif
+	err = genl_register_family_with_ops(&f->compat_family,
+					    (struct genl_ops *) f->ops, f->n_ops);
+	if (err)
+		goto error;
+
+	if (f->mcgrps) {
+		/* Need to Fix GROUP_ID() for more than one group. */
+		BUG_ON(f->n_mcgrps > 1);
+		err = genl_register_mc_group(&f->compat_family,
+					     (struct genl_multicast_group *) f->mcgrps);
+		if (err)
+			goto error;
+	}
+error:
+	return err;
+
 }
+#endif /* kernel version < 3.13.0 */
diff --git a/datapath/linux/compat/include/linux/u64_stats_sync.h b/datapath/linux/compat/include/linux/u64_stats_sync.h
index 45b617a29..234fd9156 100644
--- a/datapath/linux/compat/include/linux/u64_stats_sync.h
+++ b/datapath/linux/compat/include/linux/u64_stats_sync.h
@@ -144,4 +144,15 @@ static inline bool u64_stats_fetch_retry_bh(const struct u64_stats_sync *syncp,
 }
 
 #endif /* Linux kernel < 2.6.36 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+
+#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
+# define u64_stats_init(syncp)  seqcount_init(syncp.seq)
+#else
+# define u64_stats_init(syncp)  do { } while (0)
+#endif
+
+#endif
+
 #endif /* _LINUX_U64_STATS_SYNC_WRAPPER_H */
diff --git a/datapath/linux/compat/include/net/genetlink.h b/datapath/linux/compat/include/net/genetlink.h
index 09ee23bdd..9f425e5ff 100644
--- a/datapath/linux/compat/include/net/genetlink.h
+++ b/datapath/linux/compat/include/net/genetlink.h
@@ -17,8 +17,84 @@
 #define portid pid
 #endif
 
-extern void genl_notify(struct sk_buff *skb, struct net *net, u32 portid,
-			u32 group, struct nlmsghdr *nlh, gfp_t flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+struct rpl_genl_family {
+	struct genl_family	compat_family;
+	unsigned int            id;
+	unsigned int            hdrsize;
+	char                    name[GENL_NAMSIZ];
+	unsigned int            version;
+	unsigned int            maxattr;
+	bool                    netnsok;
+	bool                    parallel_ops;
+	int                     (*pre_doit)(const struct genl_ops *ops,
+					    struct sk_buff *skb,
+					    struct genl_info *info);
+	void                    (*post_doit)(const struct genl_ops *ops,
+					     struct sk_buff *skb,
+					     struct genl_info *info);
+	struct nlattr **        attrbuf;        /* private */
+	const struct genl_ops * ops;            /* private */
+	const struct genl_multicast_group *mcgrps; /* private */
+	unsigned int            n_ops;          /* private */
+	unsigned int            n_mcgrps;       /* private */
+	unsigned int            mcgrp_offset;   /* private */
+	struct list_head        family_list;    /* private */
+	struct module           *module;
+};
+
+#define genl_family rpl_genl_family
+#define genl_notify rpl_genl_notify
+void genl_notify(struct genl_family *family,
+		 struct sk_buff *skb, struct net *net, u32 portid, u32 group,
+		 struct nlmsghdr *nlh, gfp_t flags);
+
+static inline void *rpl_genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
+				    struct genl_family *family, int flags, u8 cmd)
+{
+	return genlmsg_put(skb, portid, seq, &family->compat_family, flags, cmd);
+}
+
+#define genlmsg_put rpl_genlmsg_put
+
+static inline int rpl_genl_unregister_family(struct genl_family *family)
+{
+	return genl_unregister_family(&family->compat_family);
+}
+#define genl_unregister_family rpl_genl_unregister_family
+
+#define genl_set_err rpl_genl_set_err
+static inline int genl_set_err(struct genl_family *family, struct net *net,
+			       u32 portid, u32 group, int code)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+	netlink_set_err(net->genl_sock, portid, group, code);
+	return 0;
+#else
+	return netlink_set_err(net->genl_sock, portid, group, code);
+#endif
+}
+
+#define genlmsg_multicast_netns rpl_genlmsg_multicast_netns
+static inline int genlmsg_multicast_netns(struct genl_family *family,
+					  struct net *net, struct sk_buff *skb,
+					  u32 portid, unsigned int group, gfp_t flags)
+{
+	return nlmsg_multicast(net->genl_sock, skb, portid, group, flags);
+}
+
+
+#define __genl_register_family rpl___genl_register_family
+int rpl___genl_register_family(struct genl_family *family);
+
+#define genl_register_family rpl_genl_register_family
+static inline int rpl_genl_register_family(struct genl_family *family)
+{
+	family->module = THIS_MODULE;
+	return rpl___genl_register_family(family);
+}
+
+#endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
 static inline struct sk_buff *genlmsg_new_unicast(size_t payload,
diff --git a/datapath/linux/compat/include/net/ip.h b/datapath/linux/compat/include/net/ip.h
index 4193d3290..c819e4d99 100644
--- a/datapath/linux/compat/include/net/ip.h
+++ b/datapath/linux/compat/include/net/ip.h
@@ -12,4 +12,14 @@ static inline bool ip_is_fragment(const struct iphdr *iph)
 }
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+static inline void rpl_inet_get_local_port_range(struct net *net, int *low,
+					     int *high)
+{
+	inet_get_local_port_range(low, high);
+}
+#define inet_get_local_port_range rpl_inet_get_local_port_range
+
+#endif
+
 #endif
diff --git a/datapath/linux/compat/utils.c b/datapath/linux/compat/utils.c
index dc4df2aaa..9404e20f4 100644
--- a/datapath/linux/compat/utils.c
+++ b/datapath/linux/compat/utils.c
@@ -6,6 +6,7 @@
 #include <linux/mm.h>
 #include <linux/net.h>
 #include <net/checksum.h>
+#include <net/ip.h>
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/percpu.h>
@@ -38,6 +39,7 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb,
 }
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
 bool __net_get_random_once(void *buf, int nbytes, bool *done,
 			   atomic_t *done_key)
 {
@@ -58,3 +60,4 @@ bool __net_get_random_once(void *buf, int nbytes, bool *done,
 
 	return true;
 }
+#endif
diff --git a/datapath/vport-lisp.c b/datapath/vport-lisp.c
index e33cffea2..8e3ff6928 100644
--- a/datapath/vport-lisp.c
+++ b/datapath/vport-lisp.c
@@ -163,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 get_src_port(struct sk_buff *skb)
+static u16 get_src_port(struct net *net, struct sk_buff *skb)
 {
 	u32 hash = skb_get_rxhash(skb);
 	unsigned int range;
@@ -177,7 +177,7 @@ static u16 get_src_port(struct sk_buff *skb)
 			    sizeof(*pkt_key) / sizeof(u32), 0);
 	}
 
-	inet_get_local_port_range(&low, &high);
+	inet_get_local_port_range(net, &low, &high);
 	range = (high - low) + 1;
 	return (((u64) hash * range) >> 32) + low;
 }
@@ -185,13 +185,14 @@ static u16 get_src_port(struct sk_buff *skb)
 static void lisp_build_header(const struct vport *vport,
 			      struct sk_buff *skb)
 {
+	struct net *net = ovs_dp_get_net(vport->dp);
 	struct lisp_port *lisp_port = lisp_vport(vport);
 	struct udphdr *udph = udp_hdr(skb);
 	struct lisphdr *lisph = (struct lisphdr *)(udph + 1);
 	const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
 
 	udph->dest = lisp_port->dst_port;
-	udph->source = htons(get_src_port(skb));
+	udph->source = htons(get_src_port(net, skb));
 	udph->check = 0;
 	udph->len = htons(skb->len - skb_transport_offset(skb));
 
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index d26478535..cc9477dd1 100644
--- a/datapath/vport-vxlan.c
+++ b/datapath/vport-vxlan.c
@@ -139,6 +139,7 @@ error:
 
 static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
 {
+	struct net *net = ovs_dp_get_net(vport->dp);
 	struct vxlan_port *vxlan_port = vxlan_vport(vport);
 	__be16 dst_port = inet_sport(vxlan_port->vs->sock->sk);
 	struct rtable *rt;
@@ -172,7 +173,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
 
 	skb->local_df = 1;
 
-	inet_get_local_port_range(&port_min, &port_max);
+	inet_get_local_port_range(net, &port_min, &port_max);
 	src_port = vxlan_src_port(port_min, port_max, skb);
 
 	err = vxlan_xmit_skb(vxlan_port->vs, rt, skb,
diff --git a/datapath/vport.c b/datapath/vport.c
index 7f12acccb..2673b8178 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -122,6 +122,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 {
 	struct vport *vport;
 	size_t alloc_size;
+	int i;
 
 	alloc_size = sizeof(struct vport);
 	if (priv_size) {
@@ -145,6 +146,12 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 		return ERR_PTR(-ENOMEM);
 	}
 
+	for_each_possible_cpu(i) {
+		struct pcpu_tstats *vport_stats;
+		vport_stats = per_cpu_ptr(vport->percpu_stats, i);
+		u64_stats_init(&vport_stats->syncp);
+	}
+
 	spin_lock_init(&vport->stats_lock);
 
 	return vport;
-- 
2.47.0