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