1#!/bin/sh 2 3# 4# Copyright (c) 2016 EMC Corp. 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28 29# uma_zalloc_arc() fail point test scenario. 30# Test scenario by Ryan Libby <rlibby@gmail.com>. 31 32# "panic: backing_object 0xfffff8016dd74420 was somehow re-referenced during 33# collapse!" seen. 34# https://people.freebsd.org/~pho/stress/log/uma_zalloc_arg.txt 35 36# Hang seen: 37# https://people.freebsd.org/~pho/stress/log/kostik869.txt 38 39[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 40 41. ../default.cfg 42 43sysctl debug.mnowait_failure.zalloc_whitelist > /dev/null 2>&1 || exit 0 44 45s1=`sysctl -n debug.mnowait_failure.zalloc_whitelist` 46s2=`sysctl -n debug.fail_point.uma_zalloc_arg` 47cleanup() { 48 sysctl debug.mnowait_failure.zalloc_whitelist="$s1" > /dev/null 49 sysctl debug.fail_point.uma_zalloc_arg="$s2" > /dev/null 50} 51trap "cleanup" EXIT INT 52sysctl debug.mnowait_failure.zalloc_whitelist='RADIX NODE' 53sysctl debug.fail_point.uma_zalloc_arg='1%return' 54 55start=`date '+%s'` 56while [ $((`date '+%s'` - start)) -lt 300 ]; do 57 sh -c "echo | cat | cat > /dev/null" 58done 59exit 0 60 61The patch from 62https://github.com/freebsd/freebsd/compare/master...rlibby:mnowait-dbg 63 64diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c 65index 01aff78..9d557a1 100644 66--- a/sys/kern/kern_malloc.c 67+++ b/sys/kern/kern_malloc.c 68@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); 69 70 #include <sys/param.h> 71 #include <sys/systm.h> 72+#include <sys/fail.h> 73 #include <sys/kdb.h> 74 #include <sys/kernel.h> 75 #include <sys/lock.h> 76@@ -472,6 +473,19 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags) 77 } 78 } 79 #endif 80+ if (flags & M_NOWAIT) { 81+ KFAIL_POINT_CODE(DEBUG_FP, malloc, { 82+ if (uma_dbg_nowait_fail_enabled_malloc( 83+ mtp->ks_shortdesc)) { 84+ /* XXX record call stack */ 85+#ifdef MALLOC_MAKE_FAILURES 86+ atomic_add_int(&malloc_failure_count, 1); 87+ t_malloc_fail = time_uptime; 88+#endif 89+ return (NULL); 90+ } 91+ }); 92+ } 93 if (flags & M_WAITOK) 94 KASSERT(curthread->td_intr_nesting_level == 0, 95 ("malloc(M_WAITOK) in interrupt context")); 96diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c 97index 1f57dff..dfa18e6 100644 98--- a/sys/vm/uma_core.c 99+++ b/sys/vm/uma_core.c 100@@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$"); 101 #include <sys/param.h> 102 #include <sys/systm.h> 103 #include <sys/bitset.h> 104+#include <sys/fail.h> 105 #include <sys/kernel.h> 106 #include <sys/types.h> 107 #include <sys/queue.h> 108@@ -2148,6 +2149,23 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags) 109 if (flags & M_WAITOK) { 110 WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, 111 "uma_zalloc_arg: zone \"%s\"", zone->uz_name); 112+ } else { 113+ KFAIL_POINT_CODE(DEBUG_FP, uma_zalloc_arg, { 114+ /* 115+ * XXX hack. Setting the fail point to 0 (default) 116+ * causes it to ignore malloc zones, nonzero causes it 117+ * to inject failures for malloc zones regardless of 118+ * the malloc black/white lists. 119+ */ 120+ if (((zone->uz_flags & UMA_ZONE_MALLOC) == 0 || 121+ RETURN_VALUE != 0) && 122+ uma_dbg_nowait_fail_enabled_zalloc( 123+ zone->uz_name)) { 124+ /* XXX record call stack */ 125+ atomic_add_long(&zone->uz_fails, 1); 126+ return NULL; 127+ } 128+ }); 129 } 130 131 KASSERT(curthread->td_critnest == 0, 132diff --git a/sys/vm/uma_dbg.c b/sys/vm/uma_dbg.c 133index 3fbd29b..bed3130 100644 134--- a/sys/vm/uma_dbg.c 135+++ b/sys/vm/uma_dbg.c 136@@ -42,6 +42,8 @@ __FBSDID("$FreeBSD$"); 137 #include <sys/lock.h> 138 #include <sys/mutex.h> 139 #include <sys/malloc.h> 140+#include <sys/rwlock.h> 141+#include <sys/sysctl.h> 142 143 #include <vm/vm.h> 144 #include <vm/vm_object.h> 145@@ -292,4 +294,143 @@ uma_dbg_free(uma_zone_t zone, uma_slab_t slab, void *item) 146 BIT_CLR_ATOMIC(SLAB_SETSIZE, freei, &slab->us_debugfree); 147 } 148 149+/* XXX explain */ 150+struct rwlock g_uma_dbg_nowait_lock; 151+RW_SYSINIT(uma_dbg_nowait, &g_uma_dbg_nowait_lock, "uma dbg nowait"); 152+ 153+#define NOWAIT_FAIL_LIST_BUFSIZE 4096 154+char malloc_fail_blacklist[NOWAIT_FAIL_LIST_BUFSIZE] = "kobj"; 155+char malloc_fail_whitelist[NOWAIT_FAIL_LIST_BUFSIZE] = ""; 156+char zalloc_fail_blacklist[NOWAIT_FAIL_LIST_BUFSIZE] = 157+ "BUF TRIE,ata_request,sackhole"; 158+char zalloc_fail_whitelist[NOWAIT_FAIL_LIST_BUFSIZE] = ""; 159+ 160+static bool 161+str_in_list(const char *list, char delim, const char *str) 162+{ 163+ const char *b, *e; 164+ size_t blen, slen; 165+ 166+ b = list; 167+ slen = strlen(str); 168+ for (;;) { 169+ e = strchr(b, delim); 170+ blen = e == NULL ? strlen(b) : e - b; 171+ if (blen == slen && strncmp(b, str, slen) == 0) 172+ return true; 173+ if (e == NULL) 174+ break; 175+ b = e + 1; 176+ } 177+ return false; 178+} 179+ 180+static bool 181+uma_dbg_nowait_fail_enabled_internal(const char *blacklist, 182+ const char *whitelist, const char *name) 183+{ 184+ bool fail; 185+ 186+ /* Protect ourselves from the sysctl handlers. */ 187+ rw_rlock(&g_uma_dbg_nowait_lock); 188+ if (whitelist[0] == '\0') 189+ fail = !str_in_list(blacklist, ',', name); 190+ else 191+ fail = str_in_list(whitelist, ',', name); 192+ rw_runlock(&g_uma_dbg_nowait_lock); 193+ 194+ return fail; 195+} 196+ 197+bool 198+uma_dbg_nowait_fail_enabled_malloc(const char *name) 199+{ 200+ return uma_dbg_nowait_fail_enabled_internal(malloc_fail_blacklist, 201+ malloc_fail_whitelist, name); 202+} 203+ 204+bool 205+uma_dbg_nowait_fail_enabled_zalloc(const char *name) 206+{ 207+ return uma_dbg_nowait_fail_enabled_internal(zalloc_fail_blacklist, 208+ zalloc_fail_whitelist, name); 209+} 210+ 211+/* 212+ * XXX provide SYSCTL_STRING_LOCKED / sysctl_string_locked_handler? 213+ * This is basically just a different sysctl_string_handler. This one wraps 214+ * the string manipulation in a lock and in a way that will not cause a sleep 215+ * under that lock. 216+ */ 217+static int 218+sysctl_debug_mnowait_failure_list(SYSCTL_HANDLER_ARGS) 219+{ 220+ char *newbuf = NULL; 221+ int error, newlen; 222+ bool have_lock = false; 223+ 224+ if (req->newptr != NULL) { 225+ newlen = req->newlen - req->newidx; 226+ if (newlen >= arg2) { 227+ error = EINVAL; 228+ goto out; 229+ } 230+ newbuf = malloc(newlen, M_TEMP, M_WAITOK); 231+ error = SYSCTL_IN(req, newbuf, newlen); 232+ if (error != 0) 233+ goto out; 234+ } 235+ 236+ error = sysctl_wire_old_buffer(req, arg2); 237+ if (error != 0) 238+ goto out; 239+ 240+ rw_wlock(&g_uma_dbg_nowait_lock); 241+ have_lock = true; 242+ 243+ error = SYSCTL_OUT(req, arg1, strnlen(arg1, arg2 - 1) + 1); 244+ if (error != 0) 245+ goto out; 246+ 247+ if (newbuf == NULL) 248+ goto out; 249+ 250+ bcopy(newbuf, arg1, newlen); 251+ ((char *)arg1)[newlen] = '\0'; 252+ out: 253+ if (have_lock) 254+ rw_wunlock(&g_uma_dbg_nowait_lock); 255+ free(newbuf, M_TEMP); 256+ return error; 257+} 258+ 259+SYSCTL_NODE(_debug, OID_AUTO, mnowait_failure, CTLFLAG_RW, 0, 260+ "Control of M_NOWAIT memory allocation failure injection."); 261+ 262+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, malloc_blacklist, 263+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, malloc_fail_blacklist, 264+ sizeof(malloc_fail_blacklist), sysctl_debug_mnowait_failure_list, "A", 265+ "With debug.fail_point.malloc and with an empty whitelist, CSV list of " 266+ "zones which remain unaffected."); 267+ 268+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, malloc_whitelist, 269+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, malloc_fail_whitelist, 270+ sizeof(malloc_fail_whitelist), sysctl_debug_mnowait_failure_list, "A", 271+ "With debug.fail_point.malloc, CSV list of zones exclusively affected. " 272+ "With an empty whitelist, all zones but those on the blacklist" 273+ "are affected."); 274+ 275+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, zalloc_blacklist, 276+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, zalloc_fail_blacklist, 277+ sizeof(zalloc_fail_blacklist), sysctl_debug_mnowait_failure_list, "A", 278+ "With debug.fail_point.uma_zalloc_arg and with an empty whitelist, CSV " 279+ "list of zones which remain unaffected."); 280+ 281+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, zalloc_whitelist, 282+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, zalloc_fail_whitelist, 283+ sizeof(zalloc_fail_whitelist), sysctl_debug_mnowait_failure_list, "A", 284+ "With debug.fail_point.uma_zalloc_arg, CSV list of zones exclusively " 285+ "affected. With an empty whitelist, all zones but those on the blacklist" 286+ "are affected."); 287+ 288 #endif /* INVARIANTS */ 289diff --git a/sys/vm/uma_int.h b/sys/vm/uma_int.h 290index ad2a405..284747f 100644 291--- a/sys/vm/uma_int.h 292+++ b/sys/vm/uma_int.h 293@@ -427,6 +427,9 @@ vsetslab(vm_offset_t va, uma_slab_t slab) 294 void *uma_small_alloc(uma_zone_t zone, vm_size_t bytes, uint8_t *pflag, 295 int wait); 296 void uma_small_free(void *mem, vm_size_t size, uint8_t flags); 297+ 298+bool uma_dbg_nowait_fail_enabled_malloc(const char *name); 299+bool uma_dbg_nowait_fail_enabled_zalloc(const char *name); 300 #endif /* _KERNEL */ 301 302 #endif /* VM_UMA_INT_H */ 303