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 49typedef struct { 50 volatile bool enabled; 51 volatile unsigned long nth; 52 volatile unsigned long cnt; 53 volatile unsigned long nfaults; 54} fault_t; 55 56static fault_t fault_global __cacheline_aligned = { 57 .enabled = false, 58 .nth = 2, 59 .cnt = 0, 60 .nfaults = 0 61}; 62 63static kmutex_t fault_global_lock __cacheline_aligned; 64static specificdata_key_t fault_lwp_key; 65 66/* -------------------------------------------------------------------------- */ 67 68bool 69fault_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 98static int 99fault_open(dev_t dev, int flag, int mode, struct lwp *l) 100{ 101 return 0; 102} 103 104static int 105fault_close(dev_t dev, int flag, int mode, struct lwp *l) 106{ 107 return 0; 108} 109 110static int 111fault_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 154static int 155fault_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 184static int 185fault_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 206static int 207fault_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 221const 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 238MODULE(MODULE_CLASS_MISC, fault, NULL); 239 240static void 241fault_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 252static void 253fault_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 259static int 260fault_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