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