8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / madv / common / madv.c
blob5656a1fec1b89aa4930403649d36a799611611c9
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <sys/shm.h>
32 #include <sys/mman.h>
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/auxv.h>
40 #include <stdarg.h>
41 #include <syslog.h>
42 #include <sys/param.h>
43 #include <sys/sysmacros.h>
44 #include <procfs.h>
45 #include <dlfcn.h>
46 #include <assert.h>
47 #include <libintl.h>
48 #include <locale.h>
50 extern int gmatch(const char *s, const char *p);
52 #pragma init(__madvmain)
54 static FILE *errfp = NULL;
55 static const char *madvident = "madv.so.1";
56 static int pagesize;
57 static int advice_all = -1;
58 static int advice_heap = -1;
59 static int advice_shm = -1;
60 static int advice_ism = -1;
61 static int advice_dism = -1;
62 static int advice_map = -1;
63 static int advice_mapshared = -1;
64 static int advice_mapprivate = -1;
65 static int advice_mapanon = -1;
67 /* environment variables */
69 #define ENV_MADV "MADV"
70 #define ENV_MADVCFGFILE "MADVCFGFILE"
71 #define ENV_MADVERRFILE "MADVERRFILE"
73 /* config file */
75 #define DEF_MADVCFGFILE "/etc/madv.conf"
76 #define MAXLINELEN MAXPATHLEN + 64
77 #define CFGDELIMITER ':'
78 #define ARGDELIMITER ' '
81 * avoid malloc which causes certain applications to crash
83 static char lbuf[MAXLINELEN];
84 static char pbuf[MAXPATHLEN];
86 #ifdef MADVDEBUG
87 #define ENV_MADVDEBUG "MADVDEBUG"
88 #define MADVPRINT(x, y) if (madvdebug & x) (void) fprintf y;
90 static int madvdebug = 0;
91 #else
92 #define MADVPRINT(x, y)
93 #endif
96 * advice options
98 static char *legal_optstr[] = {
99 "madv",
100 "heap",
101 "shm",
102 "ism",
103 "dism",
104 "map",
105 "mapshared",
106 "mapprivate",
107 "mapanon",
108 NULL
111 enum optenum {
112 OPT_MADV,
113 OPT_HEAP,
114 OPT_SHM,
115 OPT_ISM,
116 OPT_DISM,
117 OPT_MAP,
118 OPT_MAPSHARED,
119 OPT_MAPPRIVATE,
120 OPT_MAPANON
124 * Advice values
125 * These need to correspond to the order of the MADV_ flags in mman.h
126 * since the position infers the value for the flag.
128 static char *legal_madvice[] = {
129 "normal",
130 "random",
131 "sequential",
132 "willneed_NOT_SUPPORTED!",
133 "dontneed_NOT_SUPPORTED!",
134 "free_NOT_SUPPORTED!",
135 "access_default",
136 "access_lwp",
137 "access_many",
138 NULL
141 #if !defined(TEXT_DOMAIN)
142 #define TEXT_DOMAIN "SYS_TEST"
143 #endif
145 /*PRINTFLIKE2*/
146 static void
147 madverr(FILE *fp, char *fmt, ...)
149 va_list ap;
150 va_start(ap, fmt);
151 if (fp)
152 (void) vfprintf(fp, fmt, ap);
153 else
154 vsyslog(LOG_ERR, fmt, ap);
155 va_end(ap);
159 * Return the pointer to the fully-resolved path name of the process's
160 * executable file obtained from the AT_SUN_EXECNAME aux vector entry.
162 static const char *
163 mygetexecname(void)
165 const char *execname = NULL;
166 static auxv_t auxb;
169 * The first time through, read the initial aux vector that was
170 * passed to the process at exec(2). Only do this once.
172 int fd = open("/proc/self/auxv", O_RDONLY);
174 if (fd >= 0) {
175 while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) {
176 if (auxb.a_type == AT_SUN_EXECNAME) {
177 execname = auxb.a_un.a_ptr;
178 break;
181 (void) close(fd);
183 return (execname);
187 * Return the process's current brk base and size.
189 static int
190 mygetbrk(uintptr_t *base, size_t *size)
192 int fd;
193 pstatus_t ps;
194 int rc;
196 fd = open("/proc/self/status", O_RDONLY);
198 if (fd >= 0) {
199 if (read(fd, &ps, sizeof (ps)) == sizeof (ps)) {
200 *base = ps.pr_brkbase;
201 *size = ps.pr_brksize;
202 rc = 0;
203 } else {
204 rc = errno;
206 (void) close(fd);
207 } else {
208 rc = errno;
210 return (rc);
214 * Check if exec name matches cfgname found in madv cfg file.
216 static int
217 fnmatch(const char *execname, char *cfgname, char *cwd)
219 const char *ename;
220 int rc;
222 /* cfgname should not have a '/' unless it begins with one */
223 if (cfgname[0] == '/') {
225 * if execname does not begin with a '/', prepend the
226 * current directory.
228 if (execname[0] != '/') {
229 ename = (const char *)strcat(cwd, execname);
230 } else
231 ename = execname;
232 } else { /* simple cfg name */
233 if (ename = strrchr(execname, '/'))
234 /* execname is a path name - get the base name */
235 ename++;
236 else
237 ename = execname;
239 rc = gmatch(ename, cfgname);
240 MADVPRINT(2, (stderr, "gmatch: %s %s %s %d\n",
241 cfgname, ename, execname, rc));
243 return (rc);
247 * Check if string matches any of exec arguments.
249 static int
250 argmatch(char *str)
252 int fd;
253 psinfo_t pi;
254 int rc = 0;
255 int arg;
256 char **argv;
258 fd = open("/proc/self/psinfo", O_RDONLY);
260 if (fd >= 0) {
261 if (read(fd, &pi, sizeof (pi)) == sizeof (pi)) {
262 argv = (char **)pi.pr_argv;
263 argv++;
264 MADVPRINT(2, (stderr, "argmatch: %s ", str));
265 for (arg = 1; arg < pi.pr_argc; arg++, argv++) {
266 if (rc = gmatch(*argv, str)) {
267 MADVPRINT(2, (stderr, "%s ", *argv));
268 break;
271 MADVPRINT(2, (stderr, "%d\n", rc));
272 } else {
273 madverr(errfp, dgettext(TEXT_DOMAIN,
274 "%s: /proc/self/psinfo read failed [%s]\n"),
275 madvident, strerror(errno));
277 (void) close(fd);
278 } else {
279 madverr(errfp, dgettext(TEXT_DOMAIN,
280 "%s: /proc/self/psinfo open failed [%s]\n"),
281 madvident, strerror(errno));
283 return (rc);
286 static int
287 empty(char *str)
289 char c;
291 while ((c = *str) == '\n' || c == ' ' || c == '\t')
292 str++;
293 return (*str == '\0');
296 static int
297 strtoadv(char *advstr)
299 char *dummy, *locstr = advstr;
301 return (getsubopt(&locstr, legal_madvice, &dummy));
304 static void
305 advice_opts(char *optstr, const char *execname, char *cfgfile, int lineno)
307 char *value;
308 int opt;
309 int advice = 0;
311 while (*optstr != '\0') {
312 opt = getsubopt(&optstr, legal_optstr, &value);
313 if (opt < 0) {
314 madverr(errfp, dgettext(TEXT_DOMAIN,
315 "%s: invalid advice option (%s)"
316 " for %s - cfgfile: %s, line: %d\n"),
317 madvident, value, execname, cfgfile, lineno);
318 break;
319 } else if (!value) {
320 madverr(errfp, dgettext(TEXT_DOMAIN,
321 "%s: option missing advice"
322 " for %s - cfgfile: %s, line: %d\n"),
323 madvident, execname, cfgfile, lineno);
324 break;
326 advice = strtoadv(value);
327 if (advice < 0) {
328 madverr(errfp, dgettext(TEXT_DOMAIN,
329 "%s: invalid advice specified (%s)"
330 " for %s - cfgfile: %s, line: %d\n"),
331 madvident, value, execname, cfgfile, lineno);
332 break;
334 switch (opt) {
335 case OPT_MADV:
336 advice_all = advice;
337 break;
338 case OPT_HEAP:
339 if (advice_heap < 0) {
340 advice_heap = advice;
341 } else {
342 madverr(errfp, dgettext(TEXT_DOMAIN,
343 "%s: duplicate advice specified "
344 "(%s) for %s - cfgfile: %s, line: %d\n"),
345 madvident, value, execname, cfgfile,
346 lineno);
348 break;
349 case OPT_SHM:
350 if (advice_shm < 0) {
351 advice_shm = advice;
352 } else {
353 madverr(errfp, dgettext(TEXT_DOMAIN,
354 "%s: duplicate advice specified "
355 "(%s) for %s - cfgfile: %s, line: %d\n"),
356 madvident, value, execname, cfgfile,
357 lineno);
359 break;
360 case OPT_ISM:
361 if (advice_ism < 0) {
362 advice_ism = advice;
363 } else {
364 madverr(errfp, dgettext(TEXT_DOMAIN,
365 "%s: duplicate advice specified "
366 "(%s) for %s - cfgfile: %s, line: %d\n"),
367 madvident, value, execname, cfgfile,
368 lineno);
370 break;
371 case OPT_DISM:
372 if (advice_dism < 0) {
373 advice_dism = advice;
374 } else {
375 madverr(errfp, dgettext(TEXT_DOMAIN,
376 "%s: duplicate advice specified "
377 "(%s) for %s - cfgfile: %s, line: %d\n"),
378 madvident, value, execname, cfgfile,
379 lineno);
381 break;
382 case OPT_MAP:
383 if (advice_map < 0) {
384 advice_map = advice;
385 } else {
386 madverr(errfp, dgettext(TEXT_DOMAIN,
387 "%s: duplicate advice specified "
388 "(%s) for %s - cfgfile: %s, line: %d\n"),
389 madvident, value, execname, cfgfile,
390 lineno);
392 break;
393 case OPT_MAPSHARED:
394 if (advice_mapshared < 0) {
395 advice_mapshared = advice;
396 } else {
397 madverr(errfp, dgettext(TEXT_DOMAIN,
398 "%s: duplicate advice specified "
399 "(%s) for %s - cfgfile: %s, line: %d\n"),
400 madvident, value, execname, cfgfile,
401 lineno);
403 break;
404 case OPT_MAPPRIVATE:
405 if (advice_mapprivate < 0) {
406 advice_mapprivate = advice;
407 } else {
408 madverr(errfp, dgettext(TEXT_DOMAIN,
409 "%s: duplicate advice specified "
410 "(%s) for %s - cfgfile: %s, line: %d\n"),
411 madvident, value, execname, cfgfile,
412 lineno);
414 break;
415 case OPT_MAPANON:
416 if (advice_mapanon < 0) {
417 advice_mapanon = advice;
418 } else {
419 madverr(errfp, dgettext(TEXT_DOMAIN,
420 "%s: duplicate advice specified "
421 "(%s) for %s - cfgfile: %s, line: %d\n"),
422 madvident, value, execname, cfgfile,
423 lineno);
425 break;
426 default:
427 madverr(errfp, dgettext(TEXT_DOMAIN,
428 "%s: invalid advice option (%s)"
429 " for %s - cfgfile: %s, line: %d\n"),
430 madvident, value, execname, cfgfile, lineno);
431 break;
436 static void
437 __madvmain()
439 char *cfgfile, *errfile;
440 FILE *fp = NULL;
441 const char *execname;
442 char *cwd;
443 int cwdlen;
444 char *tok, *tokadv, *tokarg;
445 char *str, *envadv;
446 int lineno = 0;
447 int advice;
448 uintptr_t brkbase, brkend;
449 size_t brksize;
450 int rc;
451 char *locale;
454 * If a private error file is indicated then set the locale
455 * for error messages for the duration of this routine.
456 * Error messages destined for syslog should not be translated
457 * and thus come from the default C locale.
459 if ((errfile = getenv(ENV_MADVERRFILE)) != NULL) {
460 errfp = fopen(errfile, "aF");
461 if (errfp) {
462 locale = setlocale(LC_MESSAGES, "");
463 } else {
464 madverr(NULL, dgettext(TEXT_DOMAIN,
465 "%s: cannot open error file: %s [%s]\n"),
466 madvident, errfile, strerror(errno));
470 #ifdef MADVDEBUG
471 if (str = getenv(ENV_MADVDEBUG))
472 madvdebug = atoi(str);
473 #endif
475 if (envadv = getenv(ENV_MADV)) {
476 if ((advice = strtoadv(envadv)) >= 0)
477 advice_all = advice;
478 else
479 madverr(errfp, dgettext(TEXT_DOMAIN,
480 "%s: invalid advice specified: MADV=%s\n"),
481 madvident, envadv);
485 * Open specified cfg file or default one.
487 if (cfgfile = getenv(ENV_MADVCFGFILE)) {
488 fp = fopen(cfgfile, "rF");
489 if (!fp) {
490 madverr(errfp, dgettext(TEXT_DOMAIN,
491 "%s: cannot open configuration file: %s [%s]\n"),
492 madvident, cfgfile, strerror(errno));
494 } else {
495 cfgfile = DEF_MADVCFGFILE;
496 fp = fopen(cfgfile, "rF");
499 if (fp) {
500 execname = mygetexecname();
502 cwd = getcwd(pbuf, MAXPATHLEN);
503 if (!cwd)
504 return;
506 cwd = strcat(cwd, "/");
507 cwdlen = strlen(cwd);
509 while (fgets(lbuf, MAXLINELEN, fp)) {
510 lineno++;
513 * Make sure line wasn't truncated.
515 if (strlen(lbuf) >= MAXLINELEN - 1) {
516 madverr(errfp, dgettext(TEXT_DOMAIN,
517 "%s: invalid entry, "
518 "line too long - cfgfile:"
519 " %s, line: %d\n"),
520 madvident, cfgfile, lineno);
521 continue;
524 if (empty(lbuf))
525 continue;
528 * Get advice options.
529 * Parse right to left in case delimiter is in name.
531 if (!(tokadv = strrchr(lbuf, CFGDELIMITER))) {
532 madverr(errfp, dgettext(TEXT_DOMAIN,
533 "%s: no delimiter specified - cfgfile:"
534 " %s, line: %d\n"),
535 madvident, cfgfile, lineno);
536 continue;
538 *tokadv++ = '\0';
541 * Remove newline from end of advice options.
543 if (str = strrchr(tokadv, '\n'))
544 *str = '\0';
547 * Get optional argument string.
549 if (tokarg = strrchr(lbuf, ARGDELIMITER)) {
550 *tokarg++ = '\0';
554 * Compare exec name.
556 tok = lbuf;
557 if (!fnmatch(execname, tok, cwd)) {
558 tokadv = tokarg = NULL;
559 cwd[cwdlen] = '\0';
560 continue;
564 * Compare arguments if argument string specified.
566 if (tokarg &&
567 !empty(tokarg) &&
568 !argmatch(tokarg)) {
569 tokadv = tokarg = NULL;
570 cwd[cwdlen] = '\0';
571 continue;
575 * Parse advice options.
576 * If empty, any advice from ENV_MADV is reset.
578 if (empty(tokadv)) {
579 advice_all = -1;
580 } else {
581 advice_opts(tokadv, execname, cfgfile, lineno);
583 break;
585 (void) fclose(fp);
589 * Pagesize needed for proper aligning by brk interpose.
591 pagesize = sysconf(_SC_PAGESIZE);
594 * Apply global advice if set.
595 * Specific options in the cfgfile take precedence.
597 if (advice_all >= 0) {
598 if (advice_heap < 0)
599 advice_heap = advice_all;
600 if (advice_shm < 0)
601 advice_shm = advice_all;
602 if (advice_map < 0)
603 advice_map = advice_all;
606 MADVPRINT(2, (stderr, "advice_all %d\n", advice_all));
607 MADVPRINT(2, (stderr, "advice_heap %d\n", advice_heap));
608 MADVPRINT(2, (stderr, "advice_shm %d\n", advice_shm));
609 MADVPRINT(2, (stderr, "advice_ism %d\n", advice_ism));
610 MADVPRINT(2, (stderr, "advice_dism %d\n", advice_dism));
611 MADVPRINT(2, (stderr, "advice_map %d\n", advice_map));
612 MADVPRINT(2, (stderr, "advice_mapshared %d\n", advice_mapshared));
613 MADVPRINT(2, (stderr, "advice_mapprivate %d\n", advice_mapprivate));
614 MADVPRINT(2, (stderr, "advice_mapanon %d\n", advice_mapanon));
617 * If heap advice is specified, apply it to the existing heap.
618 * As the heap grows the kernel applies the advice automatically
619 * to new portions of the heap.
621 if (advice_heap >= 0) {
622 if (rc = mygetbrk(&brkbase, &brksize)) {
623 madverr(errfp, dgettext(TEXT_DOMAIN,
624 "%s: /proc/self/status read failed [%s]\n"),
625 madvident, strerror(rc));
626 } else {
627 MADVPRINT(4, (stderr, "brkbase 0x%x brksize 0x%x\n",
628 brkbase, brksize));
630 * Align start address for memcntl and apply advice
631 * on full pages of heap. Create a page of heap if
632 * it does not already exist.
634 brkend = roundup(brkbase+brksize, pagesize);
635 brkbase = roundup(brkbase, pagesize);
636 brksize = brkend - brkbase;
637 if (brksize < pagesize) {
638 if (sbrk(pagesize) == (void *)-1) {
639 madverr(errfp, dgettext(TEXT_DOMAIN,
640 "%s: sbrk failed [%s]\n"),
641 madvident, strerror(errno));
642 goto out;
644 brksize = pagesize;
646 MADVPRINT(1, (stderr, "heap advice: 0x%x 0x%x %d\n",
647 brkbase, brksize, advice_heap));
648 if (memcntl((caddr_t)brkbase, brksize, MC_ADVISE,
649 (caddr_t)(intptr_t)advice_heap, 0, 0) < 0) {
650 madverr(errfp, dgettext(TEXT_DOMAIN,
651 "%s: memcntl() failed [%s]: heap advice\n"),
652 madvident, strerror(errno));
656 out:
657 if (errfp) {
658 (void) fclose(errfp);
659 (void) setlocale(LC_MESSAGES, locale);
660 } else {
661 /* close log file: no-op if nothing logged to syslog */
662 closelog();
668 * shmat interpose
670 void *
671 shmat(int shmid, const void *shmaddr, int shmflag)
673 static caddr_t (*shmatfunc)() = NULL;
674 void *result;
675 int advice = -1;
676 struct shmid_ds mds;
677 #ifdef MADVDEBUG
678 int rc;
679 #else
680 /* LINTED */
681 int rc;
682 #endif
684 if (!shmatfunc) {
685 shmatfunc = (caddr_t (*)()) dlsym(RTLD_NEXT, "shmat");
686 assert(shmatfunc);
689 result = shmatfunc(shmid, shmaddr, shmflag);
692 * Options ism, dism take precedence over option shm.
694 if (advice_ism >= 0 && (shmflag & SHM_SHARE_MMU)) {
695 advice = advice_ism;
696 } else if (advice_dism >= 0 && (shmflag & SHM_PAGEABLE)) {
697 advice = advice_dism;
698 } else if (advice_shm >= 0) {
699 advice = advice_shm;
703 * Apply advice if specified and shmat succeeded.
705 if (advice >= 0 && result != (void *)-1) {
706 /* First determine segment size */
707 rc = shmctl(shmid, IPC_STAT, &mds);
708 MADVPRINT(4, (stderr, "shmctl rc %d errno %d\n",
709 strerror(errno)));
711 rc = memcntl(result, mds.shm_segsz, MC_ADVISE,
712 (caddr_t)(intptr_t)advice, 0, 0);
713 MADVPRINT(1, (stderr,
714 "shmat advice: 0x%x 0x%x %d, rc %d errno %d\n",
715 result, mds.shm_segsz, advice, rc, errno));
718 return (result);
722 * mmap interpose
724 caddr_t
725 mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos)
727 static caddr_t (*mmapfunc)() = NULL;
728 caddr_t result;
729 int advice = -1;
730 #ifdef MADVDEBUG
731 int rc;
732 #else
733 /* LINTED */
734 int rc;
735 #endif
737 if (!mmapfunc) {
738 mmapfunc = (caddr_t (*)()) dlsym(RTLD_NEXT, "mmap");
739 assert(mmapfunc);
742 result = mmapfunc(addr, len, prot, flags, fd, pos);
745 * Option mapanon has highest precedence while option map
746 * has lowest precedence.
748 if (advice_mapanon >= 0 && (flags & MAP_ANON)) {
749 advice = advice_mapanon;
750 } else if (advice_mapshared >= 0 && (flags & MAP_SHARED)) {
751 advice = advice_mapshared;
752 } else if (advice_mapprivate >= 0 && (flags & MAP_PRIVATE)) {
753 advice = advice_mapprivate;
754 } else if (advice_map >= 0) {
755 advice = advice_map;
759 * Apply advice if specified and mmap succeeded.
761 if (advice >= 0 && result != MAP_FAILED) {
762 rc = memcntl(result, len, MC_ADVISE,
763 (caddr_t)(intptr_t)advice, 0, 0);
764 MADVPRINT(1, (stderr,
765 "mmap advice: 0x%x 0x%x %d, rc %d errno %d\n",
766 result, len, advice, rc, errno));
769 return (result);
772 #if !defined(_LP64)
774 * mmap64 interpose
776 caddr_t
777 mmap64(caddr_t addr, size_t len, int prot, int flags, int fd, off64_t pos)
779 static caddr_t (*mmap64func)();
780 caddr_t result;
781 int advice = -1;
782 #ifdef MADVDEBUG
783 int rc;
784 #else
785 /* LINTED */
786 int rc;
787 #endif
789 if (!mmap64func) {
790 mmap64func = (caddr_t (*)()) dlsym(RTLD_NEXT, "mmap64");
791 assert(mmap64func);
794 result = mmap64func(addr, len, prot, flags, fd, pos);
797 * Option mapanon has highest precedence while option map
798 * has lowest precedence.
800 if (advice_mapanon >= 0 && (flags & MAP_ANON)) {
801 advice = advice_mapanon;
802 } else if (advice_mapshared >= 0 && (flags & MAP_SHARED)) {
803 advice = advice_mapshared;
804 } else if (advice_mapprivate >= 0 && (flags & MAP_PRIVATE)) {
805 advice = advice_mapprivate;
806 } else if (advice_map >= 0) {
807 advice = advice_map;
811 * Apply advice if specified and mmap succeeded.
813 if (advice >= 0 && result != MAP_FAILED) {
814 rc = memcntl(result, len, MC_ADVISE, (caddr_t)advice, 0, 0);
815 MADVPRINT(1, (stderr,
816 "mmap64 advice: 0x%x 0x%x %d, rc %d errno %d\n",
817 result, len, advice, rc, errno));
820 return (result);
822 #endif /* !_LP64 */