]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - sys/kern/kern_hhook.c
MFC r251725:
[FreeBSD/stable/9.git] / sys / kern / kern_hhook.c
1 /*-
2  * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
3  * Copyright (c) 2010 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * This software was developed by Lawrence Stewart while studying at the Centre
7  * for Advanced Internet Architectures, Swinburne University of Technology,
8  * made possible in part by grants from the FreeBSD Foundation and Cisco
9  * University Research Program Fund at Community Foundation Silicon Valley.
10  *
11  * Portions of this software were developed at the Centre for Advanced
12  * Internet Architectures, Swinburne University of Technology, Melbourne,
13  * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42 #include <sys/hhook.h>
43 #include <sys/khelp.h>
44 #include <sys/malloc.h>
45 #include <sys/module.h>
46 #include <sys/module_khelp.h>
47 #include <sys/osd.h>
48 #include <sys/queue.h>
49 #include <sys/refcount.h>
50 #include <sys/systm.h>
51
52 #include <net/vnet.h>
53
54 struct hhook {
55         hhook_func_t            hhk_func;
56         struct helper           *hhk_helper;
57         void                    *hhk_udata;
58         STAILQ_ENTRY(hhook)     hhk_next;
59 };
60
61 static MALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists");
62
63 LIST_HEAD(hhookheadhead, hhook_head);
64 VNET_DEFINE(struct hhookheadhead, hhook_head_list);
65 #define V_hhook_head_list VNET(hhook_head_list)
66
67 static struct mtx hhook_head_list_lock;
68 MTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
69     MTX_DEF);
70
71 /* Private function prototypes. */
72 static void hhook_head_destroy(struct hhook_head *hhh);
73
74 #define HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock)
75 #define HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock)
76 #define HHHLIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED)
77
78 #define HHH_LOCK_INIT(hhh) rm_init(&(hhh)->hhh_lock, "hhook_head rm lock")
79 #define HHH_LOCK_DESTROY(hhh) rm_destroy(&(hhh)->hhh_lock)
80 #define HHH_WLOCK(hhh) rm_wlock(&(hhh)->hhh_lock)
81 #define HHH_WUNLOCK(hhh) rm_wunlock(&(hhh)->hhh_lock)
82 #define HHH_RLOCK(hhh, rmpt) rm_rlock(&(hhh)->hhh_lock, (rmpt))
83 #define HHH_RUNLOCK(hhh, rmpt) rm_runlock(&(hhh)->hhh_lock, (rmpt))
84
85 /*
86  * Run all helper hook functions for a given hook point.
87  */
88 void
89 hhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd)
90 {
91         struct hhook *hhk;
92         void *hdata;
93         struct rm_priotracker rmpt;
94
95         KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh));
96
97         HHH_RLOCK(hhh, &rmpt);
98         STAILQ_FOREACH(hhk, &hhh->hhh_hooks, hhk_next) {
99                 if (hhk->hhk_helper->h_flags & HELPER_NEEDS_OSD) {
100                         hdata = osd_get(OSD_KHELP, hosd, hhk->hhk_helper->h_id);
101                         if (hdata == NULL)
102                                 continue;
103                 } else
104                         hdata = NULL;
105
106                 /*
107                  * XXXLAS: We currently ignore the int returned by the hook,
108                  * but will likely want to handle it in future to allow hhook to
109                  * be used like pfil and effect changes at the hhook calling
110                  * site e.g. we could define a new hook type of HHOOK_TYPE_PFIL
111                  * and standardise what particular return values mean and set
112                  * the context data to pass exactly the same information as pfil
113                  * hooks currently receive, thus replicating pfil with hhook.
114                  */
115                 hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata,
116                     ctx_data, hdata, hosd);
117         }
118         HHH_RUNLOCK(hhh, &rmpt);
119 }
120
121 /*
122  * Register a new helper hook function with a helper hook point.
123  */
124 int
125 hhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags)
126 {
127         struct hhook *hhk, *tmp;
128         int error;
129
130         error = 0;
131
132         if (hhh == NULL)
133                 return (ENOENT);
134
135         hhk = malloc(sizeof(struct hhook), M_HHOOK,
136             M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
137
138         if (hhk == NULL)
139                 return (ENOMEM);
140
141         hhk->hhk_helper = hki->hook_helper;
142         hhk->hhk_func = hki->hook_func;
143         hhk->hhk_udata = hki->hook_udata;
144
145         HHH_WLOCK(hhh);
146         STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
147                 if (tmp->hhk_func == hki->hook_func &&
148                     tmp->hhk_udata == hki->hook_udata) {
149                         /* The helper hook function is already registered. */
150                         error = EEXIST;
151                         break;
152                 }
153         }
154
155         if (!error) {
156                 STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next);
157                 hhh->hhh_nhooks++;
158         } else
159                 free(hhk, M_HHOOK);
160
161         HHH_WUNLOCK(hhh);
162
163         return (error);
164 }
165
166 /*
167  * Lookup a helper hook point and register a new helper hook function with it.
168  */
169 int
170 hhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags)
171 {
172         struct hhook_head *hhh;
173         int error;
174
175         hhh = hhook_head_get(hki->hook_type, hki->hook_id);
176
177         if (hhh == NULL)
178                 return (ENOENT);
179
180         error = hhook_add_hook(hhh, hki, flags);
181         hhook_head_release(hhh);
182
183         return (error);
184 }
185
186 /*
187  * Remove a helper hook function from a helper hook point.
188  */
189 int
190 hhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki)
191 {
192         struct hhook *tmp;
193
194         if (hhh == NULL)
195                 return (ENOENT);
196
197         HHH_WLOCK(hhh);
198         STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) {
199                 if (tmp->hhk_func == hki->hook_func &&
200                     tmp->hhk_udata == hki->hook_udata) {
201                         STAILQ_REMOVE(&hhh->hhh_hooks, tmp, hhook, hhk_next);
202                         free(tmp, M_HHOOK);
203                         hhh->hhh_nhooks--;
204                         break;
205                 }
206         }
207         HHH_WUNLOCK(hhh);
208
209         return (0);
210 }
211
212 /*
213  * Lookup a helper hook point and remove a helper hook function from it.
214  */
215 int
216 hhook_remove_hook_lookup(struct hookinfo *hki)
217 {
218         struct hhook_head *hhh;
219
220         hhh = hhook_head_get(hki->hook_type, hki->hook_id);
221
222         if (hhh == NULL)
223                 return (ENOENT);
224
225         hhook_remove_hook(hhh, hki);
226         hhook_head_release(hhh);
227
228         return (0);
229 }
230
231 /*
232  * Register a new helper hook point.
233  */
234 int
235 hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh,
236     uint32_t flags)
237 {
238         struct hhook_head *tmphhh;
239
240         tmphhh = hhook_head_get(hhook_type, hhook_id);
241
242         if (tmphhh != NULL) {
243                 /* Hook point previously registered. */
244                 hhook_head_release(tmphhh);
245                 return (EEXIST);
246         }
247
248         /* XXXLAS: Need to implement support for non-virtualised hooks. */
249         if ((flags & HHOOK_HEADISINVNET) == 0) {
250                 printf("%s: only vnet-style virtualised hooks can be used\n",
251                     __func__);
252                 return (EINVAL);
253         }
254
255         tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK,
256             M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
257
258         if (tmphhh == NULL)
259                 return (ENOMEM);
260
261         tmphhh->hhh_type = hhook_type;
262         tmphhh->hhh_id = hhook_id;
263         tmphhh->hhh_nhooks = 0;
264         STAILQ_INIT(&tmphhh->hhh_hooks);
265         HHH_LOCK_INIT(tmphhh);
266
267         if (hhh != NULL) {
268                 refcount_init(&tmphhh->hhh_refcount, 1);
269                 *hhh = tmphhh;
270         } else
271                 refcount_init(&tmphhh->hhh_refcount, 0);
272
273         if (flags & HHOOK_HEADISINVNET) {
274                 tmphhh->hhh_flags |= HHH_ISINVNET;
275                 HHHLIST_LOCK();
276                 LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next);
277                 HHHLIST_UNLOCK();
278         } else {
279                 /* XXXLAS: Add tmphhh to the non-virtualised list. */
280         }
281
282         return (0);
283 }
284
285 static void
286 hhook_head_destroy(struct hhook_head *hhh)
287 {
288         struct hhook *tmp, *tmp2;
289
290         HHHLIST_LOCK_ASSERT();
291
292         LIST_REMOVE(hhh, hhh_next);
293         HHH_WLOCK(hhh);
294         STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2)
295                 free(tmp, M_HHOOK);
296         HHH_WUNLOCK(hhh);
297         HHH_LOCK_DESTROY(hhh);
298         free(hhh, M_HHOOK);
299 }
300
301 /*
302  * Remove a helper hook point.
303  */
304 int
305 hhook_head_deregister(struct hhook_head *hhh)
306 {
307         int error;
308
309         error = 0;
310
311         HHHLIST_LOCK();
312         if (hhh == NULL)
313                 error = ENOENT;
314         else if (hhh->hhh_refcount > 1)
315                 error = EBUSY;
316         else
317                 hhook_head_destroy(hhh);
318         HHHLIST_UNLOCK();
319
320         return (error);
321 }
322
323 /*
324  * Remove a helper hook point via a hhook_head lookup.
325  */
326 int
327 hhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id)
328 {
329         struct hhook_head *hhh;
330         int error;
331
332         hhh = hhook_head_get(hhook_type, hhook_id);
333         error = hhook_head_deregister(hhh);
334
335         if (error == EBUSY)
336                 hhook_head_release(hhh);
337
338         return (error);
339 }
340
341 /*
342  * Lookup and return the hhook_head struct associated with the specified type
343  * and id, or NULL if not found. If found, the hhook_head's refcount is bumped.
344  */
345 struct hhook_head *
346 hhook_head_get(int32_t hhook_type, int32_t hhook_id)
347 {
348         struct hhook_head *hhh;
349
350         /* XXXLAS: Pick hhook_head_list based on hhook_head flags. */
351         HHHLIST_LOCK();
352         LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) {
353                 if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) {
354                         refcount_acquire(&hhh->hhh_refcount);
355                         break;
356                 }
357         }
358         HHHLIST_UNLOCK();
359
360         return (hhh);
361 }
362
363 void
364 hhook_head_release(struct hhook_head *hhh)
365 {
366
367         refcount_release(&hhh->hhh_refcount);
368 }
369
370 /*
371  * Check the hhook_head private flags and return the appropriate public
372  * representation of the flag to the caller. The function is implemented in a
373  * way that allows us to cope with other subsystems becoming virtualised in the
374  * future.
375  */
376 uint32_t
377 hhook_head_is_virtualised(struct hhook_head *hhh)
378 {
379         uint32_t ret;
380
381         ret = 0;
382
383         if (hhh != NULL) {
384                 if (hhh->hhh_flags & HHH_ISINVNET)
385                         ret = HHOOK_HEADISINVNET;
386         }
387
388         return (ret);
389 }
390
391 uint32_t
392 hhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id)
393 {
394         struct hhook_head *hhh;
395         uint32_t ret;
396
397         hhh = hhook_head_get(hook_type, hook_id);
398
399         if (hhh == NULL)
400                 return (0);
401
402         ret = hhook_head_is_virtualised(hhh);
403         hhook_head_release(hhh);
404
405         return (ret);
406 }
407
408 /*
409  * Vnet created and being initialised.
410  */
411 static void
412 hhook_vnet_init(const void *unused __unused)
413 {
414
415         LIST_INIT(&V_hhook_head_list);
416 }
417
418 /*
419  * Vnet being torn down and destroyed.
420  */
421 static void
422 hhook_vnet_uninit(const void *unused __unused)
423 {
424         struct hhook_head *hhh, *tmphhh;
425
426         /*
427          * If subsystems which export helper hook points use the hhook KPI
428          * correctly, the loop below should have no work to do because the
429          * subsystem should have already called hhook_head_deregister().
430          */
431         HHHLIST_LOCK();
432         LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) {
433                 printf("%s: hhook_head type=%d, id=%d cleanup required\n",
434                     __func__, hhh->hhh_type, hhh->hhh_id);
435                 hhook_head_destroy(hhh);
436         }
437         HHHLIST_UNLOCK();
438 }
439
440
441 /*
442  * When a vnet is created and being initialised, init the V_hhook_head_list.
443  */
444 VNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
445     hhook_vnet_init, NULL);
446
447 /*
448  * The hhook KPI provides a mechanism for subsystems which export helper hook
449  * points to clean up on vnet tear down, but in case the KPI is misused,
450  * provide a function to clean up and free memory for a vnet being destroyed.
451  */
452 VNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
453     hhook_vnet_uninit, NULL);