/* * 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 #include #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