Home | History | Annotate | Line # | Download | only in sys
      1 /* $NetBSD: module_hook.h,v 1.7 2024/05/12 10:38:03 rillig Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2018 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Paul Goyette
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #ifndef _SYS_MODULE_HOOK_H
     33 #define _SYS_MODULE_HOOK_H
     34 
     35 #include <sys/param.h>	/* for COHERENCY_UNIT, for __cacheline_aligned */
     36 #include <sys/localcount.h>
     37 #include <sys/stdbool.h>
     38 
     39 void module_hook_init(void);
     40 void module_hook_set(bool *, struct localcount *);
     41 void module_hook_unset(bool *, struct localcount *);
     42 bool module_hook_tryenter(bool *, struct localcount *);
     43 void module_hook_exit(struct localcount *);
     44 
     45 /*
     46  * Macros for creating MP-safe vectored function calls, where
     47  * the function implementations are in modules which could be
     48  * unloaded.
     49  */
     50 
     51 #define MODULE_HOOK(hook, type, args)				\
     52 extern struct hook ## _t {					\
     53 	struct localcount	lc;				\
     54 	type			(*f)args;			\
     55 	bool			hooked;				\
     56 } hook __cacheline_aligned;
     57 
     58 /*
     59  * We use pserialize_perform() to issue a memory barrier on the current
     60  * CPU and on all other CPUs so that all prior memory operations on the
     61  * current CPU globally happen before all subsequent memory operations
     62  * on the current CPU, as perceived by any other CPU.
     63  *
     64  * pserialize_perform() might be rather heavy-weight here, but it only
     65  * happens during module loading, and it allows MODULE_HOOK_CALL() to
     66  * work without any other memory barriers.
     67  */
     68 
     69 #define MODULE_HOOK_SET(hook, func)				\
     70 do {								\
     71 	(hook).f = func;					\
     72 	module_hook_set(&(hook).hooked, &(hook).lc);		\
     73 } while (0)
     74 
     75 #define MODULE_HOOK_UNSET(hook)					\
     76 do {								\
     77 	KASSERT((hook).f);					\
     78 	module_hook_unset(&(hook).hooked, &(hook).lc);		\
     79 	(hook).f = NULL;	/* paranoia */			\
     80 } while (0)
     81 
     82 #define MODULE_HOOK_CALL(hook, args, default, retval)		\
     83 do {								\
     84 	if (module_hook_tryenter(&(hook).hooked, &(hook).lc)) {	\
     85 		(retval) = (*(hook).f)args;			\
     86 		module_hook_exit(&(hook).lc);			\
     87 	} else {						\
     88 		(retval) = (default);				\
     89 	}							\
     90 } while (0)
     91 
     92 #define MODULE_HOOK_CALL_VOID(hook, args, default)		\
     93 do {								\
     94 	if (module_hook_tryenter(&(hook).hooked, &(hook).lc)) {	\
     95 		(*(hook).f)args;				\
     96 		module_hook_exit(&(hook).lc);			\
     97 	} else {						\
     98 		default;					\
     99 	}							\
    100 } while (0)
    101 
    102 #endif	/* _SYS_MODULE_HOOK_H */
    103