Home | History | Annotate | Line # | Download | only in kern
kern_hook.c revision 1.14
      1 /*	$NetBSD: kern_hook.c,v 1.14 2022/10/26 23:21:06 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
      9  * NASA Ames Research Center, and by Luke Mewburn.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 __KERNEL_RCSID(0, "$NetBSD: kern_hook.c,v 1.14 2022/10/26 23:21:06 riastradh Exp $");
     35 
     36 #include <sys/param.h>
     37 
     38 #include <sys/condvar.h>
     39 #include <sys/cpu.h>
     40 #include <sys/device.h>
     41 #include <sys/exec.h>
     42 #include <sys/hook.h>
     43 #include <sys/kmem.h>
     44 #include <sys/malloc.h>
     45 #include <sys/rwlock.h>
     46 #include <sys/systm.h>
     47 
     48 /*
     49  * A generic linear hook.
     50  */
     51 struct hook_desc {
     52 	LIST_ENTRY(hook_desc) hk_list;
     53 	void	(*hk_fn)(void *);
     54 	void	*hk_arg;
     55 };
     56 typedef LIST_HEAD(, hook_desc) hook_list_t;
     57 
     58 enum hook_list_st {
     59 	HKLIST_IDLE,
     60 	HKLIST_INUSE,
     61 };
     62 
     63 struct khook_list {
     64 	hook_list_t	 hl_list;
     65 	kmutex_t	 hl_lock;
     66 	kmutex_t	*hl_cvlock;
     67 	struct lwp	*hl_lwp;
     68 	kcondvar_t	 hl_cv;
     69 	enum hook_list_st
     70 			 hl_state;
     71 	khook_t		*hl_active_hk;
     72 	char		 hl_namebuf[HOOKNAMSIZ];
     73 };
     74 
     75 int	powerhook_debug = 0;
     76 
     77 static void *
     78 hook_establish(hook_list_t *list, void (*fn)(void *), void *arg)
     79 {
     80 	struct hook_desc *hd;
     81 
     82 	hd = malloc(sizeof(*hd), M_DEVBUF, M_NOWAIT);
     83 	if (hd == NULL)
     84 		return (NULL);
     85 
     86 	hd->hk_fn = fn;
     87 	hd->hk_arg = arg;
     88 	LIST_INSERT_HEAD(list, hd, hk_list);
     89 
     90 	return (hd);
     91 }
     92 
     93 static void
     94 hook_disestablish(hook_list_t *list, void *vhook)
     95 {
     96 #ifdef DIAGNOSTIC
     97 	struct hook_desc *hd;
     98 
     99 	LIST_FOREACH(hd, list, hk_list) {
    100                 if (hd == vhook)
    101 			break;
    102 	}
    103 
    104 	if (hd == NULL)
    105 		panic("hook_disestablish: hook %p not established", vhook);
    106 #endif
    107 	LIST_REMOVE((struct hook_desc *)vhook, hk_list);
    108 	free(vhook, M_DEVBUF);
    109 }
    110 
    111 static void
    112 hook_destroy(hook_list_t *list)
    113 {
    114 	struct hook_desc *hd;
    115 
    116 	while ((hd = LIST_FIRST(list)) != NULL) {
    117 		LIST_REMOVE(hd, hk_list);
    118 		free(hd, M_DEVBUF);
    119 	}
    120 }
    121 
    122 static void
    123 hook_proc_run(hook_list_t *list, struct proc *p)
    124 {
    125 	struct hook_desc *hd;
    126 
    127 	LIST_FOREACH(hd, list, hk_list) {
    128 		__FPTRCAST(void (*)(struct proc *, void *), *hd->hk_fn)(p,
    129 		    hd->hk_arg);
    130 	}
    131 }
    132 
    133 /*
    134  * "Shutdown hook" types, functions, and variables.
    135  *
    136  * Should be invoked immediately before the
    137  * system is halted or rebooted, i.e. after file systems unmounted,
    138  * after crash dump done, etc.
    139  *
    140  * Each shutdown hook is removed from the list before it's run, so that
    141  * it won't be run again.
    142  */
    143 
    144 static hook_list_t shutdownhook_list = LIST_HEAD_INITIALIZER(shutdownhook_list);
    145 
    146 void *
    147 shutdownhook_establish(void (*fn)(void *), void *arg)
    148 {
    149 	return hook_establish(&shutdownhook_list, fn, arg);
    150 }
    151 
    152 void
    153 shutdownhook_disestablish(void *vhook)
    154 {
    155 	hook_disestablish(&shutdownhook_list, vhook);
    156 }
    157 
    158 /*
    159  * Run shutdown hooks.  Should be invoked immediately before the
    160  * system is halted or rebooted, i.e. after file systems unmounted,
    161  * after crash dump done, etc.
    162  *
    163  * Each shutdown hook is removed from the list before it's run, so that
    164  * it won't be run again.
    165  */
    166 void
    167 doshutdownhooks(void)
    168 {
    169 	struct hook_desc *dp;
    170 
    171 	while ((dp = LIST_FIRST(&shutdownhook_list)) != NULL) {
    172 		LIST_REMOVE(dp, hk_list);
    173 		(*dp->hk_fn)(dp->hk_arg);
    174 #if 0
    175 		/*
    176 		 * Don't bother freeing the hook structure,, since we may
    177 		 * be rebooting because of a memory corruption problem,
    178 		 * and this might only make things worse.  It doesn't
    179 		 * matter, anyway, since the system is just about to
    180 		 * reboot.
    181 		 */
    182 		free(dp, M_DEVBUF);
    183 #endif
    184 	}
    185 }
    186 
    187 /*
    188  * "Mountroot hook" types, functions, and variables.
    189  */
    190 
    191 static hook_list_t mountroothook_list=LIST_HEAD_INITIALIZER(mountroothook_list);
    192 
    193 void *
    194 mountroothook_establish(void (*fn)(device_t), device_t dev)
    195 {
    196 	return hook_establish(&mountroothook_list, __FPTRCAST(void (*), fn),
    197 	    dev);
    198 }
    199 
    200 void
    201 mountroothook_disestablish(void *vhook)
    202 {
    203 	hook_disestablish(&mountroothook_list, vhook);
    204 }
    205 
    206 void
    207 mountroothook_destroy(void)
    208 {
    209 	hook_destroy(&mountroothook_list);
    210 }
    211 
    212 void
    213 domountroothook(device_t therootdev)
    214 {
    215 	struct hook_desc *hd;
    216 
    217 	LIST_FOREACH(hd, &mountroothook_list, hk_list) {
    218 		if (hd->hk_arg == therootdev) {
    219 			(*hd->hk_fn)(hd->hk_arg);
    220 			return;
    221 		}
    222 	}
    223 }
    224 
    225 static hook_list_t exechook_list = LIST_HEAD_INITIALIZER(exechook_list);
    226 
    227 void *
    228 exechook_establish(void (*fn)(struct proc *, void *), void *arg)
    229 {
    230 	return hook_establish(&exechook_list, __FPTRCAST(void (*)(void *), fn),
    231 	    arg);
    232 }
    233 
    234 void
    235 exechook_disestablish(void *vhook)
    236 {
    237 	hook_disestablish(&exechook_list, vhook);
    238 }
    239 
    240 /*
    241  * Run exec hooks.
    242  */
    243 void
    244 doexechooks(struct proc *p)
    245 {
    246 	hook_proc_run(&exechook_list, p);
    247 }
    248 
    249 static hook_list_t exithook_list = LIST_HEAD_INITIALIZER(exithook_list);
    250 
    251 void *
    252 exithook_establish(void (*fn)(struct proc *, void *), void *arg)
    253 {
    254 	void *rv;
    255 
    256 	rw_enter(&exec_lock, RW_WRITER);
    257 	rv = hook_establish(&exithook_list, __FPTRCAST(void (*)(void *), fn),
    258 	    arg);
    259 	rw_exit(&exec_lock);
    260 	return rv;
    261 }
    262 
    263 void
    264 exithook_disestablish(void *vhook)
    265 {
    266 
    267 	rw_enter(&exec_lock, RW_WRITER);
    268 	hook_disestablish(&exithook_list, vhook);
    269 	rw_exit(&exec_lock);
    270 }
    271 
    272 /*
    273  * Run exit hooks.
    274  */
    275 void
    276 doexithooks(struct proc *p)
    277 {
    278 	hook_proc_run(&exithook_list, p);
    279 }
    280 
    281 static hook_list_t forkhook_list = LIST_HEAD_INITIALIZER(forkhook_list);
    282 
    283 void *
    284 forkhook_establish(void (*fn)(struct proc *, struct proc *))
    285 {
    286 	return hook_establish(&forkhook_list, __FPTRCAST(void (*)(void *), fn),
    287 	    NULL);
    288 }
    289 
    290 void
    291 forkhook_disestablish(void *vhook)
    292 {
    293 	hook_disestablish(&forkhook_list, vhook);
    294 }
    295 
    296 /*
    297  * Run fork hooks.
    298  */
    299 void
    300 doforkhooks(struct proc *p2, struct proc *p1)
    301 {
    302 	struct hook_desc *hd;
    303 
    304 	LIST_FOREACH(hd, &forkhook_list, hk_list) {
    305 		__FPTRCAST(void (*)(struct proc *, struct proc *), *hd->hk_fn)
    306 		    (p2, p1);
    307 	}
    308 }
    309 
    310 static hook_list_t critpollhook_list = LIST_HEAD_INITIALIZER(critpollhook_list);
    311 
    312 void *
    313 critpollhook_establish(void (*fn)(void *), void *arg)
    314 {
    315 	return hook_establish(&critpollhook_list, fn, arg);
    316 }
    317 
    318 void
    319 critpollhook_disestablish(void *vhook)
    320 {
    321 	hook_disestablish(&critpollhook_list, vhook);
    322 }
    323 
    324 /*
    325  * Run critical polling hooks.
    326  */
    327 void
    328 docritpollhooks(void)
    329 {
    330 	struct hook_desc *hd;
    331 
    332 	LIST_FOREACH(hd, &critpollhook_list, hk_list) {
    333 		(*hd->hk_fn)(hd->hk_arg);
    334 	}
    335 }
    336 
    337 /*
    338  * "Power hook" types, functions, and variables.
    339  * The list of power hooks is kept ordered with the last registered hook
    340  * first.
    341  * When running the hooks on power down the hooks are called in reverse
    342  * registration order, when powering up in registration order.
    343  */
    344 struct powerhook_desc {
    345 	TAILQ_ENTRY(powerhook_desc) sfd_list;
    346 	void	(*sfd_fn)(int, void *);
    347 	void	*sfd_arg;
    348 	char	sfd_name[16];
    349 };
    350 
    351 static TAILQ_HEAD(powerhook_head, powerhook_desc) powerhook_list =
    352     TAILQ_HEAD_INITIALIZER(powerhook_list);
    353 
    354 void *
    355 powerhook_establish(const char *name, void (*fn)(int, void *), void *arg)
    356 {
    357 	struct powerhook_desc *ndp;
    358 
    359 	ndp = (struct powerhook_desc *)
    360 	    malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT);
    361 	if (ndp == NULL)
    362 		return (NULL);
    363 
    364 	ndp->sfd_fn = fn;
    365 	ndp->sfd_arg = arg;
    366 	strlcpy(ndp->sfd_name, name, sizeof(ndp->sfd_name));
    367 	TAILQ_INSERT_HEAD(&powerhook_list, ndp, sfd_list);
    368 
    369 	aprint_error("%s: WARNING: powerhook_establish is deprecated\n", name);
    370 	return (ndp);
    371 }
    372 
    373 void
    374 powerhook_disestablish(void *vhook)
    375 {
    376 #ifdef DIAGNOSTIC
    377 	struct powerhook_desc *dp;
    378 
    379 	TAILQ_FOREACH(dp, &powerhook_list, sfd_list)
    380                 if (dp == vhook)
    381 			goto found;
    382 	panic("powerhook_disestablish: hook %p not established", vhook);
    383  found:
    384 #endif
    385 
    386 	TAILQ_REMOVE(&powerhook_list, (struct powerhook_desc *)vhook,
    387 	    sfd_list);
    388 	free(vhook, M_DEVBUF);
    389 }
    390 
    391 /*
    392  * Run power hooks.
    393  */
    394 void
    395 dopowerhooks(int why)
    396 {
    397 	struct powerhook_desc *dp;
    398 	const char *why_name;
    399 	static const char * pwr_names[] = {PWR_NAMES};
    400 	why_name = why < __arraycount(pwr_names) ? pwr_names[why] : "???";
    401 
    402 	if (why == PWR_RESUME || why == PWR_SOFTRESUME) {
    403 		TAILQ_FOREACH_REVERSE(dp, &powerhook_list, powerhook_head,
    404 		    sfd_list)
    405 		{
    406 			if (powerhook_debug)
    407 				printf("dopowerhooks %s: %s (%p)\n",
    408 				    why_name, dp->sfd_name, dp);
    409 			(*dp->sfd_fn)(why, dp->sfd_arg);
    410 		}
    411 	} else {
    412 		TAILQ_FOREACH(dp, &powerhook_list, sfd_list) {
    413 			if (powerhook_debug)
    414 				printf("dopowerhooks %s: %s (%p)\n",
    415 				    why_name, dp->sfd_name, dp);
    416 			(*dp->sfd_fn)(why, dp->sfd_arg);
    417 		}
    418 	}
    419 
    420 	if (powerhook_debug)
    421 		printf("dopowerhooks: %s done\n", why_name);
    422 }
    423 
    424 /*
    425  * A simple linear hook.
    426  */
    427 
    428 khook_list_t *
    429 simplehook_create(int ipl, const char *wmsg)
    430 {
    431 	khook_list_t *l;
    432 
    433 	l = kmem_zalloc(sizeof(*l), KM_SLEEP);
    434 
    435 	mutex_init(&l->hl_lock, MUTEX_DEFAULT, ipl);
    436 	strlcpy(l->hl_namebuf, wmsg, sizeof(l->hl_namebuf));
    437 	cv_init(&l->hl_cv, l->hl_namebuf);
    438 	LIST_INIT(&l->hl_list);
    439 	l->hl_state = HKLIST_IDLE;
    440 
    441 	return l;
    442 }
    443 
    444 void
    445 simplehook_destroy(khook_list_t *l)
    446 {
    447 	struct hook_desc *hd;
    448 
    449 	KASSERT(l->hl_state == HKLIST_IDLE);
    450 
    451 	while ((hd = LIST_FIRST(&l->hl_list)) != NULL) {
    452 		LIST_REMOVE(hd, hk_list);
    453 		kmem_free(hd, sizeof(*hd));
    454 	}
    455 
    456 	cv_destroy(&l->hl_cv);
    457 	mutex_destroy(&l->hl_lock);
    458 	kmem_free(l, sizeof(*l));
    459 }
    460 
    461 int
    462 simplehook_dohooks(khook_list_t *l)
    463 {
    464 	struct hook_desc *hd, *nexthd;
    465 	kmutex_t *cv_lock;
    466 	void (*fn)(void *);
    467 	void *arg;
    468 
    469 	mutex_enter(&l->hl_lock);
    470 	if (l->hl_state != HKLIST_IDLE) {
    471 		mutex_exit(&l->hl_lock);
    472 		return EBUSY;
    473 	}
    474 
    475 	/* stop removing hooks */
    476 	l->hl_state = HKLIST_INUSE;
    477 	l->hl_lwp = curlwp;
    478 
    479 	LIST_FOREACH(hd, &l->hl_list, hk_list) {
    480 		if (hd->hk_fn == NULL)
    481 			continue;
    482 
    483 		fn = hd->hk_fn;
    484 		arg = hd->hk_arg;
    485 		l->hl_active_hk = hd;
    486 		l->hl_cvlock = NULL;
    487 
    488 		mutex_exit(&l->hl_lock);
    489 
    490 		/* do callback without l->hl_lock */
    491 		(*fn)(arg);
    492 
    493 		mutex_enter(&l->hl_lock);
    494 		l->hl_active_hk = NULL;
    495 		cv_lock = l->hl_cvlock;
    496 
    497 		if (hd->hk_fn == NULL) {
    498 			if (cv_lock != NULL) {
    499 				mutex_exit(&l->hl_lock);
    500 				mutex_enter(cv_lock);
    501 			}
    502 
    503 			cv_broadcast(&l->hl_cv);
    504 
    505 			if (cv_lock != NULL) {
    506 				mutex_exit(cv_lock);
    507 				mutex_enter(&l->hl_lock);
    508 			}
    509 		}
    510 	}
    511 
    512 	/* remove marked node while running hooks */
    513 	LIST_FOREACH_SAFE(hd, &l->hl_list, hk_list, nexthd) {
    514 		if (hd->hk_fn == NULL) {
    515 			LIST_REMOVE(hd, hk_list);
    516 			kmem_free(hd, sizeof(*hd));
    517 		}
    518 	}
    519 
    520 	l->hl_lwp = NULL;
    521 	l->hl_state = HKLIST_IDLE;
    522 	mutex_exit(&l->hl_lock);
    523 
    524 	return 0;
    525 }
    526 
    527 khook_t *
    528 simplehook_establish(khook_list_t *l, void (*fn)(void *), void *arg)
    529 {
    530 	struct hook_desc *hd;
    531 
    532 	hd = kmem_zalloc(sizeof(*hd), KM_SLEEP);
    533 	hd->hk_fn = fn;
    534 	hd->hk_arg = arg;
    535 
    536 	mutex_enter(&l->hl_lock);
    537 	LIST_INSERT_HEAD(&l->hl_list, hd, hk_list);
    538 	mutex_exit(&l->hl_lock);
    539 
    540 	return hd;
    541 }
    542 
    543 void
    544 simplehook_disestablish(khook_list_t *l, khook_t *hd, kmutex_t *lock)
    545 {
    546 	struct hook_desc *hd0 __diagused;
    547 	kmutex_t *cv_lock;
    548 
    549 	KASSERT(lock == NULL || mutex_owned(lock));
    550 	mutex_enter(&l->hl_lock);
    551 
    552 #ifdef DIAGNOSTIC
    553 	LIST_FOREACH(hd0, &l->hl_list, hk_list) {
    554 		if (hd == hd0)
    555 			break;
    556 	}
    557 
    558 	if (hd0 == NULL)
    559 		panic("hook_disestablish: hook %p not established", hd);
    560 #endif
    561 
    562 	/* The hook is not referred, remove immediately */
    563 	if (l->hl_state == HKLIST_IDLE) {
    564 		LIST_REMOVE(hd, hk_list);
    565 		kmem_free(hd, sizeof(*hd));
    566 		mutex_exit(&l->hl_lock);
    567 		return;
    568 	}
    569 
    570 	/* remove callback. hd will be removed in dohooks */
    571 	hd->hk_fn = NULL;
    572 	hd->hk_arg = NULL;
    573 
    574 	/* If the hook is running, wait for the completion */
    575 	if (l->hl_active_hk == hd &&
    576 	    l->hl_lwp != curlwp) {
    577 		if (lock != NULL) {
    578 			cv_lock = lock;
    579 			KASSERT(l->hl_cvlock == NULL);
    580 			l->hl_cvlock = lock;
    581 			mutex_exit(&l->hl_lock);
    582 		} else {
    583 			cv_lock = &l->hl_lock;
    584 		}
    585 
    586 		cv_wait(&l->hl_cv, cv_lock);
    587 
    588 		if (lock == NULL)
    589 			mutex_exit(&l->hl_lock);
    590 	} else {
    591 		mutex_exit(&l->hl_lock);
    592 	}
    593 }
    594 
    595 bool
    596 simplehook_has_hooks(khook_list_t *l)
    597 {
    598 	bool empty;
    599 
    600 	mutex_enter(&l->hl_lock);
    601 	empty = LIST_EMPTY(&l->hl_list);
    602 	mutex_exit(&l->hl_lock);
    603 
    604 	return !empty;
    605 }
    606