datapath: Add genl_exec().
[sliver-openvswitch.git] / datapath / genl_exec.c
1 /*
2  * Copyright (c) 2007-2012 Nicira Networks.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 2 of the GNU General Public
6  * License as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16  * 02110-1301, USA
17  */
18
19 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
21 #include <linux/version.h>
22 #include <net/genetlink.h>
23 #include "genl_exec.h"
24
25 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
26
27 static DEFINE_MUTEX(genl_exec_lock);
28
29 static genl_exec_func_t  genl_exec_function;
30 static int               genl_exec_function_ret;
31 static void             *genl_exec_data;
32 static struct completion done;
33
34 static struct sk_buff *genlmsg_skb;
35
36 static int genl_exec_cmd(struct sk_buff *dummy, struct genl_info *dummy2)
37 {
38         genl_exec_function_ret = genl_exec_function(genl_exec_data);
39         complete(&done);
40         return 0;
41 }
42
43 enum exec_cmd {
44         GENL_EXEC_UNSPEC,
45         GENL_EXEC_RUN,
46 };
47
48 static struct genl_family genl_exec_family = {
49         .id = GENL_ID_GENERATE,
50         .name = "ovs_genl_exec",
51         .version = 1,
52 };
53
54 static struct genl_ops genl_exec_ops[] = {
55         {
56          .cmd = GENL_EXEC_RUN,
57          .doit = genl_exec_cmd,
58          .flags = CAP_NET_ADMIN,
59         },
60 };
61
62 int genl_exec_init(void)
63 {
64         int err;
65
66         err = genl_register_family_with_ops(&genl_exec_family,
67                         genl_exec_ops, ARRAY_SIZE(genl_exec_ops));
68
69         if (err)
70                 return err;
71
72         genlmsg_skb = genlmsg_new(0, GFP_KERNEL);
73         if (!genlmsg_skb) {
74                 genl_unregister_family(&genl_exec_family);
75                 return -ENOMEM;
76         }
77         return 0;
78 }
79
80 void genl_exec_exit(void)
81 {
82         kfree_skb(genlmsg_skb);
83         genl_unregister_family(&genl_exec_family);
84 }
85
86 /* genl_lock() is not exported from older kernel.
87  * Following function allows any function to be executed with
88  * genl_mutex held. */
89
90 int genl_exec(genl_exec_func_t func, void *data)
91 {
92         int ret;
93
94         mutex_lock(&genl_exec_lock);
95
96         init_completion(&done);
97         skb_get(genlmsg_skb);
98         genlmsg_put(genlmsg_skb, 0, 0, &genl_exec_family,
99                     NLM_F_REQUEST, GENL_EXEC_RUN);
100
101         genl_exec_function = func;
102         genl_exec_data = data;
103         ret = genlmsg_unicast(&init_net, genlmsg_skb, 0);
104
105         if (!ret) {
106                 wait_for_completion(&done);
107                 ret = genl_exec_function_ret;
108         } else {
109                 pr_err("genl_exec send error %d\n", ret);
110         }
111
112         /* Wait for genetlink to kfree skb. */
113         while (skb_shared(genlmsg_skb))
114                 cpu_relax();
115
116         genlmsg_skb->data = genlmsg_skb->head;
117         skb_reset_tail_pointer(genlmsg_skb);
118
119         mutex_unlock(&genl_exec_lock);
120
121         return ret;
122 }
123
124 #else
125
126 int genl_exec(genl_exec_func_t func, void *data)
127 {
128         int ret;
129
130         genl_lock();
131         ret = func(data);
132         genl_unlock();
133         return ret;
134 }
135
136 int genl_exec_init(void)
137 {
138         return 0;
139 }
140
141 void genl_exec_exit(void)
142 {
143 }
144 #endif