62#include <sys/kernel.h>
63#include <sys/libkern.h>
64#include <sys/limits.h>
66#include <sys/malloc.h>
70#include <sys/sleepqueue.h>
72#include <sys/sysctl.h>
75#include <machine/atomic.h>
76#include <machine/stdarg.h>
78#ifdef ILOG_DEFINE_FOR_FILE
79ILOG_DEFINE_FOR_FILE(L_ISI_FAIL_POINT, L_ILOG, fail_point);
83#define fp_free(ptr) free(ptr, M_FAIL_POINT)
84#define fp_malloc(size, flags) malloc((size), M_FAIL_POINT, (flags))
85#define fs_free(ptr) fp_free(ptr)
86#define fs_malloc() fp_malloc(sizeof(struct fail_point_setting), \
94#define FP_SLEEP_CHANNEL(fp) (void*)(fp)
95#define FP_PAUSE_CHANNEL(fp) __DEVOLATILE(void*, &fp->fp_setting)
103#define FP_MAX_ENTRY_COUNT 20
148#define FP_TYPE_NM_LEN(s) { s, sizeof(s) - 1 }
160#define FE_COUNT_UNTRACKED (INT_MIN)
179 struct fail_point_entry_queue fp_entry_queue;
180 struct fail_point * fs_parent;
194 struct fail_point *fp);
230 if (fp_setting != NULL) {
231 TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue,
251 fs_new->fs_parent = fp;
252 TAILQ_INIT(&fs_new->fp_entry_queue);
253 mtx_init(&fs_new->feq_mtx,
"fail point entries", NULL, MTX_SPIN);
266 while (!TAILQ_EMPTY(&fp_setting->fp_entry_queue)) {
267 ent = TAILQ_FIRST(&fp_setting->fp_entry_queue);
268 TAILQ_REMOVE(&fp_setting->fp_entry_queue, ent, fe_entries);
283 fp_entry->
fe_parent = fp_setting->fs_parent;
285 fp_entry->
fe_pid = NO_PID;
287 TAILQ_INSERT_TAIL(&fp_setting->fp_entry_queue, fp_entry,
308 atomic_add_acq_32(&fp->fp_ref_cnt, 1);
309 fp_setting = fp->fp_setting;
319 KASSERT(&fp->fp_ref_cnt > 0, (
"Attempting to deref w/no refs"));
320 atomic_subtract_rel_32(&fp->fp_ref_cnt, 1);
341 fp_setting_old = fp->fp_setting;
342 fp->fp_setting = fp_setting_new;
344 return (fp_setting_old);
353 if (fp->fp_setting == fp_setting)
362 struct fail_point_setting_garbage fp_ents_free_list;
370 STAILQ_INIT(&fp_ents_free_list);
375 if (fs_current->fs_parent->fp_setting != fs_current &&
376 fs_current->fs_parent->fp_ref_cnt == 0) {
379 STAILQ_INSERT_HEAD(&fp_ents_free_list, fs_current,
385 STAILQ_FOREACH_SAFE(fs_current, &fp_ents_free_list, fs_garbage_link,
402 while (fp->fp_ref_cnt > expected_ref) {
404 tsleep(&fp, PWAIT,
"fail_point_drain",
hz / 100);
407 callout_drain(fp->fp_callout);
413 struct mtx *mtx_sleep)
416 if (fp->fp_pre_sleep_fn)
417 fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg);
421 if (fp->fp_post_sleep_fn)
422 fp->fp_post_sleep_fn(fp->fp_post_sleep_arg);
427 enum fail_point_return_code *pret)
432 timo = howmany((int64_t)msecs *
hz, 1000L);
435 if (!(fp->fp_flags & FAIL_POINT_USE_TIMEOUT_PATH)) {
436 if (fp->fp_pre_sleep_fn)
437 fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg);
441 if (fp->fp_post_sleep_fn)
442 fp->fp_post_sleep_fn(fp->fp_post_sleep_arg);
444 if (fp->fp_pre_sleep_fn)
445 fp->fp_pre_sleep_fn(fp->fp_pre_sleep_arg);
447 callout_reset(fp->fp_callout, timo,
448 fp->fp_post_sleep_fn, fp->fp_post_sleep_arg);
449 *pret = FAIL_POINT_RC_QUEUED;
456static char *
parse_number(
int *out_units,
int *out_decimal,
char *);
474 fp->fp_setting = NULL;
490 fp->fp_location =
"";
491 fp->fp_flags |= FAIL_POINT_DYNAMIC_NAME;
492 fp->fp_pre_sleep_fn = NULL;
493 fp->fp_pre_sleep_arg = NULL;
494 fp->fp_post_sleep_fn = NULL;
495 fp->fp_post_sleep_arg = NULL;
506 if (fp->fp_callout != NULL)
508 fp->fp_callout =
fp_malloc(
sizeof(*fp->fp_callout), M_WAITOK);
524 if ((fp->fp_flags & FAIL_POINT_DYNAMIC_NAME) != 0) {
525 fp_free(__DECONST(
void *, fp->fp_name));
529 if (fp->fp_callout) {
531 fp->fp_callout = NULL;
546enum fail_point_return_code
549 bool execute =
false;
552 enum fail_point_return_code ret;
558 ret = FAIL_POINT_RC_CONTINUE;
562 if (fp_setting == NULL)
565 TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, fe_entries) {
573 if (ent->
fe_pid != NO_PID && ent->
fe_pid != curproc->p_pid)
586 if (execute ==
false)
595 panic(
"fail point %s panicking", fp->fp_name);
599 if (return_value != NULL)
600 *return_value = ent->
fe_arg;
601 ret = FAIL_POINT_RC_RETURN;
605 printf(
"fail point %s breaking to debugger\n",
611 printf(
"fail point %s executing\n", fp->fp_name);
630 mtx_lock_spin(&fp_setting->feq_mtx);
632 mtx_unlock_spin(&fp_setting->feq_mtx);
673 int printed_entry_count;
677 printed_entry_count = 0;
684 if (fp_setting != NULL) {
685 TAILQ_FOREACH(ent, &fp_setting->fp_entry_queue, fe_entries) {
690 (
"FP entry list larger than allowed"));
692 fp_entry_cpy[printed_entry_count] = *ent;
693 ++printed_entry_count;
701 while (idx < printed_entry_count) {
702 ent = &fp_entry_cpy[idx];
710 while (!(decimal % 10)) {
723 if (ent->
fe_pid != NO_PID)
725 if (TAILQ_NEXT(ent, fe_entries))
728 if (!printed_entry_count)
766 bool should_wake_paused;
767 bool should_truncate;
771 should_wake_paused =
false;
772 should_truncate =
false;
796 TAILQ_FOREACH_SAFE(ent, &entries->fp_entry_queue, fe_entries, ent_next) {
798 printf(
"Discarding entry which cannot execute %s\n",
800 TAILQ_REMOVE(&entries->fp_entry_queue, ent,
804 }
else if (should_truncate) {
805 printf(
"Discarding unreachable entry %s\n",
807 TAILQ_REMOVE(&entries->fp_entry_queue, ent,
814 should_wake_paused =
true;
816 should_truncate =
true;
817 TAILQ_REMOVE(&entries->fp_entry_queue, ent,
822 should_truncate =
true;
824 FAIL_POINT_NONSLEEPABLE)) {
830 printf(
"Sleep call request on fail point in "
831 "non-sleepable context; using delay instead "
838 if (TAILQ_EMPTY(&entries->fp_entry_queue)) {
843 if (should_wake_paused)
851 IWARNING(
"Failed to set %s %s to %s",
852 fp->fp_name, fp->fp_location,
buf);
854 INOTICE(
"Set %s %s to %s",
855 fp->fp_name, fp->fp_location,
buf);
862#define MAX_FAIL_POINT_BUF 1023
870 struct fail_point *fp;
872 struct sbuf sb, *sb_check;
879 sb_check =
sbuf_new(&sb, NULL, 1024, SBUF_AUTOEXTEND);
899 error = SYSCTL_IN(req,
buf, req->newlen);
902 buf[req->newlen] =
'\0';
926 struct fail_point *fp;
927 struct sbuf sb, *sb_check;
931 sb_check =
sbuf_new(&sb, NULL, 1024, SBUF_AUTOEXTEND);
957 struct sysctl_req *sa;
962 error = SYSCTL_OUT(sa,
buf, len);
990 if (p[0] !=
'-' || p[1] !=
'>' ||
1017 while (isdigit(*p) || *p ==
'.') {
1030 }
else if (*p ==
'*') {
1031 if (!units || units < 0 || decimal)
1050 if (!isdigit(*p) && *p !=
'-')
1052 ent->
fe_arg = strtol(p, &p, 0);
1057#define PID_STRING "[pid "
1063 ent->
fe_pid = strtol(p, &p, 0);
1086 *out_units = strtol(p, &p, 10);
1087 if (p == old_p && *p !=
'.')
1095 while (isdigit(*p)) {
1096 int digit = *p -
'0';
1098 *out_decimal = *out_decimal * 10 + digit;
1133SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1141 KFAIL_POINT_RETURN(DEBUG_FP, test_fail_point);
1145 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, NULL, 0,
1147 "Trigger test fail points");
device_property_type_t type
MTX_SYSINIT(mtx_garbage_list, &mtx_garbage_list, "fail point garbage mtx", MTX_SPIN)
STAILQ_HEAD(fail_point_setting_garbage, fail_point_setting)
#define FP_SLEEP_CHANNEL(fp)
#define FP_PAUSE_CHANNEL(fp)
static struct @1 fail_type_strings[]
static MALLOC_DEFINE(M_FAIL_POINT, "Fail Points", "fail points system")
#define FE_COUNT_UNTRACKED
enum fail_point_t fe_type
static char * parse_number(int *out_units, int *out_decimal, char *)
static void fail_point_pause(struct fail_point *fp, enum fail_point_return_code *pret, struct mtx *mtx_sleep)
static char * parse_term(struct fail_point_setting *, char *)
static void fail_point_setting_garbage_append(struct fail_point_setting *fp_setting)
#define MAX_FAIL_POINT_BUF
static void fail_point_entry_destroy(struct fail_point_entry *fp_entry)
int fail_point_sysctl_status(SYSCTL_HANDLER_ARGS)
SX_SYSINIT(sx_fp_set, &sx_fp_set, "fail point set sx")
static int fail_point_set(struct fail_point *fp, char *buf)
static void fail_point_setting_destroy(struct fail_point_setting *fp_setting)
static void fail_point_garbage_collect(void)
SYSCTL_NODE(_debug, OID_AUTO, fail_point, CTLFLAG_RW|CTLFLAG_MPSAFE, 0, "fail points")
TAILQ_HEAD(fail_point_entry_queue, fail_point_entry)
static struct fail_point_setting * fail_point_setting_get_ref(struct fail_point *fp)
static void fail_point_sleep(struct fail_point *fp, int msecs, enum fail_point_return_code *pret)
static void fail_point_get(struct fail_point *fp, struct sbuf *sb, bool verbose)
int fail_sysctl_drain_func(void *, const char *, int)
int fail_point_sysctl(SYSCTL_HANDLER_ARGS)
static int sysctl_test_fail_point(SYSCTL_HANDLER_ARGS)
static void fail_point_eval_swap_out(struct fail_point *fp, struct fail_point_setting *fp_setting)
static void fail_point_setting_release_ref(struct fail_point *fp)
static struct fail_point_setting * fail_point_swap_settings(struct fail_point *fp, struct fail_point_setting *fp_setting_new)
static struct sx sx_fp_set
SYSCTL_OID(_debug_fail_point, OID_AUTO, test_trigger_fail_point, CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_NEEDGIANT, NULL, 0, sysctl_test_fail_point, "A", "Trigger test fail points")
struct fail_point * fe_parent
static void fail_point_drain(struct fail_point *fp, int expected_ref)
enum fail_point_return_code fail_point_eval_nontrivial(struct fail_point *fp, int *return_value)
static char * parse_fail_point(struct fail_point_setting *, char *)
void fail_point_alloc_callout(struct fail_point *fp)
static struct fail_point_setting * fail_point_setting_new(struct fail_point *)
static char * parse_type(struct fail_point_entry *, char *)
#define fp_malloc(size, flags)
static struct fail_point_setting_garbage fp_setting_garbage
bool fail_point_is_off(struct fail_point *fp)
static struct mtx mtx_garbage_list
static struct fail_point_entry * fail_point_entry_new(struct fail_point_setting *)
#define FP_MAX_ENTRY_COUNT
void fail_point_init(struct fail_point *fp, const char *fmt,...)
void fail_point_destroy(struct fail_point *fp)
#define FP_TYPE_NM_LEN(s)
void panic(const char *fmt,...)
void kern_yield(int prio)
void wakeup(const void *ident)
void callout_init(struct callout *c, int mpsafe)
int vsnprintf(char *str, size_t size, const char *format, va_list ap)
int printf(const char *fmt,...)
int sbuf_finish(struct sbuf *s)
void sbuf_delete(struct sbuf *s)
int sbuf_printf(struct sbuf *s, const char *fmt,...)
void sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
struct sbuf * sbuf_new(struct sbuf *s, char *buf, int length, int flags)