[ovs-dev] [PATCH] datapath: Add support for kernel 3.13

Kyle Mestery mestery at noironetworks.com
Thu Mar 27 16:44:47 UTC 2014


Add support for building the in-tree kernel datapath for Linux kernel 3.13.
There were some changes in the netlink area which required adding new
compatibility code for this layer.

Signed-off-by: Kyle Mestery <mestery at noironetworks.com>
---
v2: Address a few comments from Pravin. Some of those comments
    proved challenging, please see email reply to the list.
---
 FAQ                                           |  2 +-
 acinclude.m4                                  |  4 +--
 datapath/datapath.c                           | 27 ++++++++++++++-----
 datapath/datapath.h                           |  1 +
 datapath/dp_notify.c                          | 11 ++++----
 datapath/linux/compat/genetlink-openvswitch.c | 20 ++++++++++----
 datapath/linux/compat/include/net/genetlink.h | 39 +++++++++++++++++++++++++--
 datapath/linux/compat/include/net/ip.h        | 12 +++++++++
 datapath/linux/compat/utils.c                 |  3 +++
 datapath/vport-lisp.c                         |  7 ++---
 datapath/vport-vxlan.c                        |  3 ++-
 11 files changed, 102 insertions(+), 27 deletions(-)

diff --git a/FAQ b/FAQ
index dae5c5b..5ca6d22 100644
--- a/FAQ
+++ b/FAQ
@@ -148,7 +148,7 @@ A: The following table lists the Linux kernel versions against which the
        1.10.x     2.6.18 to 3.8
        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.12
+       2.1.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/acinclude.m4 b/acinclude.m4
index 1f52cf1..4269620 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/datapath.c b/datapath/datapath.c
index 93f8e36..66e4766 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -64,11 +64,13 @@
 
 int ovs_net_id __read_mostly;
 
