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