[ovs-dev,v3,7/7] dpif-netlink: Probe for out-of-tree tunnels, decides used interface

Message ID 20170413204732.16707-8-e@erig.me
State Changes Requested
Delegated to: Joe Stringer
Headers show

Commit Message

Eric Garver April 13, 2017, 8:47 p.m.
On dpif init, probe for whether tunnels are created using in-tree
(upstream linux) or out-of-tree (OVS). This is done by probing for the
existence of "ovs_geneve" via rtnetlink. This is used to determine how
to create the tunnel devices.

For out-of-tree tunnels, only try genetlink/compat.
For in-tree kernel tunnels, try rtnetlink then fallback to genetlink.

Signed-off-by: Eric Garver <e@erig.me>
---
 NEWS                    |  3 +++
 lib/dpif-netlink-rtnl.c | 38 +++++++++++++++++++++++++++++++++++
 lib/dpif-netlink-rtnl.h |  8 ++++++++
 lib/dpif-netlink.c      | 53 ++++++++++++++++++++++++++++++++++---------------
 4 files changed, 86 insertions(+), 16 deletions(-)

Patch

diff --git a/NEWS b/NEWS
index 05af97a1f030..1b7df356f3de 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,9 @@  Post-v2.7.0
    - Tunnels:
      * Added support to set packet mark for tunnel endpoint using
        `egress_pkt_mark` OVSDB option.
+     * When using Linux kernel datapath tunnels may be created using rtnetlink.
+       This will allow us to take advantage of new tunnel features without
+       having to make changes to the vport modules.
    - EMC insertion probability is reduced to 1% and is configurable via
      the new 'other_config:emc-insert-inv-prob' option.
    - DPDK:
diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c
index 5a3326ea1f63..cc719bc209a3 100644
--- a/lib/dpif-netlink-rtnl.c
+++ b/lib/dpif-netlink-rtnl.c
@@ -448,3 +448,41 @@  dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
     }
     return 0;
 }
+
+/**
+ * Probe for whether the modules are out-of-tree (openvswitch) or in-tree
+ * (upstream kernel).
+ *
+ * We probe for "ovs_geneve" via rtnetlink. As long as this returns something
+ * other than EOPNOTSUPP we know that the module in use is the out-of-tree one.
+ * This will be used to determine which netlink interface to use when creating
+ * ports; rtnetlink or compat/genetlink.
+ *
+ * See ovs_tunnels_out_of_tree
+ */
+bool
+dpif_netlink_rtnl_probe_oot_tunnels(void)
+{
+    struct netdev *netdev = NULL;
+    bool out_of_tree = false;
+    int error;
+
+    error = netdev_open("ovs-system-probe", "geneve", &netdev);
+    if (!error) {
+        error = dpif_netlink_rtnl_geneve_create_kind(netdev, "ovs_geneve");
+        if (error != EOPNOTSUPP) {
+            if (!error) {
+                char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+                const char *dp_port;
+
+                dp_port = netdev_vport_get_dpif_port(netdev, namebuf,
+                                                     sizeof namebuf);
+                dpif_netlink_rtnl_destroy(dp_port);
+            }
+            out_of_tree = true;
+        }
+        netdev_close(netdev);
+    }
+
+    return out_of_tree;
+}
diff --git a/lib/dpif-netlink-rtnl.h b/lib/dpif-netlink-rtnl.h
index 952c0d4187e5..5c790e0bc06f 100644
--- a/lib/dpif-netlink-rtnl.h
+++ b/lib/dpif-netlink-rtnl.h
@@ -25,6 +25,8 @@ 
 int dpif_netlink_rtnl_port_create(struct netdev *netdev);
 int dpif_netlink_rtnl_port_destroy(const char *name, const char *type);
 
+bool dpif_netlink_rtnl_probe_oot_tunnels(void);
+
 #ifndef __linux__
 /* Dummy implementations for non Linux builds. */
 
@@ -41,6 +43,12 @@  dpif_netlink_rtnl_port_destroy(const char *name OVS_UNUSED,
     return EOPNOTSUPP;
 }
 
+static inline bool
+dpif_netlink_rtnl_probe_oot_tunnels(void)
+{
+    return true;
+}
+
 #endif
 
 #endif /* DPIF_NETLINK_RTNL_H */
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 2d34a68c57e4..5244d7e24179 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -61,9 +61,6 @@  VLOG_DEFINE_THIS_MODULE(dpif_netlink);
 #ifdef _WIN32
 #include "wmi.h"
 enum { WINDOWS = 1 };
-static int dpif_netlink_port_query__(const struct dpif_netlink *dpif,
-                                     odp_port_t port_no, const char *port_name,
-                                     struct dpif_port *dpif_port);
 #else
 enum { WINDOWS = 0 };
 #endif