+static struct genl_family dp_packet_genl_family;
+
 static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
 		       struct genl_multicast_group *grp)
 {
-	genl_notify(skb, genl_info_net(info), info->snd_portid,
-		    grp->id, info->nlhdr, GFP_KERNEL);
+	genl_notify(&dp_packet_genl_family, skb, genl_info_net(info),
+		    info->snd_portid, 0, info->nlhdr, GFP_KERNEL);
 }
 
 /**
@@ -888,8 +890,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	if (!IS_ERR(reply))
 		ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
 	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,
+			     0, PTR_ERR(reply));
 	return 0;
 
 err_flow_free:
@@ -1368,8 +1370,8 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
 	reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
 	if (IS_ERR(reply)) {
 		err = PTR_ERR(reply);
-		netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
-				ovs_dp_datapath_multicast_group.id, err);
+		genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0,
+			     0, err);
 		err = 0;
 		goto unlock;
 	}
@@ -1466,7 +1468,7 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
 	[OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
 };
 
-static struct genl_family dp_vport_genl_family = {
+struct genl_family dp_vport_genl_family = {
 	.id = GENL_ID_GENERATE,
 	.hdrsize = sizeof(struct ovs_header),
 	.name = OVS_VPORT_FAMILY,
@@ -1865,6 +1867,7 @@ static int dp_register_genl(void)
 	for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) {
 		const struct genl_family_and_ops *f = &dp_genl_families[i];
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
 		err = genl_register_family_with_ops(f->family, f->ops,
 						    f->n_ops);
 		if (err)
@@ -1876,6 +1879,16 @@ static int dp_register_genl(void)
 			if (err)
 				goto error;
 		}
+#else
+		f->family->ops = f->ops;
+		f->family->n_ops = f->n_ops;
+		f->family->mcgrps = f->group;
+		f->family->n_mcgrps = f->group ? 1 : 0;
+		err = genl_register_family(f->family);
+		if (err)
+			goto error;
+		n_registered++;
+#endif
 	}
 
 	return 0;
diff --git a/datapath/datapath.h b/datapath/datapath.h
index d81e05c..40e0f90 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 0b22d0c..e96b242 100644
--- a/datapath/dp_notify.c
+++ b/datapath/dp_notify.c
@@ -35,15 +35,14 @@ static void dp_detach_port_notify(struct vport *vport)
 					  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,
+			     0, PTR_ERR(notify));
 		return;
 	}
 
-	genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
-				ovs_dp_vport_multicast_group.id,
-				GFP_KERNEL);
+	genlmsg_multicast_netns(&dp_vport_genl_family,
+				ovs_dp_get_net(dp), notify, 0,
+				0, GFP_KERNEL);
 }
 
 void ovs_dp_notify_wq(struct work_struct *work)
diff --git a/datapath/linux/compat/genetlink-openvswitch.c b/datapath/linux/compat/genetlink-openvswitch.c
index 359f916..b987aff 100644
--- a/datapath/linux/compat/genetlink-openvswitch.c
+++ b/datapath/linux/compat/genetlink-openvswitch.c
@@ -1,17 +1,27 @@
 #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,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+void genl_notify(struct genl_family *family,
+		 struct sk_buff *skb, struct net *net, u32 portid, u32 group,
 		 struct nlmsghdr *nlh, gfp_t flags)
 {
 	struct sock *sk = net->genl_sock;
 	int report = 0;
+	struct genl_multicast_group *grp;
+	int i = 0;
 
 	if (nlh)
 		report = nlmsg_report(nlh);
 
-	nlmsg_notify(sk, skb, portid, group, report, flags);
+	list_for_each_entry(grp, &family->mcast_groups, list) {
+		if (group == 0)
+			break;
+		i++;
+	}
+
+	if (WARN_ON_ONCE(grp == NULL))
+		return;
+	nlmsg_notify(sk, skb, portid, grp->id, report, flags);
 }
+#endif /* kernel version < 3.13.0 */
diff --git a/datapath/linux/compat/include/net/genetlink.h b/datapath/linux/compat/include/net/genetlink.h
index 09ee23b..2db49bf 100644
--- a/datapath/linux/compat/include/net/genetlink.h
+++ b/datapath/linux/compat/include/net/genetlink.h
@@ -17,8 +17,43 @@
 #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)
+#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);
+
+#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)
+{
+	struct genl_multicast_group *grp;
+
+	list_for_each_entry(grp, &family->mcast_groups, list) {
+		if (group == grp->id)
+			break;
+	}
+
+	return netlink_set_err(net->genl_sock, portid, grp->id, code);
+}
+
+#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)
+{
+	struct genl_multicast_group *grp;
+
+	list_for_each_entry(grp, &family->mcast_groups, list) {
+		if (group == grp->id)
+			break;
+	}
+
+	if (WARN_ON_ONCE(grp == NULL))
+		return -EINVAL;
+	return nlmsg_multicast(net->genl_sock, skb, portid, grp->id, flags);
+}
+#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 4193d32..4fd0b2c 100644
--- a/datapath/linux/compat/include/net/ip.h
+++ b/datapath/linux/compat/include/net/ip.h
@@ -12,4 +12,16 @@ static inline bool ip_is_fragment(const struct iphdr *iph)
 }
 #endif
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
+#define inet_get_local_port_range_ovs(a, b, c) \
+	inet_get_local_port_range(a, b, c)
+#else
+static inline void inet_get_local_port_range_ovs(struct net *net, int *low,
+						 int *high)
+{
+	(void)(net);
+	inet_get_local_port_range(low, high);
+}
+#endif
+
 #endif
diff --git a/datapath/linux/compat/utils.c b/datapath/linux/compat/utils.c
index dc4df2a..9404e20 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 e33cffe..a1e4ca2 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_ovs(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 d264785..4b36253 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_ovs(net, &port_min, &port_max);
 	src_port = vxlan_src_port(port_min, port_max, skb);
 
 	err = vxlan_xmit_skb(vxlan_port->vs, rt, skb,
-- 
1.8.5.3




More information about the dev mailing list