2 * Copyright (c) 2010,2013 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>
45 #include <sys/malloc.h>
46 #include <sys/module.h>
47 #include <sys/module_khelp.h>
49 #include <sys/queue.h>
50 #include <sys/refcount.h>
51 #include <sys/rwlock.h>
52 #include <sys/systm.h>
54 static struct rwlock khelp_list_lock
;
55 RW_SYSINIT(khelplistlock
, &khelp_list_lock
, "helper list lock");
57 static TAILQ_HEAD(helper_head
, helper
) helpers
= TAILQ_HEAD_INITIALIZER(helpers
);
59 /* Private function prototypes. */
60 static inline void khelp_remove_osd(struct helper
*h
, struct osd
*hosd
);
61 void khelp_new_hhook_registered(struct hhook_head
*hhh
, uint32_t flags
);
63 #define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock)
64 #define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock)
65 #define KHELP_LIST_RLOCK() rw_rlock(&khelp_list_lock)
66 #define KHELP_LIST_RUNLOCK() rw_runlock(&khelp_list_lock)
67 #define KHELP_LIST_LOCK_ASSERT() rw_assert(&khelp_list_lock, RA_LOCKED)
70 khelp_register_helper(struct helper
*h
)
73 int error
, i
, inserted
;
76 refcount_init(&h
->h_refcount
, 0);
77 h
->h_id
= osd_register(OSD_KHELP
, NULL
, NULL
);
79 /* It's only safe to add the hooks after osd_register(). */
80 for (i
= 0; i
< h
->h_nhooks
&& !error
; i
++) {
81 /* We don't require the module to assign hook_helper. */
82 h
->h_hooks
[i
].hook_helper
= h
;
83 error
= hhook_add_hook_lookup(&h
->h_hooks
[i
], HHOOK_WAITOK
);
85 printf("%s: \"%s\" khelp module unable to "
86 "hook type %d id %d due to error %d\n", __func__
,
87 h
->h_name
, h
->h_hooks
[i
].hook_type
,
88 h
->h_hooks
[i
].hook_id
, error
);
92 for (i
--; i
>= 0; i
--)
93 hhook_remove_hook_lookup(&h
->h_hooks
[i
]);
94 osd_deregister(OSD_KHELP
, h
->h_id
);
98 * Keep list of helpers sorted in descending h_id order. Due to
99 * the way osd_set() works, a sorted list ensures
100 * khelp_init_osd() will operate with improved efficiency.
102 TAILQ_FOREACH(tmph
, &helpers
, h_next
) {
103 if (tmph
->h_id
< h
->h_id
) {
104 TAILQ_INSERT_BEFORE(tmph
, h
, h_next
);
111 TAILQ_INSERT_TAIL(&helpers
, h
, h_next
);
112 KHELP_LIST_WUNLOCK();
119 khelp_deregister_helper(struct helper
*h
)
125 if (h
->h_refcount
> 0)
129 TAILQ_FOREACH(tmph
, &helpers
, h_next
) {
131 TAILQ_REMOVE(&helpers
, h
, h_next
);
137 KHELP_LIST_WUNLOCK();
140 for (i
= 0; i
< h
->h_nhooks
; i
++)
141 hhook_remove_hook_lookup(&h
->h_hooks
[i
]);
142 osd_deregister(OSD_KHELP
, h
->h_id
);
149 khelp_init_osd(uint32_t classes
, struct osd
*hosd
)
155 KASSERT(hosd
!= NULL
, ("struct osd not initialised!"));
160 TAILQ_FOREACH(h
, &helpers
, h_next
) {
161 /* If helper is correct class and needs to store OSD... */
162 if (h
->h_classes
& classes
&& h
->h_flags
& HELPER_NEEDS_OSD
) {
163 hdata
= uma_zalloc(h
->h_zone
, M_NOWAIT
);
168 osd_set(OSD_KHELP
, hosd
, h
->h_id
, hdata
);
169 refcount_acquire(&h
->h_refcount
);
174 /* Delete OSD that was assigned prior to the error. */
175 TAILQ_FOREACH(h
, &helpers
, h_next
) {
176 if (h
->h_classes
& classes
)
177 khelp_remove_osd(h
, hosd
);
180 KHELP_LIST_RUNLOCK();
186 khelp_destroy_osd(struct osd
*hosd
)
191 KASSERT(hosd
!= NULL
, ("struct osd not initialised!"));
197 * Clean up all khelp related OSD.
199 * XXXLAS: Would be nice to use something like osd_exit() here but it
200 * doesn't have the right semantics for this purpose.
202 TAILQ_FOREACH(h
, &helpers
, h_next
)
203 khelp_remove_osd(h
, hosd
);
204 KHELP_LIST_RUNLOCK();
210 khelp_remove_osd(struct helper
*h
, struct osd
*hosd
)
214 if (h
->h_flags
& HELPER_NEEDS_OSD
) {
216 * If the current helper uses OSD and calling osd_get()
217 * on the helper's h_id returns non-NULL, the helper has
218 * OSD attached to 'hosd' which needs to be cleaned up.
220 hdata
= osd_get(OSD_KHELP
, hosd
, h
->h_id
);
222 uma_zfree(h
->h_zone
, hdata
);
223 osd_del(OSD_KHELP
, hosd
, h
->h_id
);
224 refcount_release(&h
->h_refcount
);
230 khelp_get_osd(struct osd
*hosd
, int32_t id
)
233 return (osd_get(OSD_KHELP
, hosd
, id
));
237 khelp_get_id(char *hname
)
245 TAILQ_FOREACH(h
, &helpers
, h_next
) {
246 if (strncmp(h
->h_name
, hname
, HELPER_NAME_MAXLEN
) == 0) {
251 KHELP_LIST_RUNLOCK();
257 khelp_add_hhook(struct hookinfo
*hki
, uint32_t flags
)
262 * XXXLAS: Should probably include the functionality to update the
263 * helper's h_hooks struct member.
265 error
= hhook_add_hook_lookup(hki
, flags
);
271 khelp_remove_hhook(struct hookinfo
*hki
)
276 * XXXLAS: Should probably include the functionality to update the
277 * helper's h_hooks struct member.
279 error
= hhook_remove_hook_lookup(hki
);
285 * Private KPI between hhook and khelp that allows khelp modules to insert hook
286 * functions into hhook points which register after the modules were loaded.
289 khelp_new_hhook_registered(struct hhook_head
*hhh
, uint32_t flags
)
295 TAILQ_FOREACH(h
, &helpers
, h_next
) {
296 for (i
= 0; i
< h
->h_nhooks
; i
++) {
297 if (hhh
->hhh_type
!= h
->h_hooks
[i
].hook_type
||
298 hhh
->hhh_id
!= h
->h_hooks
[i
].hook_id
)
300 error
= hhook_add_hook(hhh
, &h
->h_hooks
[i
], flags
);
302 printf("%s: \"%s\" khelp module unable to "
303 "hook type %d id %d due to error %d\n",
305 h
->h_hooks
[i
].hook_type
,
306 h
->h_hooks
[i
].hook_id
, error
);
311 KHELP_LIST_RUNLOCK();
315 khelp_modevent(module_t mod
, int event_type
, void *data
)
317 struct khelp_modevent_data
*kmd
;
320 kmd
= (struct khelp_modevent_data
*)data
;
325 if (kmd
->helper
->h_flags
& HELPER_NEEDS_OSD
) {
326 if (kmd
->uma_zsize
<= 0) {
327 printf("Use KHELP_DECLARE_MOD_UMA() instead!\n");
331 kmd
->helper
->h_zone
= uma_zcreate(kmd
->name
,
332 kmd
->uma_zsize
, kmd
->umactor
, kmd
->umadtor
, NULL
,
334 if (kmd
->helper
->h_zone
== NULL
) {
339 strlcpy(kmd
->helper
->h_name
, kmd
->name
, HELPER_NAME_MAXLEN
);
340 kmd
->helper
->h_hooks
= kmd
->hooks
;
341 kmd
->helper
->h_nhooks
= kmd
->nhooks
;
342 if (kmd
->helper
->mod_init
!= NULL
)
343 error
= kmd
->helper
->mod_init();
345 error
= khelp_register_helper(kmd
->helper
);
351 error
= khelp_deregister_helper(kmd
->helper
);
353 if (kmd
->helper
->h_flags
& HELPER_NEEDS_OSD
)
354 uma_zdestroy(kmd
->helper
->h_zone
);
355 if (kmd
->helper
->mod_destroy
!= NULL
)
356 kmd
->helper
->mod_destroy();
357 } else if (error
== ENOENT
)
358 /* Do nothing and allow unload if helper not in list. */
360 else if (error
== EBUSY
)
361 printf("Khelp module \"%s\" can't unload until its "
362 "refcount drops from %d to 0.\n", kmd
->name
,
363 kmd
->helper
->h_refcount
);