subr_fault.c revision 1.1 1 /* $NetBSD: subr_fault.c,v 1.1 2020/06/07 09:45:19 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.1 2020/06/07 09:45:19 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 unsigned long nth;
52 volatile unsigned long cnt;
53 volatile unsigned long nfaults;
54 } fault_t;
55
56 static fault_t fault_global __cacheline_aligned = {
57 .enabled = false,
58 .nth = 2,
59 .cnt = 0,
60 .nfaults = 0
61 };
62
63 static kmutex_t fault_global_lock __cacheline_aligned;
64 static specificdata_key_t fault_lwp_key;
65
66 /* -------------------------------------------------------------------------- */
67
68 bool
69 fault_inject(void)
70 {
71 volatile unsigned long cnt;
72 fault_t *f;
73
74 if (__predict_false(cold))
75 return false;
76
77 if (__predict_false(atomic_load_acquire(&fault_global.enabled))) {
78 f = &fault_global;
79 } else {
80 f = lwp_getspecific(fault_lwp_key);
81 if (__predict_true(f == NULL))
82 return false;
83 if (__predict_false(!f->enabled))
84 return false;
85 }
86
87 cnt = atomic_inc_ulong_nv(&f->cnt);
88 if (__predict_false(cnt % atomic_load_relaxed(&f->nth) == 0)) {
89 atomic_inc_ulong(&f->nfaults);
90 return true;
91 }
92
93 return false;
94 }
95
96 /* -------------------------------------------------------------------------- */
97
98 static int
99 fault_open(dev_t dev, int flag, int mode, struct lwp *l)
100 {
101 return 0;
102 }
103
104 static int
105 fault_close(dev_t dev, int flag, int mode, struct lwp *l)
106 {
107 return 0;
108 }
109
110 static int
111 fault_ioc_enable(struct fault_ioc_enable *args)
112 {
113 fault_t *f;
114
115 if (args->mode != FAULT_MODE_NTH)
116 return EINVAL;
117 if (args->nth < 2)
118 return EINVAL;
119
120 switch (args->scope) {
121 case FAULT_SCOPE_GLOBAL:
122 mutex_enter(&fault_global_lock);
123 if (fault_global.enabled) {
124 mutex_exit(&fault_global_lock);
125 return EEXIST;
126 }
127 atomic_store_relaxed(&fault_global.nth, args->nth);
128 fault_global.cnt = 0;
129 fault_global.nfaults = 0;
130 atomic_store_release(&fault_global.enabled, true);
131 mutex_exit(&fault_global_lock);
132 break;
133 case FAULT_SCOPE_LWP:
134 f = lwp_getspecific(fault_lwp_key);
135 if (f != NULL) {
136 if (f->enabled)
137 return EEXIST;
138 } else {
139 f = kmem_zalloc(sizeof(*f), KM_SLEEP);
140 lwp_setspecific(fault_lwp_key, f);
141 }
142 atomic_store_relaxed(&f->nth, args->nth);
143 f->cnt = 0;
144 f->nfaults = 0;
145 atomic_store_release(&f->enabled, true);
146 break;
147 default:
148 return EINVAL;
149 }
150
151 return 0;
152 }
153
154 static int
155 fault_ioc_disable(struct fault_ioc_disable *args)
156 {
157 fault_t *f;
158
159 switch (args->scope) {
160 case FAULT_SCOPE_GLOBAL:
161 mutex_enter(&fault_global_lock);
162 if (!fault_global.enabled) {
163 mutex_exit(&fault_global_lock);
164 return ENOENT;
165 }
166 atomic_store_release(&fault_global.enabled, false);
167 mutex_exit(&fault_global_lock);
168 break;
169 case FAULT_SCOPE_LWP:
170 f = lwp_getspecific(fault_lwp_key);
171 if (f == NULL)
172 return ENOENT;
173 if (!f->enabled)
174 return ENOENT;
175 atomic_store_release(&f->enabled, false);
176 break;
177 default:
178 return EINVAL;
179 }
180
181 return 0;
182 }
183
184 static int
185 fault_ioc_getinfo(struct fault_ioc_getinfo *args)
186 {
187 fault_t *f;
188
189 switch (args->scope) {
190 case FAULT_SCOPE_GLOBAL:
191 args->nfaults = atomic_load_relaxed(&fault_global.nfaults);
192 break;
193 case FAULT_SCOPE_LWP:
194 f = lwp_getspecific(fault_lwp_key);
195 if (f == NULL)
196 return ENOENT;
197 args->nfaults = atomic_load_relaxed(&f->nfaults);
198 break;
199 default:
200 return EINVAL;
201 }
202
203 return 0;
204 }
205
206 static int
207 fault_ioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
208 {
209 switch (cmd) {
210 case FAULT_IOC_ENABLE:
211 return fault_ioc_enable(addr);
212 case FAULT_IOC_DISABLE:
213 return fault_ioc_disable(addr);
214 case FAULT_IOC_GETINFO:
215 return fault_ioc_getinfo(addr);
216 default:
217 return EINVAL;
218 }
219 }
220
221 const struct cdevsw fault_cdevsw = {
222 .d_open = fault_open,
223 .d_close = fault_close,
224 .d_read = noread,
225 .d_write = nowrite,
226 .d_ioctl = fault_ioctl,
227 .d_stop = nostop,
228 .d_tty = notty,
229 .d_poll = nopoll,
230 .d_mmap = nommap,
231 .d_kqfilter = nokqfilter,
232 .d_discard = nodiscard,
233 .d_flag = D_OTHER | D_MPSAFE
234 };
235
236 /* -------------------------------------------------------------------------- */
237
238 MODULE(MODULE_CLASS_MISC, fault, NULL);
239
240 static void
241 fault_lwp_free(void *arg)
242 {
243 fault_t *f = (fault_t *)arg;
244
245 if (f == NULL) {
246 return;
247 }
248
249 kmem_free(f, sizeof(*f));
250 }
251
252 static void
253 fault_init(void)
254 {
255 mutex_init(&fault_global_lock, MUTEX_DEFAULT, IPL_NONE);
256 lwp_specific_key_create(&fault_lwp_key, fault_lwp_free);
257 }
258
259 static int
260 fault_modcmd(modcmd_t cmd, void *arg)
261 {
262 switch (cmd) {
263 case MODULE_CMD_INIT:
264 fault_init();
265 return 0;
266 case MODULE_CMD_FINI:
267 return EINVAL;
268 default:
269 return ENOTTY;
270 }
271 }
272