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