Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F103227593
D20714.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D20714.id.diff
View Options
Index: sys/kern/kern_malloc.c
===================================================================
--- sys/kern/kern_malloc.c
+++ sys/kern/kern_malloc.c
@@ -53,6 +53,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/fail.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@@ -230,16 +231,8 @@
"Kernel malloc debugging options");
#endif
-/*
- * malloc(9) fault injection -- cause malloc failures every (n) mallocs when
- * the caller specifies M_NOWAIT. If set to 0, no failures are caused.
- */
#ifdef MALLOC_MAKE_FAILURES
-static int malloc_failure_rate;
-static int malloc_nowait_count;
static int malloc_failure_count;
-SYSCTL_INT(_debug_malloc, OID_AUTO, failure_rate, CTLFLAG_RWTUN,
- &malloc_failure_rate, 0, "Every (n) mallocs with M_NOWAIT will fail");
SYSCTL_INT(_debug_malloc, OID_AUTO, failure_count, CTLFLAG_RD,
&malloc_failure_count, 0, "Number of imposed M_NOWAIT malloc failures");
#endif
@@ -505,17 +498,6 @@
once++;
}
}
-#endif
-#ifdef MALLOC_MAKE_FAILURES
- if ((flags & M_NOWAIT) && (malloc_failure_rate != 0)) {
- atomic_add_int(&malloc_nowait_count, 1);
- if ((malloc_nowait_count % malloc_failure_rate) == 0) {
- atomic_add_int(&malloc_failure_count, 1);
- t_malloc_fail = time_uptime;
- *vap = NULL;
- return (EJUSTRETURN);
- }
- }
#endif
if (flags & M_WAITOK) {
KASSERT(curthread->td_intr_nesting_level == 0,
@@ -543,6 +525,22 @@
}
#endif
+#ifdef MALLOC_MAKE_FAILURES
+static __noinline bool
+malloc_inject_failure(struct malloc_type *mtp)
+{
+
+ if (!uma_dbg_nowait_fail_enabled(mtp->ks_shortdesc))
+ return (false);
+
+ atomic_add_int(&malloc_failure_count, 1);
+ uma_dbg_nowait_fail_record(mtp->ks_shortdesc);
+ t_malloc_fail = time_uptime;
+
+ return (true);
+}
+#endif
+
/*
* malloc:
*
@@ -561,6 +559,11 @@
unsigned long osize = size;
#endif
+ MALLOC_NOWAIT_FAIL_POINT(flags,
+ if (malloc_inject_failure(mtp))
+ return (NULL);
+ );
+
#ifdef MALLOC_DEBUG
va = NULL;
if (malloc_dbg(&va, &size, mtp, flags) != 0)
@@ -648,6 +651,11 @@
void *ret;
int domain;
+ MALLOC_NOWAIT_FAIL_POINT(flags,
+ if (malloc_inject_failure(mtp))
+ return (NULL);
+ );
+
vm_domainset_iter_policy_init(&di, ds, &domain, &flags);
do {
ret = malloc_domain(size, mtp, domain, flags);
Index: sys/vm/uma_core.c
===================================================================
--- sys/vm/uma_core.c
+++ sys/vm/uma_core.c
@@ -61,6 +61,7 @@
#include <sys/bitset.h>
#include <sys/domainset.h>
#include <sys/eventhandler.h>
+#include <sys/fail.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/limits.h>
@@ -2335,6 +2336,23 @@
uma_zfree_arg(zone, item, udata);
}
+#ifdef MALLOC_MAKE_FAILURES
+static __noinline bool
+zalloc_inject_failure(uma_zone_t zone)
+{
+
+ if (((zone->uz_flags & UMA_ZONE_MALLOC) != 0 &&
+ g_uma_dbg_nowait_fail_zalloc_ignore_malloc) ||
+ !uma_dbg_nowait_fail_enabled(zone->uz_name))
+ return (false);
+
+ counter_u64_add(zone->uz_fails, 1);
+ uma_dbg_nowait_fail_record(zone->uz_name);
+
+ return (true);
+}
+#endif
+
/* See uma.h */
void *
uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
@@ -2366,6 +2384,11 @@
KASSERT((flags & M_ZERO) == 0, ("allocating from a pcpu zone "
"with M_ZERO passed"));
+ MALLOC_NOWAIT_FAIL_POINT(flags,
+ if (zalloc_inject_failure(zone))
+ return (NULL);
+ );
+
#ifdef DEBUG_MEMGUARD
if (memguard_cmp_zone(zone)) {
item = memguard_alloc(zone->uz_size, flags);
@@ -2604,6 +2627,11 @@
KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
("uma_zalloc_domain: called with spinlock or critical section held"));
+ MALLOC_NOWAIT_FAIL_POINT(flags,
+ if (zalloc_inject_failure(zone))
+ return (NULL);
+ );
+
return (zone_alloc_item(zone, udata, domain, flags));
}
Index: sys/vm/uma_dbg.c
===================================================================
--- sys/vm/uma_dbg.c
+++ sys/vm/uma_dbg.c
@@ -35,6 +35,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_ddb.h"
+#include "opt_stack.h"
#include "opt_vm.h"
#include <sys/param.h>
@@ -46,6 +48,11 @@
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
+#include <sys/sbuf.h>
+#include <sys/stack.h>
+#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
@@ -225,3 +232,254 @@
{
(void)mtrash_ctor(mem, size, NULL, 0);
}
+
+#ifdef MALLOC_MAKE_FAILURES
+/*
+ * Debugging and failure injection for UMA and malloc M_NOWAIT memory
+ * allocations. This code and the hooks in UMA and malloc allow for
+ * injection of failures for specific UMA zones and malloc types and for
+ * tracking of the last failure injected.
+ *
+ * Configuration is done via the sysctls under debug.mnowait_failure.
+ * There is a whitelist and a blacklist containing UMA zone names (see
+ * vmstat -z) and malloc type names (see vmstat -m). If any entries are
+ * present in the whitelist, failure injection will be enabled for only
+ * the zones and malloc types matching the whitelist entries. If the
+ * whitelist is empty, then only blacklist matches will be excluded.
+ * Certain zones and malloc types may be known not to behave well with
+ * with failure injection, and they may be present in the default
+ * blacklist.
+ *
+ * Enabling failure injection is done via the fail point configurable by
+ * sysctl debug.fail_point.mnowait. See fail(9).
+ *
+ * By default, the zalloc failure injection hooks ignore allocations
+ * done for malloc.
+ *
+ * TODO: move above to a man page.
+ */
+
+#if defined(DDB) || defined(STACK)
+#define HAVE_STACK
+#endif
+
+/* Uma Dbg Nowait Failure Globals -> g_udnf_ */
+
+/* Configuration. */
+bool g_uma_dbg_nowait_fail_zalloc_ignore_malloc = true;
+#define NOWAIT_FAIL_LIST_BUFSIZE 1024
+static char g_udnf_whitelist[NOWAIT_FAIL_LIST_BUFSIZE];
+static char g_udnf_blacklist[NOWAIT_FAIL_LIST_BUFSIZE] =
+ "ata_request,"
+ "BUF TRIE,"
+ "ifaddr,"
+ "kobj,"
+ "linker,"
+ "pcb,"
+ "sackhole,"
+ "sctp_ifa,"
+ "sctp_ifn,"
+ "sctp_vrf";
+
+static struct rwlock g_udnf_conf_lock;
+RW_SYSINIT(uma_dbg_nowait_conf, &g_udnf_conf_lock, "uma dbg nowait conf");
+
+/* Tracking. */
+#define NOWAIT_FAIL_NAME_BUFSIZE 80
+static char g_udnf_last_name[NOWAIT_FAIL_NAME_BUFSIZE];
+static char g_udnf_last_comm[MAXCOMLEN + 1];
+static pid_t g_udnf_last_pid;
+static lwpid_t g_udnf_last_tid;
+static int g_udnf_last_ticks;
+
+#ifdef HAVE_STACK
+static struct stack g_udnf_last_stack;
+#endif
+
+static struct mtx g_udnf_track_lock;
+MTX_SYSINIT(uma_dbg_nowait_track, &g_udnf_track_lock, "uma dbg nowait track",
+ 0);
+
+void
+uma_dbg_nowait_fail_record(const char *name)
+{
+#ifdef HAVE_STACK
+ struct stack st = {};
+#endif
+ struct thread *td;
+
+#ifdef HAVE_STACK
+ stack_save(&st);
+#endif
+ td = curthread;
+
+ mtx_lock(&g_udnf_track_lock);
+#ifdef HAVE_STACK
+ stack_copy(&st, &g_udnf_last_stack);
+#endif
+ strlcpy(g_udnf_last_name, name, sizeof(g_udnf_last_name));
+ g_udnf_last_tid = td->td_tid;
+ g_udnf_last_pid = td->td_proc->p_pid;
+ strlcpy(g_udnf_last_comm, td->td_proc->p_comm,
+ sizeof(g_udnf_last_comm));
+ g_udnf_last_ticks = ticks;
+ mtx_unlock(&g_udnf_track_lock);
+}
+
+static int
+sysctl_debug_mnowait_failure_last_injection(SYSCTL_HANDLER_ARGS)
+{
+ char last_name[NOWAIT_FAIL_NAME_BUFSIZE];
+ char last_comm[MAXCOMLEN + 1];
+ struct sbuf sbuf;
+#ifdef HAVE_STACK
+ struct stack last_stack;
+#endif
+ pid_t last_pid;
+ lwpid_t last_tid;
+ u_int delta;
+ int error;
+ int last_ticks;
+
+ mtx_lock(&g_udnf_track_lock);
+#ifdef HAVE_STACK
+ stack_copy(&g_udnf_last_stack, &last_stack);
+#endif
+ strlcpy(last_name, g_udnf_last_name, sizeof(last_name));
+ last_tid = g_udnf_last_tid;
+ last_pid = g_udnf_last_pid;
+ strlcpy(last_comm, g_udnf_last_comm, sizeof(last_comm));
+ last_ticks = g_udnf_last_ticks;
+ mtx_unlock(&g_udnf_track_lock);
+
+ if (last_tid == 0)
+ return (0);
+
+ delta = ticks - last_ticks;
+
+ sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
+ sbuf_printf(&sbuf, "%s[%d] tid %d alloc %s %u.%03u s ago",
+ last_comm, last_pid, last_tid,
+ last_name, delta / hz, (delta % hz) * 1000 / hz);
+#ifdef HAVE_STACK
+ sbuf_putc(&sbuf, '\n');
+ stack_sbuf_print(&sbuf, &last_stack);
+#endif
+ error = sbuf_finish(&sbuf);
+ sbuf_delete(&sbuf);
+
+ return (error);
+}
+
+static bool
+str_in_list(const char *list, char delim, const char *str)
+{
+ const char *b, *e;
+ size_t blen, slen;
+
+ b = list;
+ slen = strlen(str);
+ for (;;) {
+ e = strchr(b, delim);
+ blen = e == NULL ? strlen(b) : e - b;
+ if (blen == slen && strncmp(b, str, slen) == 0)
+ return (true);
+ if (e == NULL)
+ break;
+ b = e + 1;
+ }
+ return (false);
+}
+
+bool
+uma_dbg_nowait_fail_enabled(const char *name)
+{
+ bool fail;
+
+ /* Protect ourselves from the sysctl handlers. */
+ rw_rlock(&g_udnf_conf_lock);
+ if (g_udnf_whitelist[0] == '\0')
+ fail = !str_in_list(g_udnf_blacklist, ',', name);
+ else
+ fail = str_in_list(g_udnf_whitelist, ',', name);
+ rw_runlock(&g_udnf_conf_lock);
+
+ return (fail);
+}
+
+/*
+ * XXX provide SYSCTL_STRING_LOCKED / sysctl_handle_string_locked?
+ * This is basically just a different sysctl_handle_string. This one wraps
+ * the string manipulation in a lock and in a way that will not cause a sleep
+ * under that lock.
+ */
+static int
+sysctl_debug_mnowait_failure_list(SYSCTL_HANDLER_ARGS)
+{
+ char *newbuf = NULL;
+ int error, newlen;
+ bool have_lock = false;
+
+ if (req->newptr != NULL) {
+ newlen = req->newlen - req->newidx;
+ if (newlen >= arg2) {
+ error = EINVAL;
+ goto out;
+ }
+ newbuf = malloc(newlen, M_TEMP, M_WAITOK);
+ error = SYSCTL_IN(req, newbuf, newlen);
+ if (error != 0)
+ goto out;
+ }
+
+ error = sysctl_wire_old_buffer(req, arg2);
+ if (error != 0)
+ goto out;
+
+ rw_wlock(&g_udnf_conf_lock);
+ have_lock = true;
+
+ error = SYSCTL_OUT(req, arg1, strnlen(arg1, arg2 - 1) + 1);
+ if (error != 0)
+ goto out;
+
+ if (newbuf == NULL)
+ goto out;
+
+ bcopy(newbuf, arg1, newlen);
+ ((char *)arg1)[newlen] = '\0';
+ out:
+ if (have_lock)
+ rw_wunlock(&g_udnf_conf_lock);
+ free(newbuf, M_TEMP);
+ return (error);
+}
+
+SYSCTL_NODE(_debug, OID_AUTO, mnowait_failure, CTLFLAG_RW, 0,
+ "Control of M_NOWAIT memory allocation failure injection.");
+
+KFAIL_POINT_DEFINE(DEBUG_FP, mnowait, 0);
+
+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, blacklist,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, g_udnf_blacklist,
+ sizeof(g_udnf_blacklist), sysctl_debug_mnowait_failure_list, "A",
+ "With debug.fail_point.mnowait and with an empty whitelist, CSV list of "
+ "zones which remain unaffected.");
+
+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, whitelist,
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, g_udnf_whitelist,
+ sizeof(g_udnf_whitelist), sysctl_debug_mnowait_failure_list, "A",
+ "With debug.fail_point.mnowait, CSV list of zones exclusively affected. "
+ "With an empty whitelist, all zones but those on the blacklist"
+ "are affected.");
+
+SYSCTL_BOOL(_debug_mnowait_failure, OID_AUTO, zalloc_ignore_malloc,
+ CTLFLAG_RW, &g_uma_dbg_nowait_fail_zalloc_ignore_malloc, 0,
+ "Whether zalloc failure injection ignores (does not inject) malloc "
+ "zones.");
+
+SYSCTL_PROC(_debug_mnowait_failure, OID_AUTO, last_injection,
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
+ sysctl_debug_mnowait_failure_last_injection, "A",
+ "The last allocation for which a failure was injected.");
+#endif /* MALLOC_MAKE_FAILURES */
Index: sys/vm/uma_int.h
===================================================================
--- sys/vm/uma_int.h
+++ sys/vm/uma_int.h
@@ -34,6 +34,7 @@
#include <sys/_bitset.h>
#include <sys/_domainset.h>
#include <sys/_task.h>
+#include <sys/fail.h>
/*
* This file includes definitions, structures, prototypes, and inlines that
@@ -493,6 +494,21 @@
/* Set a global soft limit on UMA managed memory. */
void uma_set_limit(unsigned long limit);
+
+#ifdef MALLOC_MAKE_FAILURES
+bool uma_dbg_nowait_fail_enabled(const char *name);
+void uma_dbg_nowait_fail_record(const char *name);
+extern bool g_uma_dbg_nowait_fail_zalloc_ignore_malloc;
+KFAIL_POINT_DECLARE(mnowait);
+#define MALLOC_NOWAIT_FAIL_POINT(flags, code...) \
+ KFAIL_POINT_EVAL(mnowait, \
+ if (((flags) & M_NOWAIT) != 0) { \
+ code; \
+ } \
+ )
+#else
+#define MALLOC_NOWAIT_FAIL_POINT(flags, code...)
+#endif /* MALLOC_MAKE_FAILURES */
#endif /* _KERNEL */
#endif /* VM_UMA_INT_H */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 23, 10:33 AM (3 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14798062
Default Alt Text
D20714.id.diff (12 KB)
Attached To
Mode
D20714: malloc/zalloc M_NOWAIT failure injection
Attached
Detach File
Event Timeline
Log In to Comment