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