datapath: Add genl_exec().
authorPravin B Shelar <pshelar@nicira.com>
Thu, 5 Jan 2012 01:20:08 +0000 (17:20 -0800)
committerPravin B Shelar <pshelar@nicira.com>
Thu, 5 Jan 2012 01:20:08 +0000 (17:20 -0800)
genl_lock is not exported from older kernel. Following patch add
genl_exec() which can run any function (passed as arg) with
genl_lock held.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
acinclude.m4
datapath/Modules.mk
datapath/datapath.c
datapath/genl_exec.c [new file with mode: 0644]
datapath/genl_exec.h [new file with mode: 0644]
datapath/linux/compat/include/linux/skbuff.h

index 648132a..4c1b065 100644 (file)
@@ -230,6 +230,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
                   [OVS_DEFINE([HAVE_SKB_DST_ACCESSOR_FUNCS])])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], 
                   [skb_copy_from_linear_data_offset])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h],
+                  [skb_reset_tail_pointer])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_cow_head])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_transport_header],
                   [OVS_DEFINE([HAVE_SKBUFF_HEADER_HELPERS])])
index e015a9d..4d17568 100644 (file)
@@ -17,6 +17,7 @@ openvswitch_sources = \
        dp_sysfs_dp.c \
        dp_sysfs_if.c \
        flow.c \
+       genl_exec.c \
        tunnel.c \
        vlan.c \
        vport.c \
@@ -33,6 +34,7 @@ openvswitch_headers = \
        datapath.h \
        dp_sysfs.h \
        flow.h \
+       genl_exec.h \
        tunnel.h \
        vlan.h \
        vport.h \
index c86c20b..281e86b 100644 (file)
@@ -54,6 +54,7 @@
 #include "checksum.h"
 #include "datapath.h"
 #include "flow.h"
+#include "genl_exec.h"
 #include "vlan.h"
 #include "tunnel.h"
 #include "vport-internal_dev.h"
@@ -2049,10 +2050,14 @@ static int __init dp_init(void)
        pr_info("Open vSwitch switching datapath %s, built "__DATE__" "__TIME__"\n",
                VERSION BUILDNR);
 
-       err = ovs_tnl_init();
+       err = genl_exec_init();
        if (err)
                goto error;
 
+       err = ovs_tnl_init();
+       if (err)
+               goto error_genl_exec;
+
        err = ovs_flow_init();
        if (err)
                goto error_tnl_exit;
@@ -2079,6 +2084,8 @@ error_flow_exit:
        ovs_flow_exit();
 error_tnl_exit:
        ovs_tnl_exit();
+error_genl_exec:
+       genl_exec_exit();
 error:
        return err;
 }
@@ -2091,6 +2098,7 @@ static void dp_cleanup(void)
        ovs_vport_exit();
        ovs_flow_exit();
        ovs_tnl_exit();
+       genl_exec_exit();
 }
 
 module_init(dp_init);
diff --git a/datapath/genl_exec.c b/datapath/genl_exec.c
new file mode 100644 (file)
index 0000000..e579529
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2007-2012 Nicira Networks.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
+#include <net/genetlink.h>
+#include "genl_exec.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+
+static DEFINE_MUTEX(genl_exec_lock);
+
+static genl_exec_func_t         genl_exec_function;
+static int              genl_exec_function_ret;
+static void            *genl_exec_data;
+static struct completion done;
+
+static struct sk_buff *genlmsg_skb;
+
+static int genl_exec_cmd(struct sk_buff *dummy, struct genl_info *dummy2)
+{
+       genl_exec_function_ret = genl_exec_function(genl_exec_data);
+       complete(&done);
+       return 0;
+}
+
+enum exec_cmd {
+       GENL_EXEC_UNSPEC,
+       GENL_EXEC_RUN,
+};
+
+static struct genl_family genl_exec_family = {
+       .id = GENL_ID_GENERATE,
+       .name = "ovs_genl_exec",
+       .version = 1,
+};
+
+static struct genl_ops genl_exec_ops[] = {
+       {
+        .cmd = GENL_EXEC_RUN,
+        .doit = genl_exec_cmd,
+        .flags = CAP_NET_ADMIN,
+       },
+};
+
+int genl_exec_init(void)
+{
+       int err;
+
+       err = genl_register_family_with_ops(&genl_exec_family,
+                       genl_exec_ops, ARRAY_SIZE(genl_exec_ops));
+
+       if (err)
+               return err;
+
+       genlmsg_skb = genlmsg_new(0, GFP_KERNEL);
+       if (!genlmsg_skb) {
+               genl_unregister_family(&genl_exec_family);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void genl_exec_exit(void)
+{
+       kfree_skb(genlmsg_skb);
+       genl_unregister_family(&genl_exec_family);
+}
+
+/* genl_lock() is not exported from older kernel.
+ * Following function allows any function to be executed with
+ * genl_mutex held. */
+
+int genl_exec(genl_exec_func_t func, void *data)
+{
+       int ret;
+
+       mutex_lock(&genl_exec_lock);
+
+       init_completion(&done);
+       skb_get(genlmsg_skb);
+       genlmsg_put(genlmsg_skb, 0, 0, &genl_exec_family,
+                   NLM_F_REQUEST, GENL_EXEC_RUN);
+
+       genl_exec_function = func;
+       genl_exec_data = data;
+       ret = genlmsg_unicast(&init_net, genlmsg_skb, 0);
+
+       if (!ret) {
+               wait_for_completion(&done);
+               ret = genl_exec_function_ret;
+       } else {
+               pr_err("genl_exec send error %d\n", ret);
+       }
+
+       /* Wait for genetlink to kfree skb. */
+       while (skb_shared(genlmsg_skb))
+               cpu_relax();
+
+       genlmsg_skb->data = genlmsg_skb->head;
+       skb_reset_tail_pointer(genlmsg_skb);
+
+       mutex_unlock(&genl_exec_lock);
+
+       return ret;
+}
+
+#else
+
+int genl_exec(genl_exec_func_t func, void *data)
+{
+       int ret;
+
+       genl_lock();
+       ret = func(data);
+       genl_unlock();
+       return ret;
+}
+
+int genl_exec_init(void)
+{
+       return 0;
+}
+
+void genl_exec_exit(void)
+{
+}
+#endif
diff --git a/datapath/genl_exec.h b/datapath/genl_exec.h
new file mode 100644 (file)
index 0000000..251aa0a
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2007-2012 Nicira Networks.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef GENL_EXEC_H
+#define GENL_EXEC_H 1
+
+typedef int (*genl_exec_func_t)(void *data);
+int genl_exec(genl_exec_func_t func, void *data);
+int genl_exec_init(void);
+void genl_exec_exit(void);
+
+#endif /* genl_exec.h */
index 96d8012..01e524e 100644 (file)
@@ -34,6 +34,12 @@ static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb,
 
 #endif /* !HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET */
 
+#ifndef HAVE_SKB_RESET_TAIL_POINTER
+static inline void skb_reset_tail_pointer(struct sk_buff *skb)
+{
+       skb->tail = skb->data;
+}
+#endif
 /*
  * The networking layer reserves some headroom in skb data (via
  * dev_alloc_skb). This is used to avoid having to reallocate skb data when