The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_fail.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 5bdfa7ee3fee5ea311c600c804f1d54a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.