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