]> git.proxmox.com Git - mirror_zfs-debian.git/blob - zfs/lib/libumem/envvar.c
Rebase to OpenSolaris b103, in the process we are removing any code which did not...
[mirror_zfs-debian.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 }