xref: /netbsd-src/sys/kern/kern_hook.c (revision 29964953ba4f69021f808034dc736f4b0b060a71)
1*29964953Shannken /*	$NetBSD: kern_hook.c,v 1.15 2024/01/17 10:18:41 hannken Exp $	*/
204b824efSpooka 
304b824efSpooka /*-
404b824efSpooka  * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 The NetBSD Foundation, Inc.
504b824efSpooka  * All rights reserved.
604b824efSpooka  *
704b824efSpooka  * This code is derived from software contributed to The NetBSD Foundation
804b824efSpooka  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
904b824efSpooka  * NASA Ames Research Center, and by Luke Mewburn.
1004b824efSpooka  *
1104b824efSpooka  * Redistribution and use in source and binary forms, with or without
1204b824efSpooka  * modification, are permitted provided that the following conditions
1304b824efSpooka  * are met:
1404b824efSpooka  * 1. Redistributions of source code must retain the above copyright
1504b824efSpooka  *    notice, this list of conditions and the following disclaimer.
1604b824efSpooka  * 2. Redistributions in binary form must reproduce the above copyright
1704b824efSpooka  *    notice, this list of conditions and the following disclaimer in the
1804b824efSpooka  *    documentation and/or other materials provided with the distribution.
1904b824efSpooka  *
2004b824efSpooka  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2104b824efSpooka  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2204b824efSpooka  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2304b824efSpooka  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2404b824efSpooka  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2504b824efSpooka  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2604b824efSpooka  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2704b824efSpooka  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2804b824efSpooka  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2904b824efSpooka  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3004b824efSpooka  * POSSIBILITY OF SUCH DAMAGE.
3104b824efSpooka  */
3204b824efSpooka 
3304b824efSpooka #include <sys/cdefs.h>
34*29964953Shannken __KERNEL_RCSID(0, "$NetBSD: kern_hook.c,v 1.15 2024/01/17 10:18:41 hannken Exp $");
3504b824efSpooka 
3604b824efSpooka #include <sys/param.h>
3718d5b72eSriastradh 
38e18e8cbcSskrll #include <sys/condvar.h>
3949c38685Sskrll #include <sys/cpu.h>
40476c17bcSmartin #include <sys/device.h>
4118d5b72eSriastradh #include <sys/exec.h>
428085a44fSyamaguchi #include <sys/hook.h>
438085a44fSyamaguchi #include <sys/kmem.h>
44e18e8cbcSskrll #include <sys/malloc.h>
45*29964953Shannken #include <sys/once.h>
46e18e8cbcSskrll #include <sys/rwlock.h>
47e18e8cbcSskrll #include <sys/systm.h>
4804b824efSpooka 
4904b824efSpooka /*
5004b824efSpooka  * A generic linear hook.
5104b824efSpooka  */
5204b824efSpooka struct hook_desc {
5304b824efSpooka 	LIST_ENTRY(hook_desc) hk_list;
5404b824efSpooka 	void	(*hk_fn)(void *);
5504b824efSpooka 	void	*hk_arg;
5604b824efSpooka };
5704b824efSpooka typedef LIST_HEAD(, hook_desc) hook_list_t;
5804b824efSpooka 
598085a44fSyamaguchi enum hook_list_st {
608085a44fSyamaguchi 	HKLIST_IDLE,
618085a44fSyamaguchi 	HKLIST_INUSE,
628085a44fSyamaguchi };
638085a44fSyamaguchi 
648085a44fSyamaguchi struct khook_list {
658085a44fSyamaguchi 	hook_list_t	 hl_list;
668085a44fSyamaguchi 	kmutex_t	 hl_lock;
678085a44fSyamaguchi 	kmutex_t	*hl_cvlock;
688085a44fSyamaguchi 	struct lwp	*hl_lwp;
698085a44fSyamaguchi 	kcondvar_t	 hl_cv;
708085a44fSyamaguchi 	enum hook_list_st
718085a44fSyamaguchi 			 hl_state;
728085a44fSyamaguchi 	khook_t		*hl_active_hk;
738085a44fSyamaguchi 	char		 hl_namebuf[HOOKNAMSIZ];
748085a44fSyamaguchi };
758085a44fSyamaguchi 
7604b824efSpooka int	powerhook_debug = 0;
7704b824efSpooka 
78*29964953Shannken static ONCE_DECL(hook_control);
79*29964953Shannken static krwlock_t exithook_lock;
80*29964953Shannken static krwlock_t forkhook_lock;
81*29964953Shannken 
82*29964953Shannken static int
hook_init(void)83*29964953Shannken hook_init(void)
84*29964953Shannken {
85*29964953Shannken 
86*29964953Shannken 	rw_init(&exithook_lock);
87*29964953Shannken 	rw_init(&forkhook_lock);
88*29964953Shannken 
89*29964953Shannken 	return 0;
90*29964953Shannken }
91*29964953Shannken 
9204b824efSpooka static void *
hook_establish(hook_list_t * list,krwlock_t * lock,void (* fn)(void *),void * arg)93*29964953Shannken hook_establish(hook_list_t *list, krwlock_t *lock,
94*29964953Shannken     void (*fn)(void *), void *arg)
9504b824efSpooka {
9604b824efSpooka 	struct hook_desc *hd;
9704b824efSpooka 
98*29964953Shannken 	RUN_ONCE(&hook_control, hook_init);
9904b824efSpooka 
100*29964953Shannken 	hd = malloc(sizeof(*hd), M_DEVBUF, M_NOWAIT);
101*29964953Shannken 	if (hd != NULL) {
102*29964953Shannken 		if (lock)
103*29964953Shannken 			rw_enter(lock, RW_WRITER);
10404b824efSpooka 		hd->hk_fn = fn;
10504b824efSpooka 		hd->hk_arg = arg;
10604b824efSpooka 		LIST_INSERT_HEAD(list, hd, hk_list);
107*29964953Shannken 		if (lock)
108*29964953Shannken 			rw_exit(lock);
109*29964953Shannken 	}
11004b824efSpooka 
11104b824efSpooka 	return (hd);
11204b824efSpooka }
11304b824efSpooka 
11404b824efSpooka static void
hook_disestablish(hook_list_t * list,krwlock_t * lock,void * vhook)115*29964953Shannken hook_disestablish(hook_list_t *list, krwlock_t *lock, void *vhook)
11604b824efSpooka {
117*29964953Shannken 
118*29964953Shannken 	if (lock)
119*29964953Shannken 		rw_enter(lock, RW_WRITER);
12004b824efSpooka #ifdef DIAGNOSTIC
12104b824efSpooka 	struct hook_desc *hd;
12204b824efSpooka 
12304b824efSpooka 	LIST_FOREACH(hd, list, hk_list) {
12404b824efSpooka                 if (hd == vhook)
12504b824efSpooka 			break;
12604b824efSpooka 	}
12704b824efSpooka 
12804b824efSpooka 	if (hd == NULL)
12904b824efSpooka 		panic("hook_disestablish: hook %p not established", vhook);
13004b824efSpooka #endif
13104b824efSpooka 	LIST_REMOVE((struct hook_desc *)vhook, hk_list);
13204b824efSpooka 	free(vhook, M_DEVBUF);
133*29964953Shannken 	if (lock)
134*29964953Shannken 		rw_exit(lock);
13504b824efSpooka }
13604b824efSpooka 
13704b824efSpooka static void
hook_destroy(hook_list_t * list)13804b824efSpooka hook_destroy(hook_list_t *list)
13904b824efSpooka {
14004b824efSpooka 	struct hook_desc *hd;
14104b824efSpooka 
14204b824efSpooka 	while ((hd = LIST_FIRST(list)) != NULL) {
14304b824efSpooka 		LIST_REMOVE(hd, hk_list);
14404b824efSpooka 		free(hd, M_DEVBUF);
14504b824efSpooka 	}
14604b824efSpooka }
14704b824efSpooka 
14804b824efSpooka static void
hook_proc_run(hook_list_t * list,krwlock_t * lock,struct proc * p)149*29964953Shannken hook_proc_run(hook_list_t *list, krwlock_t *lock, struct proc *p)
15004b824efSpooka {
15104b824efSpooka 	struct hook_desc *hd;
15204b824efSpooka 
153*29964953Shannken 	RUN_ONCE(&hook_control, hook_init);
154*29964953Shannken 
155*29964953Shannken 	if (lock)
156*29964953Shannken 		rw_enter(lock, RW_READER);
157176ada4bSchristos 	LIST_FOREACH(hd, list, hk_list) {
158176ada4bSchristos 		__FPTRCAST(void (*)(struct proc *, void *), *hd->hk_fn)(p,
159176ada4bSchristos 		    hd->hk_arg);
160176ada4bSchristos 	}
161*29964953Shannken 	if (lock)
162*29964953Shannken 		rw_exit(lock);
16304b824efSpooka }
16404b824efSpooka 
16504b824efSpooka /*
16604b824efSpooka  * "Shutdown hook" types, functions, and variables.
16704b824efSpooka  *
16804b824efSpooka  * Should be invoked immediately before the
16904b824efSpooka  * system is halted or rebooted, i.e. after file systems unmounted,
17004b824efSpooka  * after crash dump done, etc.
17104b824efSpooka  *
17204b824efSpooka  * Each shutdown hook is removed from the list before it's run, so that
17304b824efSpooka  * it won't be run again.
17404b824efSpooka  */
17504b824efSpooka 
17604b824efSpooka static hook_list_t shutdownhook_list = LIST_HEAD_INITIALIZER(shutdownhook_list);
17704b824efSpooka 
17804b824efSpooka void *
shutdownhook_establish(void (* fn)(void *),void * arg)17904b824efSpooka shutdownhook_establish(void (*fn)(void *), void *arg)
18004b824efSpooka {
181*29964953Shannken 	return hook_establish(&shutdownhook_list, NULL, fn, arg);
18204b824efSpooka }
18304b824efSpooka 
18404b824efSpooka void
shutdownhook_disestablish(void * vhook)18504b824efSpooka shutdownhook_disestablish(void *vhook)
18604b824efSpooka {
187*29964953Shannken 	hook_disestablish(&shutdownhook_list, NULL, vhook);
18804b824efSpooka }
18904b824efSpooka 
19004b824efSpooka /*
19104b824efSpooka  * Run shutdown hooks.  Should be invoked immediately before the
19204b824efSpooka  * system is halted or rebooted, i.e. after file systems unmounted,
19304b824efSpooka  * after crash dump done, etc.
19404b824efSpooka  *
19504b824efSpooka  * Each shutdown hook is removed from the list before it's run, so that
19604b824efSpooka  * it won't be run again.
19704b824efSpooka  */
19804b824efSpooka void
doshutdownhooks(void)19904b824efSpooka doshutdownhooks(void)
20004b824efSpooka {
20104b824efSpooka 	struct hook_desc *dp;
20204b824efSpooka 
20304b824efSpooka 	while ((dp = LIST_FIRST(&shutdownhook_list)) != NULL) {
20404b824efSpooka 		LIST_REMOVE(dp, hk_list);
20504b824efSpooka 		(*dp->hk_fn)(dp->hk_arg);
20604b824efSpooka #if 0
20704b824efSpooka 		/*
20804b824efSpooka 		 * Don't bother freeing the hook structure,, since we may
20904b824efSpooka 		 * be rebooting because of a memory corruption problem,
21004b824efSpooka 		 * and this might only make things worse.  It doesn't
21104b824efSpooka 		 * matter, anyway, since the system is just about to
21204b824efSpooka 		 * reboot.
21304b824efSpooka 		 */
21404b824efSpooka 		free(dp, M_DEVBUF);
21504b824efSpooka #endif
21604b824efSpooka 	}
21704b824efSpooka }
21804b824efSpooka 
21904b824efSpooka /*
22004b824efSpooka  * "Mountroot hook" types, functions, and variables.
22104b824efSpooka  */
22204b824efSpooka 
22304b824efSpooka static hook_list_t mountroothook_list=LIST_HEAD_INITIALIZER(mountroothook_list);
22404b824efSpooka 
22504b824efSpooka void *
mountroothook_establish(void (* fn)(device_t),device_t dev)22604b824efSpooka mountroothook_establish(void (*fn)(device_t), device_t dev)
22704b824efSpooka {
228*29964953Shannken 	return hook_establish(&mountroothook_list, NULL,
229*29964953Shannken 	    __FPTRCAST(void (*), fn), dev);
23004b824efSpooka }
23104b824efSpooka 
23204b824efSpooka void
mountroothook_disestablish(void * vhook)23304b824efSpooka mountroothook_disestablish(void *vhook)
23404b824efSpooka {
235*29964953Shannken 	hook_disestablish(&mountroothook_list, NULL, vhook);
23604b824efSpooka }
23704b824efSpooka 
23804b824efSpooka void
mountroothook_destroy(void)23904b824efSpooka mountroothook_destroy(void)
24004b824efSpooka {
24104b824efSpooka 	hook_destroy(&mountroothook_list);
24204b824efSpooka }
24304b824efSpooka 
24404b824efSpooka void
domountroothook(device_t therootdev)245cbab9cadSchs domountroothook(device_t therootdev)
24604b824efSpooka {
24704b824efSpooka 	struct hook_desc *hd;
24804b824efSpooka 
24904b824efSpooka 	LIST_FOREACH(hd, &mountroothook_list, hk_list) {
2503cba3248Spooka 		if (hd->hk_arg == therootdev) {
25104b824efSpooka 			(*hd->hk_fn)(hd->hk_arg);
25204b824efSpooka 			return;
25304b824efSpooka 		}
25404b824efSpooka 	}
25504b824efSpooka }
25604b824efSpooka 
25704b824efSpooka static hook_list_t exechook_list = LIST_HEAD_INITIALIZER(exechook_list);
25804b824efSpooka 
25904b824efSpooka void *
exechook_establish(void (* fn)(struct proc *,void *),void * arg)26004b824efSpooka exechook_establish(void (*fn)(struct proc *, void *), void *arg)
26104b824efSpooka {
262*29964953Shannken 	return hook_establish(&exechook_list, &exec_lock,
263*29964953Shannken 		__FPTRCAST(void (*)(void *), fn), arg);
26404b824efSpooka }
26504b824efSpooka 
26604b824efSpooka void
exechook_disestablish(void * vhook)26704b824efSpooka exechook_disestablish(void *vhook)
26804b824efSpooka {
269*29964953Shannken 	hook_disestablish(&exechook_list, &exec_lock, vhook);
27004b824efSpooka }
27104b824efSpooka 
27204b824efSpooka /*
27304b824efSpooka  * Run exec hooks.
27404b824efSpooka  */
27504b824efSpooka void
doexechooks(struct proc * p)27604b824efSpooka doexechooks(struct proc *p)
27704b824efSpooka {
278*29964953Shannken 	KASSERT(rw_lock_held(&exec_lock));
279*29964953Shannken 
280*29964953Shannken 	hook_proc_run(&exechook_list, NULL, p);
28104b824efSpooka }
28204b824efSpooka 
28304b824efSpooka static hook_list_t exithook_list = LIST_HEAD_INITIALIZER(exithook_list);
28404b824efSpooka 
28504b824efSpooka void *
exithook_establish(void (* fn)(struct proc *,void *),void * arg)28604b824efSpooka exithook_establish(void (*fn)(struct proc *, void *), void *arg)
28704b824efSpooka {
28804b824efSpooka 
289*29964953Shannken 	return hook_establish(&exithook_list, &exithook_lock,
290*29964953Shannken 	    __FPTRCAST(void (*)(void *), fn), arg);
29104b824efSpooka }
29204b824efSpooka 
29304b824efSpooka void
exithook_disestablish(void * vhook)29404b824efSpooka exithook_disestablish(void *vhook)
29504b824efSpooka {
29604b824efSpooka 
297*29964953Shannken 	hook_disestablish(&exithook_list, &exithook_lock, vhook);
29804b824efSpooka }
29904b824efSpooka 
30004b824efSpooka /*
30104b824efSpooka  * Run exit hooks.
30204b824efSpooka  */
30304b824efSpooka void
doexithooks(struct proc * p)30404b824efSpooka doexithooks(struct proc *p)
30504b824efSpooka {
306*29964953Shannken 	hook_proc_run(&exithook_list, &exithook_lock, p);
30704b824efSpooka }
30804b824efSpooka 
30904b824efSpooka static hook_list_t forkhook_list = LIST_HEAD_INITIALIZER(forkhook_list);
31004b824efSpooka 
31104b824efSpooka void *
forkhook_establish(void (* fn)(struct proc *,struct proc *))31204b824efSpooka forkhook_establish(void (*fn)(struct proc *, struct proc *))
31304b824efSpooka {
314*29964953Shannken 	return hook_establish(&forkhook_list, &forkhook_lock,
315*29964953Shannken 	    __FPTRCAST(void (*)(void *), fn), NULL);
31604b824efSpooka }
31704b824efSpooka 
31804b824efSpooka void
forkhook_disestablish(void * vhook)31904b824efSpooka forkhook_disestablish(void *vhook)
32004b824efSpooka {
321*29964953Shannken 	hook_disestablish(&forkhook_list, &forkhook_lock, vhook);
32204b824efSpooka }
32304b824efSpooka 
32404b824efSpooka /*
32504b824efSpooka  * Run fork hooks.
32604b824efSpooka  */
32704b824efSpooka void
doforkhooks(struct proc * p2,struct proc * p1)32804b824efSpooka doforkhooks(struct proc *p2, struct proc *p1)
32904b824efSpooka {
33004b824efSpooka 	struct hook_desc *hd;
33104b824efSpooka 
332*29964953Shannken 	RUN_ONCE(&hook_control, hook_init);
333*29964953Shannken 
334*29964953Shannken 	rw_enter(&forkhook_lock, RW_READER);
33504b824efSpooka 	LIST_FOREACH(hd, &forkhook_list, hk_list) {
336176ada4bSchristos 		__FPTRCAST(void (*)(struct proc *, struct proc *), *hd->hk_fn)
33704b824efSpooka 		    (p2, p1);
33804b824efSpooka 	}
339*29964953Shannken 	rw_exit(&forkhook_lock);
34004b824efSpooka }
34104b824efSpooka 
342e66ccad2Smatt static hook_list_t critpollhook_list = LIST_HEAD_INITIALIZER(critpollhook_list);
343e66ccad2Smatt 
344e66ccad2Smatt void *
critpollhook_establish(void (* fn)(void *),void * arg)345e66ccad2Smatt critpollhook_establish(void (*fn)(void *), void *arg)
346e66ccad2Smatt {
347*29964953Shannken 	return hook_establish(&critpollhook_list, NULL, fn, arg);
348e66ccad2Smatt }
349e66ccad2Smatt 
350e66ccad2Smatt void
critpollhook_disestablish(void * vhook)351e66ccad2Smatt critpollhook_disestablish(void *vhook)
352e66ccad2Smatt {
353*29964953Shannken 	hook_disestablish(&critpollhook_list, NULL, vhook);
354e66ccad2Smatt }
355e66ccad2Smatt 
356e66ccad2Smatt /*
357e66ccad2Smatt  * Run critical polling hooks.
358e66ccad2Smatt  */
359e66ccad2Smatt void
docritpollhooks(void)360e66ccad2Smatt docritpollhooks(void)
361e66ccad2Smatt {
362e66ccad2Smatt 	struct hook_desc *hd;
363e66ccad2Smatt 
364e66ccad2Smatt 	LIST_FOREACH(hd, &critpollhook_list, hk_list) {
365e66ccad2Smatt 		(*hd->hk_fn)(hd->hk_arg);
366e66ccad2Smatt 	}
367e66ccad2Smatt }
368e66ccad2Smatt 
36904b824efSpooka /*
37004b824efSpooka  * "Power hook" types, functions, and variables.
37104b824efSpooka  * The list of power hooks is kept ordered with the last registered hook
37204b824efSpooka  * first.
37304b824efSpooka  * When running the hooks on power down the hooks are called in reverse
37404b824efSpooka  * registration order, when powering up in registration order.
37504b824efSpooka  */
37604b824efSpooka struct powerhook_desc {
377471b216bSchristos 	TAILQ_ENTRY(powerhook_desc) sfd_list;
37804b824efSpooka 	void	(*sfd_fn)(int, void *);
37904b824efSpooka 	void	*sfd_arg;
38004b824efSpooka 	char	sfd_name[16];
38104b824efSpooka };
38204b824efSpooka 
383471b216bSchristos static TAILQ_HEAD(powerhook_head, powerhook_desc) powerhook_list =
384471b216bSchristos     TAILQ_HEAD_INITIALIZER(powerhook_list);
38504b824efSpooka 
38604b824efSpooka void *
powerhook_establish(const char * name,void (* fn)(int,void *),void * arg)38704b824efSpooka powerhook_establish(const char *name, void (*fn)(int, void *), void *arg)
38804b824efSpooka {
38904b824efSpooka 	struct powerhook_desc *ndp;
39004b824efSpooka 
39104b824efSpooka 	ndp = (struct powerhook_desc *)
39204b824efSpooka 	    malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT);
39304b824efSpooka 	if (ndp == NULL)
39404b824efSpooka 		return (NULL);
39504b824efSpooka 
39604b824efSpooka 	ndp->sfd_fn = fn;
39704b824efSpooka 	ndp->sfd_arg = arg;
39804b824efSpooka 	strlcpy(ndp->sfd_name, name, sizeof(ndp->sfd_name));
399471b216bSchristos 	TAILQ_INSERT_HEAD(&powerhook_list, ndp, sfd_list);
40004b824efSpooka 
40104b824efSpooka 	aprint_error("%s: WARNING: powerhook_establish is deprecated\n", name);
40204b824efSpooka 	return (ndp);
40304b824efSpooka }
40404b824efSpooka 
40504b824efSpooka void
powerhook_disestablish(void * vhook)40604b824efSpooka powerhook_disestablish(void *vhook)
40704b824efSpooka {
40804b824efSpooka #ifdef DIAGNOSTIC
40904b824efSpooka 	struct powerhook_desc *dp;
41004b824efSpooka 
411471b216bSchristos 	TAILQ_FOREACH(dp, &powerhook_list, sfd_list)
41204b824efSpooka                 if (dp == vhook)
41304b824efSpooka 			goto found;
41404b824efSpooka 	panic("powerhook_disestablish: hook %p not established", vhook);
41504b824efSpooka  found:
41604b824efSpooka #endif
41704b824efSpooka 
418471b216bSchristos 	TAILQ_REMOVE(&powerhook_list, (struct powerhook_desc *)vhook,
41904b824efSpooka 	    sfd_list);
42004b824efSpooka 	free(vhook, M_DEVBUF);
42104b824efSpooka }
42204b824efSpooka 
42304b824efSpooka /*
42404b824efSpooka  * Run power hooks.
42504b824efSpooka  */
42604b824efSpooka void
dopowerhooks(int why)42704b824efSpooka dopowerhooks(int why)
42804b824efSpooka {
42904b824efSpooka 	struct powerhook_desc *dp;
43004b824efSpooka 	const char *why_name;
43104b824efSpooka 	static const char * pwr_names[] = {PWR_NAMES};
43204b824efSpooka 	why_name = why < __arraycount(pwr_names) ? pwr_names[why] : "???";
43304b824efSpooka 
43404b824efSpooka 	if (why == PWR_RESUME || why == PWR_SOFTRESUME) {
435471b216bSchristos 		TAILQ_FOREACH_REVERSE(dp, &powerhook_list, powerhook_head,
436471b216bSchristos 		    sfd_list)
437471b216bSchristos 		{
43804b824efSpooka 			if (powerhook_debug)
43904b824efSpooka 				printf("dopowerhooks %s: %s (%p)\n",
44004b824efSpooka 				    why_name, dp->sfd_name, dp);
44104b824efSpooka 			(*dp->sfd_fn)(why, dp->sfd_arg);
44204b824efSpooka 		}
44304b824efSpooka 	} else {
444471b216bSchristos 		TAILQ_FOREACH(dp, &powerhook_list, sfd_list) {
44504b824efSpooka 			if (powerhook_debug)
44604b824efSpooka 				printf("dopowerhooks %s: %s (%p)\n",
44704b824efSpooka 				    why_name, dp->sfd_name, dp);
44804b824efSpooka 			(*dp->sfd_fn)(why, dp->sfd_arg);
44904b824efSpooka 		}
45004b824efSpooka 	}
45104b824efSpooka 
45204b824efSpooka 	if (powerhook_debug)
45304b824efSpooka 		printf("dopowerhooks: %s done\n", why_name);
45404b824efSpooka }
4558085a44fSyamaguchi 
4568085a44fSyamaguchi /*
4578085a44fSyamaguchi  * A simple linear hook.
4588085a44fSyamaguchi  */
4598085a44fSyamaguchi 
4608085a44fSyamaguchi khook_list_t *
simplehook_create(int ipl,const char * wmsg)4618085a44fSyamaguchi simplehook_create(int ipl, const char *wmsg)
4628085a44fSyamaguchi {
4638085a44fSyamaguchi 	khook_list_t *l;
4648085a44fSyamaguchi 
4658085a44fSyamaguchi 	l = kmem_zalloc(sizeof(*l), KM_SLEEP);
4668085a44fSyamaguchi 
4678085a44fSyamaguchi 	mutex_init(&l->hl_lock, MUTEX_DEFAULT, ipl);
4688085a44fSyamaguchi 	strlcpy(l->hl_namebuf, wmsg, sizeof(l->hl_namebuf));
4698085a44fSyamaguchi 	cv_init(&l->hl_cv, l->hl_namebuf);
4708085a44fSyamaguchi 	LIST_INIT(&l->hl_list);
4718085a44fSyamaguchi 	l->hl_state = HKLIST_IDLE;
4728085a44fSyamaguchi 
4738085a44fSyamaguchi 	return l;
4748085a44fSyamaguchi }
4758085a44fSyamaguchi 
4768085a44fSyamaguchi void
simplehook_destroy(khook_list_t * l)4778085a44fSyamaguchi simplehook_destroy(khook_list_t *l)
4788085a44fSyamaguchi {
4798085a44fSyamaguchi 	struct hook_desc *hd;
4808085a44fSyamaguchi 
4818085a44fSyamaguchi 	KASSERT(l->hl_state == HKLIST_IDLE);
4828085a44fSyamaguchi 
4838085a44fSyamaguchi 	while ((hd = LIST_FIRST(&l->hl_list)) != NULL) {
4848085a44fSyamaguchi 		LIST_REMOVE(hd, hk_list);
4858085a44fSyamaguchi 		kmem_free(hd, sizeof(*hd));
4868085a44fSyamaguchi 	}
4878085a44fSyamaguchi 
4888085a44fSyamaguchi 	cv_destroy(&l->hl_cv);
4898085a44fSyamaguchi 	mutex_destroy(&l->hl_lock);
4908085a44fSyamaguchi 	kmem_free(l, sizeof(*l));
4918085a44fSyamaguchi }
4928085a44fSyamaguchi 
4938085a44fSyamaguchi int
simplehook_dohooks(khook_list_t * l)4948085a44fSyamaguchi simplehook_dohooks(khook_list_t *l)
4958085a44fSyamaguchi {
4968085a44fSyamaguchi 	struct hook_desc *hd, *nexthd;
4978085a44fSyamaguchi 	kmutex_t *cv_lock;
4988085a44fSyamaguchi 	void (*fn)(void *);
4998085a44fSyamaguchi 	void *arg;
5008085a44fSyamaguchi 
5018085a44fSyamaguchi 	mutex_enter(&l->hl_lock);
5028085a44fSyamaguchi 	if (l->hl_state != HKLIST_IDLE) {
5038085a44fSyamaguchi 		mutex_exit(&l->hl_lock);
5048085a44fSyamaguchi 		return EBUSY;
5058085a44fSyamaguchi 	}
5068085a44fSyamaguchi 
5078085a44fSyamaguchi 	/* stop removing hooks */
5088085a44fSyamaguchi 	l->hl_state = HKLIST_INUSE;
5098085a44fSyamaguchi 	l->hl_lwp = curlwp;
5108085a44fSyamaguchi 
5118085a44fSyamaguchi 	LIST_FOREACH(hd, &l->hl_list, hk_list) {
5128085a44fSyamaguchi 		if (hd->hk_fn == NULL)
5138085a44fSyamaguchi 			continue;
5148085a44fSyamaguchi 
5158085a44fSyamaguchi 		fn = hd->hk_fn;
5168085a44fSyamaguchi 		arg = hd->hk_arg;
5178085a44fSyamaguchi 		l->hl_active_hk = hd;
5188085a44fSyamaguchi 		l->hl_cvlock = NULL;
5198085a44fSyamaguchi 
5208085a44fSyamaguchi 		mutex_exit(&l->hl_lock);
5218085a44fSyamaguchi 
5228085a44fSyamaguchi 		/* do callback without l->hl_lock */
5238085a44fSyamaguchi 		(*fn)(arg);
5248085a44fSyamaguchi 
5258085a44fSyamaguchi 		mutex_enter(&l->hl_lock);
5268085a44fSyamaguchi 		l->hl_active_hk = NULL;
5278085a44fSyamaguchi 		cv_lock = l->hl_cvlock;
5288085a44fSyamaguchi 
5298085a44fSyamaguchi 		if (hd->hk_fn == NULL) {
5308085a44fSyamaguchi 			if (cv_lock != NULL) {
5318085a44fSyamaguchi 				mutex_exit(&l->hl_lock);
5328085a44fSyamaguchi 				mutex_enter(cv_lock);
5338085a44fSyamaguchi 			}
5348085a44fSyamaguchi 
5358085a44fSyamaguchi 			cv_broadcast(&l->hl_cv);
5368085a44fSyamaguchi 
5378085a44fSyamaguchi 			if (cv_lock != NULL) {
5388085a44fSyamaguchi 				mutex_exit(cv_lock);
5398085a44fSyamaguchi 				mutex_enter(&l->hl_lock);
5408085a44fSyamaguchi 			}
5418085a44fSyamaguchi 		}
5428085a44fSyamaguchi 	}
5438085a44fSyamaguchi 
5448085a44fSyamaguchi 	/* remove marked node while running hooks */
5458085a44fSyamaguchi 	LIST_FOREACH_SAFE(hd, &l->hl_list, hk_list, nexthd) {
5468085a44fSyamaguchi 		if (hd->hk_fn == NULL) {
5478085a44fSyamaguchi 			LIST_REMOVE(hd, hk_list);
5488085a44fSyamaguchi 			kmem_free(hd, sizeof(*hd));
5498085a44fSyamaguchi 		}
5508085a44fSyamaguchi 	}
5518085a44fSyamaguchi 
5528085a44fSyamaguchi 	l->hl_lwp = NULL;
5538085a44fSyamaguchi 	l->hl_state = HKLIST_IDLE;
5548085a44fSyamaguchi 	mutex_exit(&l->hl_lock);
5558085a44fSyamaguchi 
5568085a44fSyamaguchi 	return 0;
5578085a44fSyamaguchi }
5588085a44fSyamaguchi 
5598085a44fSyamaguchi khook_t *
simplehook_establish(khook_list_t * l,void (* fn)(void *),void * arg)5608085a44fSyamaguchi simplehook_establish(khook_list_t *l, void (*fn)(void *), void *arg)
5618085a44fSyamaguchi {
5628085a44fSyamaguchi 	struct hook_desc *hd;
5638085a44fSyamaguchi 
5648085a44fSyamaguchi 	hd = kmem_zalloc(sizeof(*hd), KM_SLEEP);
5658085a44fSyamaguchi 	hd->hk_fn = fn;
5668085a44fSyamaguchi 	hd->hk_arg = arg;
5678085a44fSyamaguchi 
5688085a44fSyamaguchi 	mutex_enter(&l->hl_lock);
5698085a44fSyamaguchi 	LIST_INSERT_HEAD(&l->hl_list, hd, hk_list);
5708085a44fSyamaguchi 	mutex_exit(&l->hl_lock);
5718085a44fSyamaguchi 
5728085a44fSyamaguchi 	return hd;
5738085a44fSyamaguchi }
5748085a44fSyamaguchi 
5758085a44fSyamaguchi void
simplehook_disestablish(khook_list_t * l,khook_t * hd,kmutex_t * lock)5768085a44fSyamaguchi simplehook_disestablish(khook_list_t *l, khook_t *hd, kmutex_t *lock)
5778085a44fSyamaguchi {
5788085a44fSyamaguchi 	struct hook_desc *hd0 __diagused;
5798085a44fSyamaguchi 	kmutex_t *cv_lock;
5808085a44fSyamaguchi 
5818085a44fSyamaguchi 	KASSERT(lock == NULL || mutex_owned(lock));
5828085a44fSyamaguchi 	mutex_enter(&l->hl_lock);
5838085a44fSyamaguchi 
5848085a44fSyamaguchi #ifdef DIAGNOSTIC
5858085a44fSyamaguchi 	LIST_FOREACH(hd0, &l->hl_list, hk_list) {
5868085a44fSyamaguchi 		if (hd == hd0)
5878085a44fSyamaguchi 			break;
5888085a44fSyamaguchi 	}
5898085a44fSyamaguchi 
5908085a44fSyamaguchi 	if (hd0 == NULL)
5918085a44fSyamaguchi 		panic("hook_disestablish: hook %p not established", hd);
5928085a44fSyamaguchi #endif
5938085a44fSyamaguchi 
594ff23aff6Sandvar 	/* The hook is not referred, remove immediately */
5958085a44fSyamaguchi 	if (l->hl_state == HKLIST_IDLE) {
5968085a44fSyamaguchi 		LIST_REMOVE(hd, hk_list);
5978085a44fSyamaguchi 		kmem_free(hd, sizeof(*hd));
5988085a44fSyamaguchi 		mutex_exit(&l->hl_lock);
5998085a44fSyamaguchi 		return;
6008085a44fSyamaguchi 	}
6018085a44fSyamaguchi 
6028085a44fSyamaguchi 	/* remove callback. hd will be removed in dohooks */
6038085a44fSyamaguchi 	hd->hk_fn = NULL;
6048085a44fSyamaguchi 	hd->hk_arg = NULL;
6058085a44fSyamaguchi 
6068085a44fSyamaguchi 	/* If the hook is running, wait for the completion */
6078085a44fSyamaguchi 	if (l->hl_active_hk == hd &&
6088085a44fSyamaguchi 	    l->hl_lwp != curlwp) {
6098085a44fSyamaguchi 		if (lock != NULL) {
6108085a44fSyamaguchi 			cv_lock = lock;
6118085a44fSyamaguchi 			KASSERT(l->hl_cvlock == NULL);
6128085a44fSyamaguchi 			l->hl_cvlock = lock;
6138085a44fSyamaguchi 			mutex_exit(&l->hl_lock);
6148085a44fSyamaguchi 		} else {
6158085a44fSyamaguchi 			cv_lock = &l->hl_lock;
6168085a44fSyamaguchi 		}
6178085a44fSyamaguchi 
6188085a44fSyamaguchi 		cv_wait(&l->hl_cv, cv_lock);
6198085a44fSyamaguchi 
6208085a44fSyamaguchi 		if (lock == NULL)
6218085a44fSyamaguchi 			mutex_exit(&l->hl_lock);
6228085a44fSyamaguchi 	} else {
6238085a44fSyamaguchi 		mutex_exit(&l->hl_lock);
6248085a44fSyamaguchi 	}
6258085a44fSyamaguchi }
6268085a44fSyamaguchi 
6278085a44fSyamaguchi bool
simplehook_has_hooks(khook_list_t * l)6288085a44fSyamaguchi simplehook_has_hooks(khook_list_t *l)
6298085a44fSyamaguchi {
6308085a44fSyamaguchi 	bool empty;
6318085a44fSyamaguchi 
6328085a44fSyamaguchi 	mutex_enter(&l->hl_lock);
6338085a44fSyamaguchi 	empty = LIST_EMPTY(&l->hl_list);
6348085a44fSyamaguchi 	mutex_exit(&l->hl_lock);
6358085a44fSyamaguchi 
6368085a44fSyamaguchi 	return !empty;
6378085a44fSyamaguchi }
638