1/* $NetBSD: module_hook.h,v 1.3 2019/03/01 11:06:57 pgoyette 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/mutex.h>
37#include <sys/localcount.h>
38#include <sys/condvar.h>
39#include <sys/pserialize.h>
40#include <sys/atomic.h>
41
42/*
43 * Macros for creating MP-safe vectored function calls, where
44 * the function implementations are in modules which could be
45 * unloaded.
46 */
47
48#define MODULE_HOOK(hook, type, args) \
49extern struct hook ## _t { \
50 kmutex_t mtx; \
51 kcondvar_t cv; \
52 struct localcount lc; \
53 pserialize_t psz; \
54 bool hooked; \
55 type (*f)args; \
56} hook __cacheline_aligned;
57
58#define MODULE_HOOK_SET(hook, waitchan, func) \
59do { \
60 \
61 KASSERT(!hook.hooked); \
62 \
63 hook.psz = pserialize_create(); \
64 mutex_init(&hook.mtx, MUTEX_DEFAULT, IPL_NONE); \
65 cv_init(&hook.cv, waitchan); \
66 localcount_init(&hook.lc); \
67 hook.f = func; \
68 \
69 /* Make sure it's initialized before anyone uses it */ \
70 membar_producer(); \
71 \
72 /* Let them use it */ \
73 hook.hooked = true; \
74} while /* CONSTCOND */ (0)
75
76#define MODULE_HOOK_UNSET(hook) \
77do { \
78 \
79 KASSERT(kernconfig_is_held()); \
80 KASSERT(hook.hooked); \
81 KASSERT(hook.f); \
82 \
83 /* Grab the mutex */ \
84 mutex_enter(&hook.mtx); \
85 \
86 /* Prevent new localcount_acquire calls. */ \
87 hook.hooked = false; \
88 \
89 /* \
90 * Wait for localcount_acquire calls already under way \
91 * to finish. \
92 */ \
93 pserialize_perform(hook.psz); \
94 \
95 /* Wait for existing localcount references to drain. */\
96 localcount_drain(&hook.lc, &hook.cv, &hook.mtx); \
97 \
98 /* Release the mutex and clean up all resources */ \
99 mutex_exit(&hook.mtx); \
100 localcount_fini(&hook.lc); \
101 cv_destroy(&hook.cv); \
102 mutex_destroy(&hook.mtx); \
103 pserialize_destroy(hook.psz); \
104} while /* CONSTCOND */ (0)
105
106#define MODULE_HOOK_CALL(hook, args, default, retval) \
107do { \
108 bool __hooked; \
109 int __hook_s; \
110 \
111 __hook_s = pserialize_read_enter(); \
112 __hooked = hook.hooked; \
113 if (__hooked) { \
114 membar_consumer(); \
115 localcount_acquire(&hook.lc); \
116 } \
117 pserialize_read_exit(__hook_s); \
118 \
119 if (__hooked) { \
120 retval = (*hook.f)args; \
121 localcount_release(&hook.lc, &hook.cv, \
122 &hook.mtx); \
123 } else { \
124 retval = default; \
125 } \
126} while /* CONSTCOND */ (0)
127
128#define MODULE_HOOK_CALL_VOID(hook, args, default) \
129do { \
130 bool __hooked; \
131 int __hook_s; \
132 \
133 __hook_s = pserialize_read_enter(); \
134 __hooked = hook.hooked; \
135 if (__hooked) { \
136 membar_consumer(); \
137 localcount_acquire(&hook.lc); \
138 } \
139 pserialize_read_exit(__hook_s); \
140 \
141 if (__hooked) { \
142 (*hook.f)args; \
143 localcount_release(&hook.lc, &hook.cv, \
144 &hook.mtx); \
145 } else { \
146 default; \
147 } \
148} while /* CONSTCOND */ (0)
149
150#endif /* _SYS_MODULE_HOOK_H */
151