FreeBSD/Linux Kernel Cross Reference
sys/kern/subr_fault.c
1 /* $NetBSD: subr_fault.c,v 1.2 2020/06/30 16:28:17 maxv Exp $ */
2
3 /*
4 * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Maxime Villard.
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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: subr_fault.c,v 1.2 2020/06/30 16:28:17 maxv Exp $");
34
35 #include <sys/module.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39
40 #include <sys/conf.h>
41 #include <sys/types.h>
42 #include <sys/specificdata.h>
43 #include <sys/kmem.h>
44 #include <sys/atomic.h>
45 #include <sys/ioccom.h>
46 #include <sys/lwp.h>
47 #include <sys/fault.h>
48
49 typedef struct {
50 volatile bool enabled;
51 volatile bool oneshot;
52 volatile unsigned long nth;
53 volatile unsigned long cnt;
54 volatile unsigned long nfaults;
55 } fault_t;
56
57 static fault_t fault_global __cacheline_aligned = {
58 .enabled = false,
59 .oneshot = false,
60 .nth = FAULT_NTH_MIN,
61 .cnt = 0,
62 .nfaults = 0
63 };
64
65 static kmutex_t fault_global_lock __cacheline_aligned;
66 static specificdata_key_t fault_lwp_key;
67
68 /* -------------------------------------------------------------------------- */
69
70 bool
71 fault_inject(void)
72 {
73 volatile unsigned long cnt;
74 fault_t *f;
75
76 if (__predict_false(cold))
77 return false;
78
79 if (__predict_false(atomic_load_acquire(&fault_global.enabled))) {
80 f = &fault_global;
81 } else {
82 f = lwp_getspecific(fault_lwp_key);
83 if (__predict_true(f == NULL))
84 return false;
85 if (__predict_false(!f->enabled))
86 return false;
87 }
88
89 if (atomic_load_relaxed(&f->oneshot)) {
90 if (__predict_true(atomic_load_relaxed(&f->nfaults) > 0))
91 return false;
92 }
93
94 cnt = atomic_inc_ulong_nv(&f->cnt);
95 if (__predict_false(cnt % atomic_load_relaxed(&f->nth) == 0)) {
96 atomic_inc_ulong(&f->nfaults);
97 return true;
98 }
99
100 return false;
101 }
102
103 /* -------------------------------------------------------------------------- */
104
105 static int
106 fault_open(dev_t dev, int flag, int mode, struct lwp *l)
107 {
108 return 0;
109 }
110
111 static int
112 fault_close(dev_t dev, int flag, int mode, struct lwp *l)
113 {
114 return 0;
115 }
116
117 static int
118 fault_ioc_enable(struct fault_ioc_enable *args)
119 {
120 fault_t *f;
121
122 if (args->mode != FAULT_MODE_NTH_ONESHOT)
123 return EINVAL;
124 if (args->nth < FAULT_NTH_MIN)
125 return EINVAL;
126
127 switch (args->scope) {
128 case FAULT_SCOPE_GLOBAL:
129 mutex_enter(&fault_global_lock);
130 if (fault_global.enabled) {
131 mutex_exit(&fault_global_lock);
132 return EEXIST;
133 }
134 fault_global.oneshot = true;
135 atomic_store_relaxed(&fault_global.nth, args->nth);
136 fault_global.cnt = 0;
137 fault_global.nfaults = 0;
138 atomic_store_release(&fault_global.enabled, true);
139 mutex_exit(&fault_global_lock);
140 break;
141 case FAULT_SCOPE_LWP:
142 f = lwp_getspecific(fault_lwp_key);
143 if (f != NULL) {
144 if (f->enabled)
145 return EEXIST;
146 } else {
147 f = kmem_zalloc(sizeof(*f), KM_SLEEP);
148 lwp_setspecific(fault_lwp_key, f);
149 }
150 f->oneshot = true;
151 atomic_store_relaxed(&f->nth, args->nth);
152 f->cnt = 0;
153 f->nfaults = 0;
154 atomic_store_release(&f->enabled, true);
155 break;
156 default:
157 return EINVAL;
158 }
159
160 return 0;
161 }
162
163 static int
164 fault_ioc_disable(struct fault_ioc_disable *args)
165 {
166 fault_t *f;
167
168 switch (args->scope) {
169 case FAULT_SCOPE_GLOBAL:
170 mutex_enter(&fault_global_lock);
171 if (!fault_global.enabled) {
172 mutex_exit(&fault_global_lock);
173 return ENOENT;
174 }
175 atomic_store_release(&fault_global.enabled, false);
176 mutex_exit(&fault_global_lock);
177 break;
178 case FAULT_SCOPE_LWP:
179 f = lwp_getspecific(fault_lwp_key);
180 if (f == NULL)
181 return ENOENT;
182 if (!f->enabled)
183 return ENOENT;
184 atomic_store_release(&f->enabled, false);
185 break;
186 default:
187 return EINVAL;
188 }
189
190 return 0;
191 }
192
193 static int
194 fault_ioc_getinfo(struct fault_ioc_getinfo *args)
195 {
196 fault_t *f;
197
198 switch (args->scope) {
199 case FAULT_SCOPE_GLOBAL:
200 args->nfaults = atomic_load_relaxed(&fault_global.nfaults);
201 break;
202 case FAULT_SCOPE_LWP:
203 f = lwp_getspecific(fault_lwp_key);
204 if (f == NULL)
205 return ENOENT;
206 args->nfaults = atomic_load_relaxed(&f->nfaults);
207 break;
208 default:
209 return EINVAL;
210 }
211
212 return 0;
213 }
214
215 static int
216 fault_ioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
217 {
218 switch (cmd) {
219 case FAULT_IOC_ENABLE:
220 return fault_ioc_enable(addr);
221 case FAULT_IOC_DISABLE:
222 return fault_ioc_disable(addr);
223 case FAULT_IOC_GETINFO:
224 return fault_ioc_getinfo(addr);
225 default:
226 return EINVAL;
227 }
228 }
229
230 const struct cdevsw fault_cdevsw = {
231 .d_open = fault_open,
232 .d_close = fault_close,
233 .d_read = noread,
234 .d_write = nowrite,
235 .d_ioctl = fault_ioctl,
236 .d_stop = nostop,
237 .d_tty = notty,
238 .d_poll = nopoll,
239 .d_mmap = nommap,
240 .d_kqfilter = nokqfilter,
241 .d_discard = nodiscard,
242 .d_flag = D_OTHER | D_MPSAFE
243 };
244
245 /* -------------------------------------------------------------------------- */
246
247 MODULE(MODULE_CLASS_MISC, fault, NULL);
248
249 static void
250 fault_lwp_free(void *arg)
251 {
252 fault_t *f = (fault_t *)arg;
253
254 if (f == NULL) {
255 return;
256 }
257
258 kmem_free(f, sizeof(*f));
259 }
260
261 static void
262 fault_init(void)
263 {
264 mutex_init(&fault_global_lock, MUTEX_DEFAULT, IPL_NONE);
265 lwp_specific_key_create(&fault_lwp_key, fault_lwp_free);
266 }
267
268 static int
269 fault_modcmd(modcmd_t cmd, void *arg)
270 {
271 switch (cmd) {
272 case MODULE_CMD_INIT:
273 fault_init();
274 return 0;
275 case MODULE_CMD_FINI:
276 return EINVAL;
277 default:
278 return ENOTTY;
279 }
280 }
Cache object: 324a3a08a72d2d3eadca9c07c151dcb6
|