]> git.proxmox.com Git - mirror_zfs-debian.git/blob - zfs/lib/libumem/envvar.c
Initial Linux ZFS GIT Repo
[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, 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 }