@@ -214,6 +211,12 @@  static int ovs_packet_family;
  * Initialized by dpif_netlink_init(). */
 static unsigned int ovs_vport_mcgroup;
 
+/* If true, tunnel devices are created using OVS compat/genetlink.
+ * If false, tunnel devices are created with rtnetlink and using light weight
+ * tunnels. If we fail to create the tunnel the rtnetlink+LWT, then we fallback
+ * to using the compat interface. */
+static bool ovs_tunnels_out_of_tree = true;
+
 static int dpif_netlink_init(void);
 static int open_dpif(const struct dpif_netlink_dp *, struct dpif **);
 static uint32_t dpif_netlink_port_get_pid(const struct dpif *,
@@ -225,6 +228,9 @@  static void dpif_netlink_vport_to_ofpbuf(const struct dpif_netlink_vport *,
                                          struct ofpbuf *);
 static int dpif_netlink_vport_from_ofpbuf(struct dpif_netlink_vport *,
                                           const struct ofpbuf *);
+static int dpif_netlink_port_query__(const struct dpif_netlink *dpif,
+                                     odp_port_t port_no, const char *port_name,
+                                     struct dpif_port *dpif_port);
 
 static struct dpif_netlink *
 dpif_netlink_cast(const struct dpif *dpif)
@@ -947,7 +953,7 @@  dpif_netlink_port_add_compat(struct dpif_netlink *dpif, struct netdev *netdev,
 
 }
 
-static int OVS_UNUSED
+static int
 dpif_netlink_rtnl_port_create_and_add(struct dpif_netlink *dpif,
                                       struct netdev *netdev,
                                       odp_port_t *port_nop)
@@ -981,10 +987,15 @@  dpif_netlink_port_add(struct dpif *dpif_, struct netdev *netdev,
                       odp_port_t *port_nop)
 {
     struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
-    int error;
+    int error = EOPNOTSUPP;
 
     fat_rwlock_wrlock(&dpif->upcall_lock);
-    error = dpif_netlink_port_add_compat(dpif, netdev, port_nop);
+    if (!ovs_tunnels_out_of_tree) {
+        error = dpif_netlink_rtnl_port_create_and_add(dpif, netdev, port_nop);
+    }
+    if (error) {
+        error = dpif_netlink_port_add_compat(dpif, netdev, port_nop);
+    }
     fat_rwlock_unlock(&dpif->upcall_lock);
 
     return error;
@@ -995,31 +1006,39 @@  dpif_netlink_port_del__(struct dpif_netlink *dpif, odp_port_t port_no)
     OVS_REQ_WRLOCK(dpif->upcall_lock)
 {
     struct dpif_netlink_vport vport;
+    struct dpif_port dpif_port;
     int error;
 
+    error = dpif_netlink_port_query__(dpif, port_no, NULL, &dpif_port);
+    if (error) {
+        return error;
+    }
+
     dpif_netlink_vport_init(&vport);
     vport.cmd = OVS_VPORT_CMD_DEL;
     vport.dp_ifindex = dpif->dp_ifindex;
     vport.port_no = port_no;
 #ifdef _WIN32
-    struct dpif_port temp_dpif_port;
-
-    error = dpif_netlink_port_query__(dpif, port_no, NULL, &temp_dpif_port);
-    if (error) {
-        return error;
-    }
-    if (!strcmp(temp_dpif_port.type, "internal")) {
-        if (!delete_wmi_port(temp_dpif_port.name)){
+    if (!strcmp(dpif_port.type, "internal")) {
+        if (!delete_wmi_port(dpif_port.name)) {
             VLOG_ERR("Could not delete wmi port with name: %s",
-                     temp_dpif_port.name);
+                     dpif_port.name);
         };
     }
-    dpif_port_destroy(&temp_dpif_port);
 #endif
     error = dpif_netlink_vport_transact(&vport, NULL, NULL);
 
     vport_del_channels(dpif, port_no);
 
+    if (!error && !ovs_tunnels_out_of_tree) {
+        error = dpif_netlink_rtnl_port_destroy(dpif_port.name, dpif_port.type);
+        if (error == EOPNOTSUPP) {
+            error = 0;
+        }
+    }
+
+    dpif_port_destroy(&dpif_port);
+
     return error;
 }
 
@@ -2531,6 +2550,8 @@  dpif_netlink_init(void)
                                            &ovs_vport_mcgroup);
         }
 
+        ovs_tunnels_out_of_tree = dpif_netlink_rtnl_probe_oot_tunnels();
+
         ovsthread_once_done(&once);
     }