+/*
+ * 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