Temporarily move taskq+util to libzpool until that directory is broken in to lib...
[zfs.git] / zfs / lib / libumem / envvar.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26
27 #pragma ident   "%Z%%M% %I%     %E% SMI"
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <dlfcn.h>
35 #include "umem_base.h"
36 #include "vmem_base.h"
37
38 /*
39  * A umem environment variable, like UMEM_DEBUG, is set to a series
40  * of items, seperated by ',':
41  *
42  *   UMEM_DEBUG="audit=10,guards,firewall=512"
43  *
44  * This structure describes items.  Each item has a name, type, and
45  * description.  During processing, an item read from the user may
46  * be either "valid" or "invalid".
47  *
48  * A valid item has an argument, if required, and it is of the right
49  * form (doesn't overflow, doesn't contain any unexpected characters).
50  *
51  * If the item is valid, item_flag_target != NULL, and:
52  *      type is not CLEARFLAG, then (*item_flag_target) |= item_flag_value
53  *      type is CLEARFLAG, then (*item_flag_target) &= ~item_flag_value
54  */
55
56 #define UMEM_ENV_ITEM_MAX       512
57
58 struct umem_env_item;
59
60 typedef int arg_process_t(const struct umem_env_item *item, const char *value);
61 #define ARG_SUCCESS     0       /* processing successful */
62 #define ARG_BAD         1       /* argument had a bad value */
63
64 typedef struct umem_env_item {
65         const char *item_name;  /* tag in environment variable */
66         const char *item_interface_stability;
67         enum {
68             ITEM_INVALID,
69             ITEM_FLAG,          /* only a flag.  No argument allowed */
70             ITEM_CLEARFLAG,     /* only a flag, but clear instead of set */
71             ITEM_OPTUINT,       /* optional integer argument */
72             ITEM_UINT,          /* required integer argument */
73             ITEM_OPTSIZE,       /* optional size_t argument */
74             ITEM_SIZE,          /* required size_t argument */
75             ITEM_SPECIAL        /* special argument processing */
76         } item_type;
77         const char *item_description;
78         uint_t *item_flag_target; /* the variable containing the flag */
79         uint_t item_flag_value; /* the value to OR in */
80         uint_t *item_uint_target; /* the variable to hold the integer */
81         size_t *item_size_target;
82         arg_process_t *item_special; /* callback for special handling */
83 } umem_env_item_t;
84
85 #ifndef UMEM_STANDALONE
86 static arg_process_t umem_backend_process;
87 #endif
88
89 static arg_process_t umem_log_process;
90
91 static size_t umem_size_tempval;
92 static arg_process_t umem_size_process;
93
94 const char *____umem_environ_msg_options = "-- UMEM_OPTIONS --";
95
96 static umem_env_item_t umem_options_items[] = {
97 #ifndef UMEM_STANDALONE
98         { "backend",            "Evolving",     ITEM_SPECIAL,
99                 "=sbrk for sbrk(2), =mmap for mmap(2)",
100                 NULL, 0, NULL, NULL,
101                 &umem_backend_process
102         },
103 #endif
104
105         { "concurrency",        "Private",      ITEM_UINT,
106                 "Max concurrency",
107                 NULL, 0,        &umem_max_ncpus
108         },
109         { "max_contention",     "Private",      ITEM_UINT,
110                 "Maximum contention in a reap interval before the depot is "
111                     "resized.",
112                 NULL, 0,        &umem_depot_contention
113         },
114         { "nomagazines",        "Private",      ITEM_FLAG,
115                 "no caches will be multithreaded, and no caching will occur.",
116                 &umem_flags,    UMF_NOMAGAZINE
117         },
118         { "reap_interval",      "Private",      ITEM_UINT,
119                 "Minimum time between reaps and updates, in seconds.",
120                 NULL, 0,        &umem_reap_interval
121         },
122
123         { "size_add",           "Private",      ITEM_SPECIAL,
124                 "add a size to the cache size table",
125                 NULL, 0, NULL,
126                 &umem_size_tempval,             &umem_size_process
127         },
128         { "size_clear",         "Private",      ITEM_SPECIAL,
129                 "clear all but the largest size from the cache size table",
130                 NULL, 0, NULL,
131                 &umem_size_tempval,             &umem_size_process
132         },
133         { "size_remove",        "Private",      ITEM_SPECIAL,
134             "remove a size from the cache size table",
135                 NULL, 0, NULL,
136                 &umem_size_tempval,             &umem_size_process
137         },
138
139 #ifndef UMEM_STANDALONE
140         { "sbrk_minalloc",      "Private",      ITEM_SIZE,
141                 "The minimum allocation chunk for the sbrk(2) heap.",
142                 NULL, 0, NULL,  &vmem_sbrk_minalloc
143         },
144         { "sbrk_pagesize",      "Private",      ITEM_SIZE,
145                 "The preferred page size for the sbrk(2) heap.",
146                 NULL, 0, NULL,  &vmem_sbrk_pagesize
147         },
148 #endif
149
150         { NULL, "-- end of UMEM_OPTIONS --",    ITEM_INVALID }
151 };
152
153 const char *____umem_environ_msg_debug = "-- UMEM_DEBUG --";
154
155 static umem_env_item_t umem_debug_items[] = {
156         { "default",            "Unstable",     ITEM_FLAG,
157                 "audit,contents,guards",
158                 &umem_flags,
159                 UMF_AUDIT | UMF_CONTENTS | UMF_DEADBEEF | UMF_REDZONE
160         },
161         { "audit",              "Unstable",     ITEM_OPTUINT,
162                 "Enable auditing.  optionally =frames to set the number of "
163                     "stored stack frames",
164                 &umem_flags,    UMF_AUDIT,      &umem_stack_depth
165         },
166         { "contents",           "Unstable",     ITEM_OPTSIZE,
167                 "Enable contents storing.  UMEM_LOGGING=contents also "
168                     "required.  optionally =bytes to set the number of stored "
169                     "bytes",
170                 &umem_flags,    UMF_CONTENTS, NULL,     &umem_content_maxsave
171         },
172         { "guards",             "Unstable",     ITEM_FLAG,
173                 "Enables guards and special patterns",
174                 &umem_flags,    UMF_DEADBEEF | UMF_REDZONE
175         },
176         { "verbose",            "Unstable",     ITEM_FLAG,
177                 "Enables writing error messages to stderr",
178                 &umem_output,   1
179         },
180
181         { "nosignal",   "Private",      ITEM_FLAG,
182                 "Abort if called from a signal handler.  Turns on 'audit'.  "
183                     "Note that this is not always a bug.",
184                 &umem_flags,    UMF_AUDIT | UMF_CHECKSIGNAL
185         },
186         { "firewall",           "Private",      ITEM_SIZE,
187                 "=minbytes.  Every object >= minbytes in size will have its "
188                     "end against an unmapped page",
189                 &umem_flags,    UMF_FIREWALL,   NULL,   &umem_minfirewall
190         },
191         { "lite",               "Private",      ITEM_FLAG,
192                 "debugging-lite",
193                 &umem_flags,    UMF_LITE
194         },
195         { "maxverify",          "Private",      ITEM_SIZE,
196                 "=maxbytes, Maximum bytes to check when 'guards' is active. "
197                     "Normally all bytes are checked.",
198                 NULL, 0, NULL,  &umem_maxverify
199         },
200         { "noabort",            "Private",      ITEM_CLEARFLAG,
201                 "umem will not abort when a recoverable error occurs "
202                     "(i.e. double frees, certain kinds of corruption)",
203                 &umem_abort,    1
204         },
205         { "mtbf",               "Private",      ITEM_UINT,
206                 "=mtbf, the mean time between injected failures.  Works best "
207                     "if prime.\n",
208                 NULL, 0,        &umem_mtbf
209         },
210         { "random",             "Private",      ITEM_FLAG,
211                 "randomize flags on a per-cache basis",
212                 &umem_flags,    UMF_RANDOMIZE
213         },
214         { "allverbose",         "Private",      ITEM_FLAG,
215                 "Enables writing all logged messages to stderr",
216                 &umem_output,   2
217         },
218
219         { NULL, "-- end of UMEM_DEBUG --",      ITEM_INVALID }
220 };
221
222 const char *____umem_environ_msg_logging = "-- UMEM_LOGGING --";
223
224 static umem_env_item_t umem_logging_items[] = {
225         { "transaction",        "Unstable",     ITEM_SPECIAL,
226                 "If 'audit' is set in UMEM_DEBUG, the audit structures "
227                     "from previous transactions are entered into this log.",
228                 NULL, 0, NULL,
229                 &umem_transaction_log_size,     &umem_log_process
230         },
231         { "contents",           "Unstable",     ITEM_SPECIAL,
232                 "If 'audit' is set in UMEM_DEBUG, the contents of objects "
233                     "are recorded in this log as they are freed.  If the "
234                     "'contents' option is not set in UMEM_DEBUG, the first "
235                     "256 bytes of each freed buffer will be saved.",
236                 &umem_flags,    UMF_CONTENTS,   NULL,
237                 &umem_content_log_size,         &umem_log_process
238         },
239         { "fail",               "Unstable",     ITEM_SPECIAL,
240                 "Records are entered into this log for every failed "
241                     "allocation.",
242                 NULL, 0, NULL,
243                 &umem_failure_log_size,         &umem_log_process
244         },
245
246         { "slab",               "Private",      ITEM_SPECIAL,
247                 "Every slab created will be entered into this log.",
248                 NULL, 0, NULL,
249                 &umem_slab_log_size,            &umem_log_process
250         },
251
252         { NULL, "-- end of UMEM_LOGGING --",    ITEM_INVALID }
253 };
254
255 typedef struct umem_envvar {
256         const char *env_name;
257         const char *env_func;
258         umem_env_item_t *env_item_list;
259         const char *env_getenv_result;
260         const char *env_func_result;
261 } umem_envvar_t;
262
263 static umem_envvar_t umem_envvars[] = {
264         { "UMEM_DEBUG",         "_umem_debug_init",     umem_debug_items },
265         { "UMEM_OPTIONS",       "_umem_options_init",   umem_options_items },
266         { "UMEM_LOGGING",       "_umem_logging_init",   umem_logging_items },
267         { NULL, NULL, NULL }
268 };
269
270 static umem_envvar_t *env_current;
271 #define CURRENT         (env_current->env_name)
272
273 static int
274 empty(const char *str)
275 {
276         char c;
277
278         while ((c = *str) != '\0' && isspace(c))
279                 str++;
280
281         return (*str == '\0');
282 }
283
284 static int
285 item_uint_process(const umem_env_item_t *item, const char *item_arg)
286 {
287         ulong_t result;
288         char *endptr = "";
289         int olderrno;
290
291         olderrno = errno;
292         errno = 0;
293
294         if (empty(item_arg)) {
295                 goto badnumber;
296         }
297
298         result = strtoul(item_arg, &endptr, 10);
299
300         if (result == ULONG_MAX && errno == ERANGE) {
301                 errno = olderrno;
302                 goto overflow;
303         }
304         errno = olderrno;
305
306         if (*endptr != '\0')
307                 goto badnumber;
308         if ((uint_t)result != result)
309                 goto overflow;
310
311         (*item->item_uint_target) = (uint_t)result;
312         return (ARG_SUCCESS);
313
314 badnumber:
315         log_message("%s: %s: not a number\n", CURRENT, item->item_name);
316         return (ARG_BAD);
317
318 overflow:
319         log_message("%s: %s: overflowed\n", CURRENT, item->item_name);
320         return (ARG_BAD);
321 }
322
323 static int
324 item_size_process(const umem_env_item_t *item, const char *item_arg)
325 {
326         ulong_t result;
327         ulong_t result_arg;
328         char *endptr = "";
329         int olderrno;
330
331         if (empty(item_arg))
332                 goto badnumber;
333
334         olderrno = errno;
335         errno = 0;
336
337         result_arg = strtoul(item_arg, &endptr, 10);
338
339         if (result_arg == ULONG_MAX && errno == ERANGE) {
340                 errno = olderrno;
341                 goto overflow;
342         }
343         errno = olderrno;
344
345         result = result_arg;
346
347         switch (*endptr) {
348         case 't':
349         case 'T':
350                 result *= 1024;
351                 if (result < result_arg)
352                         goto overflow;
353                 /*FALLTHRU*/
354         case 'g':
355         case 'G':
356                 result *= 1024;
357                 if (result < result_arg)
358                         goto overflow;
359                 /*FALLTHRU*/
360         case 'm':
361         case 'M':
362                 result *= 1024;
363                 if (result < result_arg)
364                         goto overflow;
365                 /*FALLTHRU*/
366         case 'k':
367         case 'K':
368                 result *= 1024;
369                 if (result < result_arg)
370                         goto overflow;
371                 endptr++;               /* skip over the size character */
372                 break;
373         default:
374                 break;                  /* handled later */
375         }
376
377         if (*endptr != '\0')
378                 goto badnumber;
379
380         (*item->item_size_target) = result;
381         return (ARG_SUCCESS);
382
383 badnumber:
384         log_message("%s: %s: not a number\n", CURRENT, item->item_name);
385         return (ARG_BAD);
386
387 overflow:
388         log_message("%s: %s: overflowed\n", CURRENT, item->item_name);
389         return (ARG_BAD);
390 }
391
392 static int
393 umem_log_process(const umem_env_item_t *item, const char *item_arg)
394 {
395         if (item_arg != NULL) {
396                 int ret;
397                 ret = item_size_process(item, item_arg);
398                 if (ret != ARG_SUCCESS)
399                         return (ret);
400
401                 if (*item->item_size_target == 0)
402                         return (ARG_SUCCESS);
403         } else
404                 *item->item_size_target = 64*1024;
405
406         umem_logging = 1;
407         return (ARG_SUCCESS);
408 }
409
410 static int
411 umem_size_process(const umem_env_item_t *item, const char *item_arg)
412 {
413         const char *name = item->item_name;
414         void (*action_func)(size_t);
415
416         size_t result;
417
418         int ret;
419
420         if (strcmp(name, "size_clear") == 0) {
421                 if (item_arg != NULL) {
422                         log_message("%s: %s: does not take a value. ignored\n",
423                             CURRENT, name);
424                         return (ARG_BAD);
425                 }
426                 umem_alloc_sizes_clear();
427                 return (ARG_SUCCESS);
428         } else if (strcmp(name, "size_add") == 0) {
429                 action_func = umem_alloc_sizes_add;
430         } else if (strcmp(name, "size_remove") == 0) {
431                 action_func = umem_alloc_sizes_remove;
432         } else {
433                 log_message("%s: %s: internally unrecognized\n",
434                     CURRENT, name, name, name);
435                 return (ARG_BAD);
436         }
437
438         if (item_arg == NULL) {
439                 log_message("%s: %s: requires a value. ignored\n",
440                     CURRENT, name);
441                 return (ARG_BAD);
442         }
443
444         ret = item_size_process(item, item_arg);
445         if (ret != ARG_SUCCESS)
446                 return (ret);
447
448         result = *item->item_size_target;
449         action_func(result);
450         return (ARG_SUCCESS);
451 }
452
453 #ifndef UMEM_STANDALONE
454 static int
455 umem_backend_process(const umem_env_item_t *item, const char *item_arg)
456 {
457         const char *name = item->item_name;
458
459         if (item_arg == NULL)
460                 goto fail;
461
462         if (strcmp(item_arg, "sbrk") == 0)
463                 vmem_backend |= VMEM_BACKEND_SBRK;
464         else if (strcmp(item_arg, "mmap") == 0)
465                 vmem_backend |= VMEM_BACKEND_MMAP;
466         else
467                 goto fail;
468
469         return (ARG_SUCCESS);
470
471 fail:
472         log_message("%s: %s: must be %s=sbrk or %s=mmap\n",
473             CURRENT, name, name, name);
474         return (ARG_BAD);
475 }
476 #endif
477
478 static int
479 process_item(const umem_env_item_t *item, const char *item_arg)
480 {
481         int arg_required = 0;
482         arg_process_t *processor;
483
484         switch (item->item_type) {
485         case ITEM_FLAG:
486         case ITEM_CLEARFLAG:
487         case ITEM_OPTUINT:
488         case ITEM_OPTSIZE:
489         case ITEM_SPECIAL:
490                 arg_required = 0;
491                 break;
492
493         case ITEM_UINT:
494         case ITEM_SIZE:
495                 arg_required = 1;
496                 break;
497         }
498
499         switch (item->item_type) {
500         case ITEM_FLAG:
501         case ITEM_CLEARFLAG:
502                 if (item_arg != NULL) {
503                         log_message("%s: %s: does not take a value. ignored\n",
504                             CURRENT, item->item_name);
505                         return (1);
506                 }
507                 processor = NULL;
508                 break;
509
510         case ITEM_UINT:
511         case ITEM_OPTUINT:
512                 processor = item_uint_process;
513                 break;
514
515         case ITEM_SIZE:
516         case ITEM_OPTSIZE:
517                 processor = item_size_process;
518                 break;
519
520         case ITEM_SPECIAL:
521                 processor = item->item_special;
522                 break;
523
524         default:
525                 log_message("%s: %s: Invalid type.  Ignored\n",
526                     CURRENT, item->item_name);
527                 return (1);
528         }
529
530         if (arg_required && item_arg == NULL) {
531                 log_message("%s: %s: Required value missing\n",
532                     CURRENT, item->item_name);
533                 goto invalid;
534         }
535
536         if (item_arg != NULL || item->item_type == ITEM_SPECIAL) {
537                 if (processor(item, item_arg) != ARG_SUCCESS)
538                         goto invalid;
539         }
540
541         if (item->item_flag_target) {
542                 if (item->item_type == ITEM_CLEARFLAG)
543                         (*item->item_flag_target) &= ~item->item_flag_value;
544                 else
545                         (*item->item_flag_target) |= item->item_flag_value;
546         }
547         return (0);
548
549 invalid:
550         return (1);
551 }
552
553 #define ENV_SHORT_BYTES 10      /* bytes to print on error */
554 void
555 umem_process_value(umem_env_item_t *item_list, const char *beg, const char *end)
556 {
557         char buf[UMEM_ENV_ITEM_MAX];
558         char *argptr;
559
560         size_t count;
561
562         while (beg < end && isspace(*beg))
563                 beg++;
564
565         while (beg < end && isspace(*(end - 1)))
566                 end--;
567
568         if (beg >= end) {
569                 log_message("%s: empty option\n", CURRENT);
570                 return;
571         }
572
573         count = end - beg;
574
575         if (count + 1 > sizeof (buf)) {
576                 char outbuf[ENV_SHORT_BYTES + 1];
577                 /*
578                  * Have to do this, since sprintf("%10s",...) calls malloc()
579                  */
580                 (void) strncpy(outbuf, beg, ENV_SHORT_BYTES);
581                 outbuf[ENV_SHORT_BYTES] = 0;
582
583                 log_message("%s: argument \"%s...\" too long\n", CURRENT,
584                     outbuf);
585                 return;
586         }
587
588         (void) strncpy(buf, beg, count);
589         buf[count] = 0;
590
591         argptr = strchr(buf, '=');
592
593         if (argptr != NULL)
594                 *argptr++ = 0;
595
596         for (; item_list->item_name != NULL; item_list++) {
597                 if (strcmp(buf, item_list->item_name) == 0) {
598                         (void) process_item(item_list, argptr);
599                         return;
600                 }
601         }
602         log_message("%s: '%s' not recognized\n", CURRENT, buf);
603 }
604
605 /*ARGSUSED*/
606 void
607 umem_setup_envvars(int invalid)
608 {
609         umem_envvar_t *cur_env;
610         static volatile enum {
611                 STATE_START,
612                 STATE_GETENV,
613                 STATE_DLOPEN,
614                 STATE_DLSYM,
615                 STATE_FUNC,
616                 STATE_DONE
617         } state = STATE_START;
618 #ifndef UMEM_STANDALONE
619         void *h;
620 #endif
621
622         if (invalid) {
623                 const char *where;
624                 /*
625                  * One of the calls below invoked malloc() recursively.  We
626                  * remove any partial results and return.
627                  */
628
629                 switch (state) {
630                 case STATE_START:
631                         where = "before getenv(3C) calls -- "
632                             "getenv(3C) results ignored.";
633                         break;
634                 case STATE_GETENV:
635                         where = "during getenv(3C) calls -- "
636                             "getenv(3C) results ignored.";
637                         break;
638                 case STATE_DLOPEN:
639                         where = "during dlopen(3C) call -- "
640                             "_umem_*() results ignored.";
641                         break;
642                 case STATE_DLSYM:
643                         where = "during dlsym(3C) call -- "
644                             "_umem_*() results ignored.";
645                         break;
646                 case STATE_FUNC:
647                         where = "during _umem_*() call -- "
648                             "_umem_*() results ignored.";
649                         break;
650                 case STATE_DONE:
651                         where = "after dlsym() or _umem_*() calls.";
652                         break;
653                 default:
654                         where = "at unknown point -- "
655                             "_umem_*() results ignored.";
656                         break;
657                 }
658
659                 log_message("recursive allocation %s\n", where);
660
661                 for (cur_env = umem_envvars; cur_env->env_name != NULL;
662                     cur_env++) {
663                         if (state == STATE_GETENV)
664                                 cur_env->env_getenv_result = NULL;
665                         if (state != STATE_DONE)
666                                 cur_env->env_func_result = NULL;
667                 }
668
669                 state = STATE_DONE;
670                 return;
671         }
672
673         state = STATE_GETENV;
674
675         for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) {
676                 cur_env->env_getenv_result = getenv(cur_env->env_name);
677                 if (state == STATE_DONE)
678                         return;         /* recursed */
679         }
680
681 #ifndef UMEM_STANDALONE
682         state = STATE_DLOPEN;
683
684         /* get a handle to the "a.out" object */
685         if ((h = dlopen(0, RTLD_FIRST | RTLD_LAZY)) != NULL) {
686                 for (cur_env = umem_envvars; cur_env->env_name != NULL;
687                     cur_env++) {
688                         const char *(*func)(void);
689                         const char *value;
690
691                         state = STATE_DLSYM;
692                         func = (const char *(*)(void))dlsym(h,
693                             cur_env->env_func);
694
695                         if (state == STATE_DONE)
696                                 break;          /* recursed */
697
698                         state = STATE_FUNC;
699                         if (func != NULL) {
700                                 value = func();
701                                 if (state == STATE_DONE)
702                                         break;          /* recursed */
703                                 cur_env->env_func_result = value;
704                         }
705                 }
706                 (void) dlclose(h);
707         } else {
708                 (void) dlerror();               /* snarf dlerror() */
709         }
710 #endif /* UMEM_STANDALONE */
711
712         state = STATE_DONE;
713 }
714
715 /*
716  * Process the environment variables.
717  */
718 void
719 umem_process_envvars(void)
720 {
721         const char *value;
722         const char *end, *next;
723         umem_envvar_t *cur_env;
724
725         for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) {
726                 env_current = cur_env;
727
728                 value = cur_env->env_getenv_result;
729                 if (value == NULL)
730                         value = cur_env->env_func_result;
731
732                 /* ignore if missing or empty */
733                 if (value == NULL)
734                         continue;
735
736                 for (end = value; *end != '\0'; value = next) {
737                         end = strchr(value, ',');
738                         if (end != NULL)
739                                 next = end + 1;         /* skip the comma */
740                         else
741                                 next = end = value + strlen(value);
742
743                         umem_process_value(cur_env->env_item_list, value, end);
744                 }
745         }
746 }