1cfeb7489SZachary Loafman /*- 2cfeb7489SZachary Loafman * Copyright (c) 2009 Isilon Inc http://www.isilon.com/ 3cfeb7489SZachary Loafman * 4cfeb7489SZachary Loafman * Redistribution and use in source and binary forms, with or without 5cfeb7489SZachary Loafman * modification, are permitted provided that the following conditions 6cfeb7489SZachary Loafman * are met: 7cfeb7489SZachary Loafman * 1. Redistributions of source code must retain the above copyright 8cfeb7489SZachary Loafman * notice, this list of conditions and the following disclaimer. 9cfeb7489SZachary Loafman * 2. Redistributions in binary form must reproduce the above copyright 10cfeb7489SZachary Loafman * notice, this list of conditions and the following disclaimer in the 11cfeb7489SZachary Loafman * documentation and/or other materials provided with the distribution. 12cfeb7489SZachary Loafman * 13cfeb7489SZachary Loafman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14cfeb7489SZachary Loafman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15cfeb7489SZachary Loafman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16cfeb7489SZachary Loafman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17cfeb7489SZachary Loafman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18cfeb7489SZachary Loafman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19cfeb7489SZachary Loafman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20cfeb7489SZachary Loafman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21cfeb7489SZachary Loafman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22cfeb7489SZachary Loafman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23cfeb7489SZachary Loafman * SUCH DAMAGE. 24cfeb7489SZachary Loafman */ 25cfeb7489SZachary Loafman /** 26cfeb7489SZachary Loafman * @file 27cfeb7489SZachary Loafman * 28cfeb7489SZachary Loafman * fail(9) Facility. 29cfeb7489SZachary Loafman * 30cfeb7489SZachary Loafman * @ingroup failpoint_private 31cfeb7489SZachary Loafman */ 32cfeb7489SZachary Loafman /** 33cfeb7489SZachary Loafman * @defgroup failpoint fail(9) Facility 34cfeb7489SZachary Loafman * 35cfeb7489SZachary Loafman * Failpoints allow for injecting fake errors into running code on the fly, 36cfeb7489SZachary Loafman * without modifying code or recompiling with flags. Failpoints are always 37cfeb7489SZachary Loafman * present, and are very efficient when disabled. Failpoints are described 38cfeb7489SZachary Loafman * in man fail(9). 39cfeb7489SZachary Loafman */ 40cfeb7489SZachary Loafman /** 41cfeb7489SZachary Loafman * @defgroup failpoint_private Private fail(9) Implementation functions 42cfeb7489SZachary Loafman * 43cfeb7489SZachary Loafman * Private implementations for the actual failpoint code. 44cfeb7489SZachary Loafman * 45cfeb7489SZachary Loafman * @ingroup failpoint 46cfeb7489SZachary Loafman */ 47cfeb7489SZachary Loafman /** 48cfeb7489SZachary Loafman * @addtogroup failpoint_private 49cfeb7489SZachary Loafman * @{ 50cfeb7489SZachary Loafman */ 51cfeb7489SZachary Loafman 52cfeb7489SZachary Loafman #include <sys/cdefs.h> 53cfeb7489SZachary Loafman __FBSDID("$FreeBSD$"); 54cfeb7489SZachary Loafman 55cfeb7489SZachary Loafman #include <sys/errno.h> 56cfeb7489SZachary Loafman #include <sys/fail.h> 57cfeb7489SZachary Loafman #include <sys/kernel.h> 58cfeb7489SZachary Loafman #include <sys/libkern.h> 59cfeb7489SZachary Loafman #include <sys/lock.h> 60cfeb7489SZachary Loafman #include <sys/malloc.h> 61cfeb7489SZachary Loafman #include <sys/mutex.h> 62cfeb7489SZachary Loafman #include <sys/sbuf.h> 63cfeb7489SZachary Loafman 64cfeb7489SZachary Loafman #include <machine/stdarg.h> 65cfeb7489SZachary Loafman 66cfeb7489SZachary Loafman #ifdef ILOG_DEFINE_FOR_FILE 67cfeb7489SZachary Loafman ILOG_DEFINE_FOR_FILE(L_ISI_FAIL_POINT, L_ILOG, fail_point); 68cfeb7489SZachary Loafman #endif 69cfeb7489SZachary Loafman 70cfeb7489SZachary Loafman MALLOC_DEFINE(M_FAIL_POINT, "Fail Points", "fail points system"); 71cfeb7489SZachary Loafman #define fp_free(ptr) free(ptr, M_FAIL_POINT) 72cfeb7489SZachary Loafman #define fp_malloc(size, flags) malloc((size), M_FAIL_POINT, (flags)) 73cfeb7489SZachary Loafman 74cfeb7489SZachary Loafman static struct mtx g_fp_mtx; 75cfeb7489SZachary Loafman MTX_SYSINIT(g_fp_mtx, &g_fp_mtx, "fail point mtx", MTX_DEF); 76cfeb7489SZachary Loafman #define FP_LOCK() mtx_lock(&g_fp_mtx) 77cfeb7489SZachary Loafman #define FP_UNLOCK() mtx_unlock(&g_fp_mtx) 78cfeb7489SZachary Loafman 79cfeb7489SZachary Loafman static inline void 80cfeb7489SZachary Loafman fail_point_sleep(struct fail_point *fp, struct fail_point_entry *ent, 81cfeb7489SZachary Loafman int msecs, enum fail_point_return_code *pret) 82cfeb7489SZachary Loafman { 83cfeb7489SZachary Loafman /* convert from millisecs to ticks, rounding up */ 84cfeb7489SZachary Loafman int timo = ((msecs * hz) + 999) / 1000; 85cfeb7489SZachary Loafman 86cfeb7489SZachary Loafman if (timo) { 87cfeb7489SZachary Loafman if (fp->fp_sleep_fn == NULL) { 88cfeb7489SZachary Loafman msleep(fp, &g_fp_mtx, PWAIT, "failpt", timo); 89cfeb7489SZachary Loafman } else { 90cfeb7489SZachary Loafman timeout(fp->fp_sleep_fn, fp->fp_sleep_arg, timo); 91cfeb7489SZachary Loafman *pret = FAIL_POINT_RC_QUEUED; 92cfeb7489SZachary Loafman } 93cfeb7489SZachary Loafman } 94cfeb7489SZachary Loafman } 95cfeb7489SZachary Loafman 96cfeb7489SZachary Loafman 97cfeb7489SZachary Loafman /** 98cfeb7489SZachary Loafman * Defines stating the equivalent of probablilty one (100%) 99cfeb7489SZachary Loafman */ 100cfeb7489SZachary Loafman enum { 101cfeb7489SZachary Loafman PROB_MAX = 1000000, /* probability between zero and this number */ 102cfeb7489SZachary Loafman PROB_DIGITS = 6, /* number of zero's in above number */ 103cfeb7489SZachary Loafman }; 104cfeb7489SZachary Loafman 105cfeb7489SZachary Loafman static const char *fail_type_strings[] = { 106cfeb7489SZachary Loafman "off", 107cfeb7489SZachary Loafman "panic", 108cfeb7489SZachary Loafman "return", 109cfeb7489SZachary Loafman "break", 110cfeb7489SZachary Loafman "print", 111cfeb7489SZachary Loafman "sleep", 112cfeb7489SZachary Loafman }; 113cfeb7489SZachary Loafman 114cfeb7489SZachary Loafman static char *parse_fail_point(struct fail_point_entries *, char *); 115cfeb7489SZachary Loafman static char *parse_term(struct fail_point_entries *, char *); 116cfeb7489SZachary Loafman static char *parse_number(int *out_units, int *out_decimal, char *); 117cfeb7489SZachary Loafman static char *parse_type(struct fail_point_entry *, char *); 118cfeb7489SZachary Loafman static void free_entry(struct fail_point_entries *, struct fail_point_entry *); 119cfeb7489SZachary Loafman static void clear_entries(struct fail_point_entries *); 120cfeb7489SZachary Loafman 121cfeb7489SZachary Loafman /** 122cfeb7489SZachary Loafman * Initialize a fail_point. The name is formed in a printf-like fashion 123cfeb7489SZachary Loafman * from "fmt" and subsequent arguments. This function is generally used 124cfeb7489SZachary Loafman * for custom failpoints located at odd places in the sysctl tree, and is 125cfeb7489SZachary Loafman * not explicitly needed for standard in-line-declared failpoints. 126cfeb7489SZachary Loafman * 127cfeb7489SZachary Loafman * @ingroup failpoint 128cfeb7489SZachary Loafman */ 129cfeb7489SZachary Loafman void 130cfeb7489SZachary Loafman fail_point_init(struct fail_point *fp, const char *fmt, ...) 131cfeb7489SZachary Loafman { 132cfeb7489SZachary Loafman va_list ap; 133cfeb7489SZachary Loafman char *name; 134cfeb7489SZachary Loafman int n; 135cfeb7489SZachary Loafman 136cfeb7489SZachary Loafman TAILQ_INIT(&fp->fp_entries); 137cfeb7489SZachary Loafman fp->fp_flags = 0; 138cfeb7489SZachary Loafman 139cfeb7489SZachary Loafman /* Figure out the size of the name. */ 140cfeb7489SZachary Loafman va_start(ap, fmt); 141cfeb7489SZachary Loafman n = vsnprintf(NULL, 0, fmt, ap); 142cfeb7489SZachary Loafman va_end(ap); 143cfeb7489SZachary Loafman 144cfeb7489SZachary Loafman /* Allocate the name and fill it in. */ 145cfeb7489SZachary Loafman name = fp_malloc(n + 1, M_WAITOK); 146cfeb7489SZachary Loafman if (name != NULL) { 147cfeb7489SZachary Loafman va_start(ap, fmt); 148cfeb7489SZachary Loafman vsnprintf(name, n + 1, fmt, ap); 149cfeb7489SZachary Loafman va_end(ap); 150cfeb7489SZachary Loafman } 151cfeb7489SZachary Loafman fp->fp_name = name; 152cfeb7489SZachary Loafman fp->fp_flags |= FAIL_POINT_DYNAMIC_NAME; 153cfeb7489SZachary Loafman fp->fp_sleep_fn = NULL; 154cfeb7489SZachary Loafman fp->fp_sleep_arg = NULL; 155cfeb7489SZachary Loafman } 156cfeb7489SZachary Loafman 157cfeb7489SZachary Loafman /** 158cfeb7489SZachary Loafman * Free the resources held by a fail_point. 159cfeb7489SZachary Loafman * 160cfeb7489SZachary Loafman * @ingroup failpoint 161cfeb7489SZachary Loafman */ 162cfeb7489SZachary Loafman void 163cfeb7489SZachary Loafman fail_point_destroy(struct fail_point *fp) 164cfeb7489SZachary Loafman { 165cfeb7489SZachary Loafman struct fail_point_entry *ent; 166cfeb7489SZachary Loafman 167cfeb7489SZachary Loafman if (fp->fp_flags & FAIL_POINT_DYNAMIC_NAME && fp->fp_name != NULL) { 168cfeb7489SZachary Loafman fp_free((void *)(intptr_t)fp->fp_name); 169cfeb7489SZachary Loafman fp->fp_name = NULL; 170cfeb7489SZachary Loafman } 171cfeb7489SZachary Loafman fp->fp_flags = 0; 172cfeb7489SZachary Loafman 173cfeb7489SZachary Loafman while (!TAILQ_EMPTY(&fp->fp_entries)) { 174cfeb7489SZachary Loafman ent = TAILQ_FIRST(&fp->fp_entries); 175cfeb7489SZachary Loafman TAILQ_REMOVE(&fp->fp_entries, ent, fe_entries); 176cfeb7489SZachary Loafman fp_free(ent); 177cfeb7489SZachary Loafman } 178cfeb7489SZachary Loafman } 179cfeb7489SZachary Loafman 180cfeb7489SZachary Loafman /** 181cfeb7489SZachary Loafman * This does the real work of evaluating a fail point. If the fail point tells 182cfeb7489SZachary Loafman * us to return a value, this function returns 1 and fills in 'return_value' 183cfeb7489SZachary Loafman * (return_value is allowed to be null). If the fail point tells us to panic, 184cfeb7489SZachary Loafman * we never return. Otherwise we just return 0 after doing some work, which 185cfeb7489SZachary Loafman * means "keep going". 186cfeb7489SZachary Loafman */ 187cfeb7489SZachary Loafman enum fail_point_return_code 188cfeb7489SZachary Loafman fail_point_eval_nontrivial(struct fail_point *fp, int *return_value) 189cfeb7489SZachary Loafman { 190cfeb7489SZachary Loafman enum fail_point_return_code ret = FAIL_POINT_RC_CONTINUE; 191cfeb7489SZachary Loafman struct fail_point_entry *ent, *next; 192cfeb7489SZachary Loafman int msecs; 193cfeb7489SZachary Loafman 194cfeb7489SZachary Loafman FP_LOCK(); 195cfeb7489SZachary Loafman 196cfeb7489SZachary Loafman ent = TAILQ_FIRST(&fp->fp_entries); 197cfeb7489SZachary Loafman while (ent) { 198cfeb7489SZachary Loafman int cont = 0; /* don't continue by default */ 199cfeb7489SZachary Loafman next = TAILQ_NEXT(ent, fe_entries); 200cfeb7489SZachary Loafman 201cfeb7489SZachary Loafman if (ent->fe_prob < PROB_MAX && 202cfeb7489SZachary Loafman ent->fe_prob < random() % PROB_MAX) { 203cfeb7489SZachary Loafman cont = 1; 204cfeb7489SZachary Loafman goto loop_end; 205cfeb7489SZachary Loafman } 206cfeb7489SZachary Loafman 207cfeb7489SZachary Loafman switch (ent->fe_type) { 208cfeb7489SZachary Loafman case FAIL_POINT_PANIC: 209cfeb7489SZachary Loafman panic("fail point %s panicking", fp->fp_name); 210cfeb7489SZachary Loafman /* NOTREACHED */ 211cfeb7489SZachary Loafman 212cfeb7489SZachary Loafman case FAIL_POINT_RETURN: 213cfeb7489SZachary Loafman if (return_value) 214cfeb7489SZachary Loafman *return_value = ent->fe_arg; 215cfeb7489SZachary Loafman ret = FAIL_POINT_RC_RETURN; 216cfeb7489SZachary Loafman break; 217cfeb7489SZachary Loafman 218cfeb7489SZachary Loafman case FAIL_POINT_BREAK: 219cfeb7489SZachary Loafman printf("fail point %s breaking to debugger\n", fp->fp_name); 220cfeb7489SZachary Loafman breakpoint(); 221cfeb7489SZachary Loafman break; 222cfeb7489SZachary Loafman 223cfeb7489SZachary Loafman case FAIL_POINT_PRINT: 224cfeb7489SZachary Loafman printf("fail point %s executing\n", fp->fp_name); 225cfeb7489SZachary Loafman cont = ent->fe_arg; 226cfeb7489SZachary Loafman break; 227cfeb7489SZachary Loafman 228cfeb7489SZachary Loafman case FAIL_POINT_SLEEP: 229cfeb7489SZachary Loafman /* 230cfeb7489SZachary Loafman * Free the entry now if necessary, since 231cfeb7489SZachary Loafman * we're about to drop the mutex and sleep. 232cfeb7489SZachary Loafman */ 233cfeb7489SZachary Loafman msecs = ent->fe_arg; 234cfeb7489SZachary Loafman if (ent->fe_count > 0 && --ent->fe_count == 0) { 235cfeb7489SZachary Loafman free_entry(&fp->fp_entries, ent); 236cfeb7489SZachary Loafman ent = NULL; 237cfeb7489SZachary Loafman } 238cfeb7489SZachary Loafman 239cfeb7489SZachary Loafman if (msecs) 240cfeb7489SZachary Loafman fail_point_sleep(fp, ent, msecs, &ret); 241cfeb7489SZachary Loafman break; 242cfeb7489SZachary Loafman 243cfeb7489SZachary Loafman default: 244cfeb7489SZachary Loafman break; 245cfeb7489SZachary Loafman } 246cfeb7489SZachary Loafman 247cfeb7489SZachary Loafman if (ent && ent->fe_count > 0 && --ent->fe_count == 0) 248cfeb7489SZachary Loafman free_entry(&fp->fp_entries, ent); 249cfeb7489SZachary Loafman 250cfeb7489SZachary Loafman loop_end: 251cfeb7489SZachary Loafman if (cont) 252cfeb7489SZachary Loafman ent = next; 253cfeb7489SZachary Loafman else 254cfeb7489SZachary Loafman break; 255cfeb7489SZachary Loafman } 256cfeb7489SZachary Loafman 257cfeb7489SZachary Loafman /* Get rid of "off"s at the end. */ 258cfeb7489SZachary Loafman while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) && 259cfeb7489SZachary Loafman ent->fe_type == FAIL_POINT_OFF) 260cfeb7489SZachary Loafman free_entry(&fp->fp_entries, ent); 261cfeb7489SZachary Loafman 262cfeb7489SZachary Loafman FP_UNLOCK(); 263cfeb7489SZachary Loafman 264cfeb7489SZachary Loafman return ret; 265cfeb7489SZachary Loafman } 266cfeb7489SZachary Loafman 267cfeb7489SZachary Loafman /** 268cfeb7489SZachary Loafman * Translate internal fail_point structure into human-readable text. 269cfeb7489SZachary Loafman */ 270cfeb7489SZachary Loafman static void 271cfeb7489SZachary Loafman fail_point_get(struct fail_point *fp, struct sbuf *sb) 272cfeb7489SZachary Loafman { 273cfeb7489SZachary Loafman struct fail_point_entry *ent; 274cfeb7489SZachary Loafman 275cfeb7489SZachary Loafman FP_LOCK(); 276cfeb7489SZachary Loafman 277cfeb7489SZachary Loafman TAILQ_FOREACH(ent, &fp->fp_entries, fe_entries) { 278cfeb7489SZachary Loafman if (ent->fe_prob < PROB_MAX) { 279cfeb7489SZachary Loafman int decimal = ent->fe_prob % (PROB_MAX / 100); 280cfeb7489SZachary Loafman int units = ent->fe_prob / (PROB_MAX / 100); 281cfeb7489SZachary Loafman sbuf_printf(sb, "%d", units); 282cfeb7489SZachary Loafman if (decimal) { 283cfeb7489SZachary Loafman int digits = PROB_DIGITS - 2; 284cfeb7489SZachary Loafman while (!(decimal % 10)) { 285cfeb7489SZachary Loafman digits--; 286cfeb7489SZachary Loafman decimal /= 10; 287cfeb7489SZachary Loafman } 288cfeb7489SZachary Loafman sbuf_printf(sb, ".%0*d", digits, decimal); 289cfeb7489SZachary Loafman } 290cfeb7489SZachary Loafman sbuf_printf(sb, "%%"); 291cfeb7489SZachary Loafman } 292cfeb7489SZachary Loafman if (ent->fe_count > 0) 293cfeb7489SZachary Loafman sbuf_printf(sb, "%d*", ent->fe_count); 294cfeb7489SZachary Loafman sbuf_printf(sb, "%s", fail_type_strings[ent->fe_type]); 295cfeb7489SZachary Loafman if (ent->fe_arg) 296cfeb7489SZachary Loafman sbuf_printf(sb, "(%d)", ent->fe_arg); 297cfeb7489SZachary Loafman if (TAILQ_NEXT(ent, fe_entries)) 298cfeb7489SZachary Loafman sbuf_printf(sb, "->"); 299cfeb7489SZachary Loafman } 300cfeb7489SZachary Loafman if (TAILQ_EMPTY(&fp->fp_entries)) 301cfeb7489SZachary Loafman sbuf_printf(sb, "off"); 302cfeb7489SZachary Loafman 303cfeb7489SZachary Loafman FP_UNLOCK(); 304cfeb7489SZachary Loafman } 305cfeb7489SZachary Loafman 306cfeb7489SZachary Loafman /** 307cfeb7489SZachary Loafman * Set an internal fail_point structure from a human-readable failpoint string 308cfeb7489SZachary Loafman * in a lock-safe manner. 309cfeb7489SZachary Loafman */ 310cfeb7489SZachary Loafman static int 311cfeb7489SZachary Loafman fail_point_set(struct fail_point *fp, char *buf) 312cfeb7489SZachary Loafman { 313cfeb7489SZachary Loafman int error = 0; 314cfeb7489SZachary Loafman struct fail_point_entry *ent, *ent_next; 315cfeb7489SZachary Loafman struct fail_point_entries new_entries; 316cfeb7489SZachary Loafman 317cfeb7489SZachary Loafman /* Parse new entries. */ 318cfeb7489SZachary Loafman TAILQ_INIT(&new_entries); 319cfeb7489SZachary Loafman if (!parse_fail_point(&new_entries, buf)) { 320cfeb7489SZachary Loafman clear_entries(&new_entries); 321cfeb7489SZachary Loafman error = EINVAL; 322cfeb7489SZachary Loafman goto end; 323cfeb7489SZachary Loafman } 324cfeb7489SZachary Loafman 325cfeb7489SZachary Loafman FP_LOCK(); 326cfeb7489SZachary Loafman 327cfeb7489SZachary Loafman /* Move new entries in. */ 328cfeb7489SZachary Loafman TAILQ_SWAP(&fp->fp_entries, &new_entries, fail_point_entry, fe_entries); 329cfeb7489SZachary Loafman clear_entries(&new_entries); 330cfeb7489SZachary Loafman 331cfeb7489SZachary Loafman /* Get rid of useless zero probability entries. */ 332cfeb7489SZachary Loafman TAILQ_FOREACH_SAFE(ent, &fp->fp_entries, fe_entries, ent_next) { 333cfeb7489SZachary Loafman if (ent->fe_prob == 0) 334cfeb7489SZachary Loafman free_entry(&fp->fp_entries, ent); 335cfeb7489SZachary Loafman } 336cfeb7489SZachary Loafman 337cfeb7489SZachary Loafman /* Get rid of "off"s at the end. */ 338cfeb7489SZachary Loafman while ((ent = TAILQ_LAST(&fp->fp_entries, fail_point_entries)) && 339cfeb7489SZachary Loafman ent->fe_type == FAIL_POINT_OFF) 340cfeb7489SZachary Loafman free_entry(&fp->fp_entries, ent); 341cfeb7489SZachary Loafman 342cfeb7489SZachary Loafman FP_UNLOCK(); 343cfeb7489SZachary Loafman 344cfeb7489SZachary Loafman end: 345cfeb7489SZachary Loafman #ifdef IWARNING 346cfeb7489SZachary Loafman if (error) 347cfeb7489SZachary Loafman IWARNING("Failed to set %s (%s) to %s", 348cfeb7489SZachary Loafman fp->fp_name, fp->fp_location, buf); 349cfeb7489SZachary Loafman else 350cfeb7489SZachary Loafman INOTICE("Set %s (%s) to %s", 351cfeb7489SZachary Loafman fp->fp_name, fp->fp_location, buf); 352cfeb7489SZachary Loafman #endif /* IWARNING */ 353cfeb7489SZachary Loafman 354cfeb7489SZachary Loafman return error; 355cfeb7489SZachary Loafman } 356cfeb7489SZachary Loafman 357cfeb7489SZachary Loafman #define MAX_FAIL_POINT_BUF 1023 358cfeb7489SZachary Loafman 359cfeb7489SZachary Loafman /** 360cfeb7489SZachary Loafman * Handle kernel failpoint set/get. 361cfeb7489SZachary Loafman */ 362cfeb7489SZachary Loafman int 363cfeb7489SZachary Loafman fail_point_sysctl(SYSCTL_HANDLER_ARGS) 364cfeb7489SZachary Loafman { 365cfeb7489SZachary Loafman struct fail_point *fp = arg1; 366cfeb7489SZachary Loafman char *buf = NULL; 367cfeb7489SZachary Loafman struct sbuf sb; 368cfeb7489SZachary Loafman int error; 369cfeb7489SZachary Loafman 370cfeb7489SZachary Loafman /* Retrieving */ 371cfeb7489SZachary Loafman sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND); 372cfeb7489SZachary Loafman fail_point_get(fp, &sb); 373cfeb7489SZachary Loafman sbuf_trim(&sb); 374cfeb7489SZachary Loafman sbuf_finish(&sb); 375cfeb7489SZachary Loafman error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb)); 376cfeb7489SZachary Loafman sbuf_delete(&sb); 377cfeb7489SZachary Loafman 378cfeb7489SZachary Loafman /* Setting */ 379cfeb7489SZachary Loafman if (!error && req->newptr) { 380cfeb7489SZachary Loafman if (req->newlen > MAX_FAIL_POINT_BUF) { 381cfeb7489SZachary Loafman error = EINVAL; 382cfeb7489SZachary Loafman goto out; 383cfeb7489SZachary Loafman } 384cfeb7489SZachary Loafman 385cfeb7489SZachary Loafman buf = fp_malloc(req->newlen + 1, M_WAITOK); 386cfeb7489SZachary Loafman 387cfeb7489SZachary Loafman error = SYSCTL_IN(req, buf, req->newlen); 388cfeb7489SZachary Loafman if (error) 389cfeb7489SZachary Loafman goto out; 390cfeb7489SZachary Loafman buf[req->newlen] = '\0'; 391cfeb7489SZachary Loafman 392cfeb7489SZachary Loafman error = fail_point_set(fp, buf); 393cfeb7489SZachary Loafman } 394cfeb7489SZachary Loafman 395cfeb7489SZachary Loafman out: 396cfeb7489SZachary Loafman if (buf) 397cfeb7489SZachary Loafman fp_free(buf); 398cfeb7489SZachary Loafman return error; 399cfeb7489SZachary Loafman } 400cfeb7489SZachary Loafman 401cfeb7489SZachary Loafman /** 402cfeb7489SZachary Loafman * Internal helper function to translate a human-readable failpoint string 403cfeb7489SZachary Loafman * into a internally-parsable fail_point structure. 404cfeb7489SZachary Loafman */ 405cfeb7489SZachary Loafman static char * 406cfeb7489SZachary Loafman parse_fail_point(struct fail_point_entries *ents, char *p) 407cfeb7489SZachary Loafman { 408cfeb7489SZachary Loafman /* <fail_point> :: 409cfeb7489SZachary Loafman * <term> ( "->" <term> )* 410cfeb7489SZachary Loafman */ 411cfeb7489SZachary Loafman if (!(p = parse_term(ents, p))) 412cfeb7489SZachary Loafman return 0; 413cfeb7489SZachary Loafman while (*p) 414cfeb7489SZachary Loafman if (p[0] != '-' || p[1] != '>' || !(p = parse_term(ents, p+2))) 415cfeb7489SZachary Loafman return 0; 416cfeb7489SZachary Loafman return p; 417cfeb7489SZachary Loafman } 418cfeb7489SZachary Loafman 419cfeb7489SZachary Loafman /** 420cfeb7489SZachary Loafman * Internal helper function to parse an individual term from a failpoint. 421cfeb7489SZachary Loafman */ 422cfeb7489SZachary Loafman static char * 423cfeb7489SZachary Loafman parse_term(struct fail_point_entries *ents, char *p) 424cfeb7489SZachary Loafman { 425cfeb7489SZachary Loafman struct fail_point_entry *ent; 426cfeb7489SZachary Loafman 427cfeb7489SZachary Loafman ent = fp_malloc(sizeof *ent, M_WAITOK | M_ZERO); 428cfeb7489SZachary Loafman ent->fe_prob = PROB_MAX; 429cfeb7489SZachary Loafman TAILQ_INSERT_TAIL(ents, ent, fe_entries); 430cfeb7489SZachary Loafman 431cfeb7489SZachary Loafman /* 432cfeb7489SZachary Loafman * <term> :: 433cfeb7489SZachary Loafman * ( (<float> "%") | (<integer> "*" ) )* 434cfeb7489SZachary Loafman * <type> 435cfeb7489SZachary Loafman * [ "(" <integer> ")" ] 436cfeb7489SZachary Loafman */ 437cfeb7489SZachary Loafman 438cfeb7489SZachary Loafman /* ( (<float> "%") | (<integer> "*" ) )* */ 439cfeb7489SZachary Loafman while (('0' <= *p && *p <= '9') || *p == '.') { 440cfeb7489SZachary Loafman int units, decimal; 441cfeb7489SZachary Loafman 442cfeb7489SZachary Loafman if (!(p = parse_number(&units, &decimal, p))) 443cfeb7489SZachary Loafman return 0; 444cfeb7489SZachary Loafman 445cfeb7489SZachary Loafman if (*p == '%') { 446cfeb7489SZachary Loafman if (units > 100) /* prevent overflow early */ 447cfeb7489SZachary Loafman units = 100; 448cfeb7489SZachary Loafman ent->fe_prob = units * (PROB_MAX / 100) + decimal; 449cfeb7489SZachary Loafman if (ent->fe_prob > PROB_MAX) 450cfeb7489SZachary Loafman ent->fe_prob = PROB_MAX; 451cfeb7489SZachary Loafman 452cfeb7489SZachary Loafman } else if (*p == '*') { 453cfeb7489SZachary Loafman if (!units || decimal) 454cfeb7489SZachary Loafman return 0; 455cfeb7489SZachary Loafman ent->fe_count = units;; 456cfeb7489SZachary Loafman 457cfeb7489SZachary Loafman } else { 458cfeb7489SZachary Loafman return 0; 459cfeb7489SZachary Loafman } 460cfeb7489SZachary Loafman 461cfeb7489SZachary Loafman p++; 462cfeb7489SZachary Loafman } 463cfeb7489SZachary Loafman 464cfeb7489SZachary Loafman /* <type> */ 465cfeb7489SZachary Loafman if (!(p = parse_type(ent, p))) 466cfeb7489SZachary Loafman return 0; 467cfeb7489SZachary Loafman if (*p == '\0') 468cfeb7489SZachary Loafman return p; 469cfeb7489SZachary Loafman 470cfeb7489SZachary Loafman /* [ "(" <integer> ")" ] */ 471cfeb7489SZachary Loafman if (*p != '(') 472cfeb7489SZachary Loafman return p; 473cfeb7489SZachary Loafman p++; 474cfeb7489SZachary Loafman if (('0' <= *p && *p <= '9') || *p == '-') 475cfeb7489SZachary Loafman ent->fe_arg = strtol(p, &p, 0); 476cfeb7489SZachary Loafman else 477cfeb7489SZachary Loafman return 0; 478cfeb7489SZachary Loafman if (*p++ != ')') 479cfeb7489SZachary Loafman return 0; 480cfeb7489SZachary Loafman 481cfeb7489SZachary Loafman return p; 482cfeb7489SZachary Loafman } 483cfeb7489SZachary Loafman 484cfeb7489SZachary Loafman /** 485cfeb7489SZachary Loafman * Internal helper function to parse a numeric for a failpoint term. 486cfeb7489SZachary Loafman */ 487cfeb7489SZachary Loafman static char * 488cfeb7489SZachary Loafman parse_number(int *out_units, int *out_decimal, char *p) 489cfeb7489SZachary Loafman { 490cfeb7489SZachary Loafman char *old_p; 491cfeb7489SZachary Loafman 492cfeb7489SZachary Loafman /* 493cfeb7489SZachary Loafman * <number> :: 494cfeb7489SZachary Loafman * <integer> [ "." <integer> ] | 495cfeb7489SZachary Loafman * "." <integer> 496cfeb7489SZachary Loafman */ 497cfeb7489SZachary Loafman 498cfeb7489SZachary Loafman /* whole part */ 499cfeb7489SZachary Loafman old_p = p; 500cfeb7489SZachary Loafman *out_units = strtol(p, &p, 10);; 501cfeb7489SZachary Loafman if (p == old_p && *p != '.') 502cfeb7489SZachary Loafman return 0; 503cfeb7489SZachary Loafman 504cfeb7489SZachary Loafman /* fractional part */ 505cfeb7489SZachary Loafman *out_decimal = 0; 506cfeb7489SZachary Loafman if (*p == '.') { 507cfeb7489SZachary Loafman int digits = 0; 508cfeb7489SZachary Loafman p++; 509cfeb7489SZachary Loafman while ('0' <= *p && *p <= '9') { 510cfeb7489SZachary Loafman int digit = *p - '0'; 511cfeb7489SZachary Loafman if (digits < PROB_DIGITS - 2) 512cfeb7489SZachary Loafman *out_decimal = *out_decimal * 10 + digit; 513cfeb7489SZachary Loafman else if (digits == PROB_DIGITS - 2 && digit >= 5) 514cfeb7489SZachary Loafman (*out_decimal)++; 515cfeb7489SZachary Loafman digits++; 516cfeb7489SZachary Loafman p++; 517cfeb7489SZachary Loafman } 518cfeb7489SZachary Loafman if (!digits) /* need at least one digit after '.' */ 519cfeb7489SZachary Loafman return 0; 520cfeb7489SZachary Loafman while (digits++ < PROB_DIGITS - 2) /* add implicit zeros */ 521cfeb7489SZachary Loafman *out_decimal *= 10; 522cfeb7489SZachary Loafman } 523cfeb7489SZachary Loafman 524cfeb7489SZachary Loafman return p; /* success */ 525cfeb7489SZachary Loafman } 526cfeb7489SZachary Loafman 527cfeb7489SZachary Loafman /** 528cfeb7489SZachary Loafman * Internal helper function to parse an individual type for a failpoint term. 529cfeb7489SZachary Loafman */ 530cfeb7489SZachary Loafman static char * 531cfeb7489SZachary Loafman parse_type(struct fail_point_entry *ent, char *beg) 532cfeb7489SZachary Loafman { 533cfeb7489SZachary Loafman enum fail_point_t type; 534cfeb7489SZachary Loafman char *end = beg; 535cfeb7489SZachary Loafman while ('a' <= *end && *end <= 'z') 536cfeb7489SZachary Loafman end++; 537cfeb7489SZachary Loafman if (beg == end) 538cfeb7489SZachary Loafman return 0; 539cfeb7489SZachary Loafman for (type = FAIL_POINT_OFF; type != FAIL_POINT_INVALID; type++) { 540cfeb7489SZachary Loafman const char *p = fail_type_strings[type]; 541cfeb7489SZachary Loafman const char *q = beg; 542cfeb7489SZachary Loafman while (q < end && *p++ == *q++); 543cfeb7489SZachary Loafman if (q == end && *p == '\0') { 544cfeb7489SZachary Loafman ent->fe_type = type; 545cfeb7489SZachary Loafman return end; 546cfeb7489SZachary Loafman } 547cfeb7489SZachary Loafman } 548cfeb7489SZachary Loafman return 0; 549cfeb7489SZachary Loafman } 550cfeb7489SZachary Loafman 551cfeb7489SZachary Loafman /** 552cfeb7489SZachary Loafman * Internal helper function to free an individual failpoint term. 553cfeb7489SZachary Loafman */ 554cfeb7489SZachary Loafman static void 555cfeb7489SZachary Loafman free_entry(struct fail_point_entries *ents, struct fail_point_entry *ent) 556cfeb7489SZachary Loafman { 557cfeb7489SZachary Loafman TAILQ_REMOVE(ents, ent, fe_entries); 558cfeb7489SZachary Loafman fp_free(ent); 559cfeb7489SZachary Loafman } 560cfeb7489SZachary Loafman 561cfeb7489SZachary Loafman /** 562cfeb7489SZachary Loafman * Internal helper function to clear out all failpoint terms for a single 563cfeb7489SZachary Loafman * failpoint. 564cfeb7489SZachary Loafman */ 565cfeb7489SZachary Loafman static void 566cfeb7489SZachary Loafman clear_entries(struct fail_point_entries *ents) 567cfeb7489SZachary Loafman { 568cfeb7489SZachary Loafman struct fail_point_entry *ent, *ent_next; 569cfeb7489SZachary Loafman TAILQ_FOREACH_SAFE(ent, ents, fe_entries, ent_next) 570cfeb7489SZachary Loafman fp_free(ent); 571cfeb7489SZachary Loafman TAILQ_INIT(ents); 572cfeb7489SZachary Loafman } 573cfeb7489SZachary Loafman 574cfeb7489SZachary Loafman /* The fail point sysctl tree. */ 575cfeb7489SZachary Loafman SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW, 0, "fail points"); 576