[PATCH net-next] openvswitch: report features supported by the kernel datapath

Jiri Benc
2015-10-08T13:53:29Z

Allow the user space to query what features are supported by the openvswitch
module. This will be used to allow or disallow certain configurations and/or
switch between newer and older APIs depending on what the kernel supports.

Two features are reported as supported by this patch: lwtunnel and IPv6
tunneling support. Theoretically, we could merge these two, as any of them
implies the other with this patch applied, but it's better to keep them
separate: kernel 4.3 supports lwtunnels but not IPv6 for ovs, and the
separation of the two flags allows us to backport a version of this patch
to 4.3 should the need arise.

Signed-off-by: Jiri Benc <jbenc@redhat.com>
---
This is intentionally submitted to net-next. Kernel 4.3 will be fine without
this feature (but despite that, I'd like to keep the two flags separate).
---
include/uapi/linux/openvswitch.h | 12 ++++++++++++
net/openvswitch/datapath.c | 21 ++++++++++++++++++---
2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 32e07d8cbaf4..3d8d00b6e55d 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -73,6 +73,10 @@ enum ovs_datapath_cmd {
* datapath. Always present in notifications.
* @OVS_DP_ATTR_MEGAFLOW_STATS: Statistics about mega flow masks usage for the
* datapath. Always present in notifications.
+ * @OVS_DP_ATTR_KERNEL_FEATURES: Bitmask of features that the datapath
+ * supports. When creating a datapath, this is a minimal feature set
+ * requested, the request will fail if it cannot be honored. On get
+ * operations, this contains all features supported by the datapath.
*
* These attributes follow the &struct ovs_header within the Generic Netlink
* payload for %OVS_DP_* commands.
@@ -84,6 +88,7 @@ enum ovs_datapath_attr {
OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */
OVS_DP_ATTR_MEGAFLOW_STATS, /* struct ovs_dp_megaflow_stats */
OVS_DP_ATTR_USER_FEATURES, /* OVS_DP_F_* */
+ OVS_DP_ATTR_KERNEL_FEATURES, /* OVS_DP_KF_* */
__OVS_DP_ATTR_MAX
};

@@ -121,6 +126,13 @@ struct ovs_vport_stats {
/* Allow datapath to associate multiple Netlink PIDs to each vport */
#define OVS_DP_F_VPORT_PIDS (1 << 1)

+/* Datapath features */
+#define OVS_DP_KF_LWTUNNEL (1ULL << 0) /* lwtunnel interface supported */
+#define OVS_DP_KF_IPV6_TUNNEL (1ULL << 1) /* IPv6 tunneling supported */
+
+#define OVS_DP_KF_ALL (OVS_DP_KF_LWTUNNEL | \
+ OVS_DP_KF_IPV6_TUNNEL)
+
/* Fixed logical ports. */
#define OVSP_LOCAL ((__u32)0)

diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index a75828091e21..896435779fc8 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1439,6 +1439,7 @@ static size_t ovs_dp_cmd_msg_size(void)
msgsize += nla_total_size(sizeof(struct ovs_dp_stats));
msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats));
msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */
+ msgsize += nla_total_size(sizeof(u64)); /* OVS_DP_ATTR_KERNEL_FEATURES */

return msgsize;
}
@@ -1476,6 +1477,9 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
if (nla_put_u32(skb, OVS_DP_ATTR_USER_FEATURES, dp->user_features))
goto nla_put_failure;

+ if (nla_put_u64(skb, OVS_DP_ATTR_KERNEL_FEATURES, OVS_DP_KF_ALL))
+ goto nla_put_failure;
+
genlmsg_end(skb, ovs_header);
return 0;

@@ -1526,6 +1530,13 @@ static void ovs_dp_change(struct datapath *dp, struct nlattr *a[])
dp->user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]);
}

+static bool ovs_dp_kernel_features_ok(struct nlattr *a[])
+{
+ if (!a[OVS_DP_ATTR_KERNEL_FEATURES])
+ return true;
+ return !(nla_get_u64(a[OVS_DP_ATTR_KERNEL_FEATURES]) & ~OVS_DP_KF_ALL);
+}
+
static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **a = info->attrs;
@@ -1536,9 +1547,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
struct ovs_net *ovs_net;
int err, i;

- err = -EINVAL;
if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID])
- goto err;
+ return -EINVAL;
+ if (!ovs_dp_kernel_features_ok(a))
+ return -EOPNOTSUPP;

reply = ovs_dp_cmd_alloc_info(info);
if (!reply)
@@ -1626,7 +1638,6 @@ err_free_dp:
kfree(dp);
err_free_reply:
kfree_skb(reply);
-err:
return err;
}

@@ -1694,6 +1705,9 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
struct datapath *dp;
int err;

+ if (!ovs_dp_kernel_features_ok(info->attrs))
+ return -EOPNOTSUPP;
+
reply = ovs_dp_cmd_alloc_info(info);
if (!reply)
return -ENOMEM;
@@ -1777,6 +1791,7 @@ 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 },
+ [OVS_DP_ATTR_KERNEL_FEATURES] = { .type = NLA_U64 },
};

static const struct genl_ops dp_datapath_genl_ops[] = {
--
1.8.3.1