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