2 * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
3 * Copyright (c) 2010 The FreeBSD Foundation
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.
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.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
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.
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
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
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>
48 #include <sys/queue.h>
49 #include <sys/refcount.h>
50 #include <sys/systm.h>
55 hhook_func_t hhk_func;
56 struct helper *hhk_helper;
58 STAILQ_ENTRY(hhook) hhk_next;
61 static MALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists");
63 LIST_HEAD(hhookheadhead, hhook_head);
64 VNET_DEFINE(struct hhookheadhead, hhook_head_list);
65 #define V_hhook_head_list VNET(hhook_head_list)
67 static struct mtx hhook_head_list_lock;
68 MTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
71 /* Private function prototypes. */
72 static void hhook_head_destroy(struct hhook_head *hhh);
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)
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))
86 * Run all helper hook functions for a given hook point.
89 hhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd)
93 struct rm_priotracker rmpt;
95 KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh));
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);
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.
115 hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata,
116 ctx_data, hdata, hosd);
118 HHH_RUNLOCK(hhh, &rmpt);
122 * Register a new helper hook function with a helper hook point.
125 hhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags)
127 struct hhook *hhk, *tmp;
135 hhk = malloc(sizeof(struct hhook), M_HHOOK,
136 M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
141 hhk->hhk_helper = hki->hook_helper;
142 hhk->hhk_func = hki->hook_func;
143 hhk->hhk_udata = hki->hook_udata;
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. */
156 STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next);
167 * Lookup a helper hook point and register a new helper hook function with it.
170 hhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags)
172 struct hhook_head *hhh;
175 hhh = hhook_head_get(hki->hook_type, hki->hook_id);
180 error = hhook_add_hook(hhh, hki, flags);
181 hhook_head_release(hhh);
187 * Remove a helper hook function from a helper hook point.
190 hhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki)
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);
213 * Lookup a helper hook point and remove a helper hook function from it.
216 hhook_remove_hook_lookup(struct hookinfo *hki)
218 struct hhook_head *hhh;
220 hhh = hhook_head_get(hki->hook_type, hki->hook_id);
225 hhook_remove_hook(hhh, hki);
226 hhook_head_release(hhh);
232 * Register a new helper hook point.
235 hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh,
238 struct hhook_head *tmphhh;
240 tmphhh = hhook_head_get(hhook_type, hhook_id);
242 if (tmphhh != NULL) {
243 /* Hook point previously registered. */
244 hhook_head_release(tmphhh);
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",
255 tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK,
256 M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
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);
268 refcount_init(&tmphhh->hhh_refcount, 1);
270 refcount_init(&tmphhh->hhh_refcount, 0);
272 if (flags & HHOOK_HEADISINVNET) {
273 tmphhh->hhh_flags |= HHH_ISINVNET;
275 LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next);
278 /* XXXLAS: Add tmphhh to the non-virtualised list. */
287 hhook_head_destroy(struct hhook_head *hhh)
289 struct hhook *tmp, *tmp2;
291 HHHLIST_LOCK_ASSERT();
293 LIST_REMOVE(hhh, hhh_next);
295 STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2)
298 HHH_LOCK_DESTROY(hhh);
303 * Remove a helper hook point.
306 hhook_head_deregister(struct hhook_head *hhh)
315 else if (hhh->hhh_refcount > 1)
318 hhook_head_destroy(hhh);
325 * Remove a helper hook point via a hhook_head lookup.
328 hhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id)
330 struct hhook_head *hhh;
333 hhh = hhook_head_get(hhook_type, hhook_id);
334 error = hhook_head_deregister(hhh);
337 hhook_head_release(hhh);
343 * Lookup and return the hhook_head struct associated with the specified type
344 * and id, or NULL if not found. If found, the hhook_head's refcount is bumped.
347 hhook_head_get(int32_t hhook_type, int32_t hhook_id)
349 struct hhook_head *hhh;
351 /* XXXLAS: Pick hhook_head_list based on hhook_head flags. */
353 LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) {
354 if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) {
355 refcount_acquire(&hhh->hhh_refcount);
365 hhook_head_release(struct hhook_head *hhh)
368 refcount_release(&hhh->hhh_refcount);
372 * Check the hhook_head private flags and return the appropriate public
373 * representation of the flag to the caller. The function is implemented in a
374 * way that allows us to cope with other subsystems becoming virtualised in the
378 hhook_head_is_virtualised(struct hhook_head *hhh)
385 if (hhh->hhh_flags & HHH_ISINVNET)
386 ret = HHOOK_HEADISINVNET;
393 hhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id)
395 struct hhook_head *hhh;
398 hhh = hhook_head_get(hook_type, hook_id);
403 ret = hhook_head_is_virtualised(hhh);
404 hhook_head_release(hhh);
410 * Vnet created and being initialised.
413 hhook_vnet_init(const void *unused __unused)
416 LIST_INIT(&V_hhook_head_list);
420 * Vnet being torn down and destroyed.
423 hhook_vnet_uninit(const void *unused __unused)
425 struct hhook_head *hhh, *tmphhh;
428 * If subsystems which export helper hook points use the hhook KPI
429 * correctly, the loop below should have no work to do because the
430 * subsystem should have already called hhook_head_deregister().
433 LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) {
434 printf("%s: hhook_head type=%d, id=%d cleanup required\n",
435 __func__, hhh->hhh_type, hhh->hhh_id);
436 hhook_head_destroy(hhh);
443 * When a vnet is created and being initialised, init the V_hhook_head_list.
445 VNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
446 hhook_vnet_init, NULL);
449 * The hhook KPI provides a mechanism for subsystems which export helper hook
450 * points to clean up on vnet tear down, but in case the KPI is misused,
451 * provide a function to clean up and free memory for a vnet being destroyed.
453 VNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
454 hhook_vnet_uninit, NULL);