Home | History | Annotate | Line # | Download | only in kern
      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