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
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.
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]
23 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
39 #include <sys/types.h>
44 #include <sys/sysmacros.h>
55 static struct ps_prochandle
*Pr
;
57 static volatile int interrupt
;
61 static void intr(int);
62 static int setpgsz(struct ps_prochandle
*, int, size_t *);
63 static int setpgsz_anon(struct ps_prochandle
*, size_t, int);
64 static caddr_t
setup_mha(uint_t
, size_t, int);
65 static size_t discover_optimal_pagesize(struct ps_prochandle
*,
73 static char *suboptstr
[] = {
92 if (optarg
== NULL
|| optarg
[0] == '\0')
95 sz
= strtoll(optarg
, &endptr
, 0);
122 /* pgsz array sufficient for max page sizes */
124 static size_t pgsza
[8 * sizeof (void *)];
130 if ((nelem
= getpagesizes(NULL
, 0)) == 0) {
131 (void) fprintf(stderr
, "%s: cannot determine system page"
132 " sizes\n", command
);
136 (void) getpagesizes(pgsza
, nelem
);
140 cnvpgsz(char *optarg
)
142 size_t pgsz
= atosz(optarg
);
145 if (!ISP2(pgsz
) || ((pgsz
< pgsza
[0]) && pgsz
!= 0)) {
148 for (i
= nelem
- 1; i
>= 0; i
--) {
149 if (pgsz
== pgsza
[i
])
151 if (pgsz
> pgsza
[i
]) {
157 if (pgsz
== INVPGSZ
) {
158 if (optarg
!= NULL
) {
159 (void) fprintf(stderr
,
160 "%s: invalid page size specified (%s)\n",
173 (void) fprintf(stderr
,
174 "usage:\t%s -o option[,option] [-F] cmd | -p pid ...\n"
175 " (set preferred page size of cmd or each process)\n"
176 " -o option[,option]: options are\n"
179 " anon=sz (sz: valid page size or 0 (zero))\n"
180 " -F: force grabbing of the target process(es)\n"
181 " cmd: launch command\n"
182 " -p pid ...: process id list\n",
188 main(int argc
, char *argv
[])
193 char *options
, *value
;
194 size_t pgsz
[] = {INVPGSZ
, INVPGSZ
, INVPGSZ
};
198 if ((command
= strrchr(argv
[0], '/')) != NULL
)
206 while ((opt
= getopt(argc
, argv
, "o:Fp")) != EOF
) {
208 case 'o': /* options */
210 while (*options
!= '\0') {
211 subopt
= getsubopt(&options
, suboptstr
, &value
);
216 pgsz
[subopt
] = cnvpgsz(value
);
224 case 'F': /* force grabbing (no O_EXCL) */
239 if ((pgsz
[E_HEAP
] == INVPGSZ
&& pgsz
[E_STACK
] == INVPGSZ
&&
240 pgsz
[E_ANON
] == INVPGSZ
) || errflg
|| argc
<= 0) {
244 /* catch signals from terminal */
245 if (sigset(SIGHUP
, SIG_IGN
) == SIG_DFL
)
246 (void) sigset(SIGHUP
, intr
);
247 if (sigset(SIGINT
, SIG_IGN
) == SIG_DFL
)
248 (void) sigset(SIGINT
, intr
);
249 if (sigset(SIGQUIT
, SIG_IGN
) == SIG_DFL
)
250 (void) sigset(SIGQUIT
, intr
);
251 (void) sigset(SIGTERM
, intr
);
253 if (cflag
&& !interrupt
) { /* command */
257 Pr
= Pcreate(argv
[0], &argv
[0], &err
, path
, sizeof (path
));
261 (void) fprintf(stderr
,
262 "%s: cannot control set-id or "
263 "unreadable object file: %s\n",
267 (void) fprintf(stderr
,
268 "%s: cannot control _LP64 "
269 "program: %s\n", command
, path
);
272 (void) fprintf(stderr
, "%s: cannot execute "
273 "program: %s\n", command
, argv
[0]);
277 (void) fprintf(stderr
, "%s: cannot find "
278 "program: %s\n", command
, argv
[0]);
284 (void) fprintf(stderr
,
285 "%s: %s\n", command
, Pcreate_error(err
));
291 if ((rc
= setpgsz(Pr
, Pstatus(Pr
)->pr_dmodel
, pgsz
)) != 0) {
292 (void) fprintf(stderr
, "%s: set page size "
293 "failed for program: %s\n", command
, argv
[0]);
294 (void) pr_exit(Pr
, 1);
299 * release the command to run, wait for it and
300 * return it's exit status if we can.
305 } while (pid
== -1 && errno
== EINTR
);
308 (void) fprintf(stderr
, "%s: wait() error: %s\n",
309 command
, strerror(errno
));
314 * Pass thru the child's exit value.
316 if (WIFEXITED(status
))
317 exit(WEXITSTATUS(status
));
318 exit(status
| WCOREFLG
);
323 while (--argc
>= 0 && !interrupt
) {
328 (void) fflush(stdout
); /* line-at-a-time */
330 /* get the specified pid and the psinfo struct */
332 pid
= proc_arg_psinfo(arg
, PR_ARG_PIDS
, &psinfo
, &gret
);
335 (void) fprintf(stderr
, "%s: cannot examine pid %s:"
336 " %s\n", command
, arg
, Pgrab_error(gret
));
337 if (!isdigit(arg
[0]) && strncmp(arg
, "/proc/", 6)) {
338 (void) fprintf(stderr
,
339 "\tdo not use -p option"
340 " to launch a command\n");
343 } else if ((Pr
= Pgrab(pid
, Fflag
, &gret
)) != NULL
) {
344 rc
= setpgsz(Pr
, Pstatus(Pr
)->pr_dmodel
, pgsz
);
346 (void) fprintf(stderr
, "%s: set page size "
347 "failed for pid: %d\n", command
, (int)pid
);
355 proc_unctrl_psinfo(&psinfo
);
356 (void) fprintf(stderr
, "%s: cannot set page "
357 "size for system process: %d [ %s ]\n",
358 command
, (int)pid
, psinfo
.pr_psargs
);
362 /* do it to own self */
363 rc
= setpgsz(NULL
, psinfo
.pr_dmodel
, pgsz
);
365 (void) fprintf(stderr
, "%s: set page"
366 "size failed for self: %d\n",
372 (void) fprintf(stderr
, "%s: %s: %d\n",
373 command
, Pgrab_error(gret
), (int)pid
);
380 if (interrupt
|| err
)
393 /* ------ begin specific code ------ */
395 /* set process page size */
398 setpgsz(struct ps_prochandle
*Pr
, int dmodel
, size_t pgsz
[])
404 static uint_t pgszcmd
[] =
405 {MHA_MAPSIZE_BSSBRK
, MHA_MAPSIZE_STACK
, MHA_MAPSIZE_VA
};
407 for (i
= E_HEAP
; i
<= E_ANON
; i
++) {
408 if (pgsz
[i
] == INVPGSZ
)
412 rc
= setpgsz_anon(Pr
, pgsz
[i
], dmodel
);
414 mpss
= setup_mha(pgszcmd
[i
], pgsz
[i
], dmodel
);
415 rc
= pr_memcntl(Pr
, NULL
, 0, MC_HAT_ADVISE
, mpss
, 0, 0);
419 (void) fprintf(stderr
, "%s: warning: set %s page size "
420 "failed (%s) for pid %d\n", command
, suboptstr
[i
],
421 strerror(errno
), (int)Pstatus(Pr
)->pr_pid
);
430 * Walk through the process' address space segments. Set all anonymous
431 * segments to the new page size.
434 setpgsz_anon(struct ps_prochandle
*Pr
, size_t pgsz
, int dmodel
)
440 const psinfo_t
*psinfo
;
441 const pstatus_t
*pstatus
;
447 * Setting the page size for anonymous segments on a process before it
448 * has run will have no effect, since it has not configured anonymous
449 * memory and the page size setting is not "sticky" inside the kernel.
450 * Any anonymous memory subsequently mapped will have the default page
456 if ((psinfo
= Ppsinfo(Pr
)) == NULL
)
458 if ((pstatus
= Pstatus(Pr
)) == NULL
)
462 pgsz
= discover_optimal_pagesize(Pr
, dmodel
, psinfo
->pr_pid
);
464 mpss
= setup_mha(MHA_MAPSIZE_VA
, pgsz
, dmodel
);
466 (void) snprintf(path
, PATH_MAX
, "/proc/%d/map", (int)psinfo
->pr_pid
);
467 if ((fd
= open(path
, O_RDONLY
)) < 0)
470 while (read(fd
, &map
, sizeof (map
)) == sizeof (map
)) {
471 if ((map
.pr_mflags
& MA_ANON
) == 0) {
474 } else if (map
.pr_mflags
& MA_SHARED
) {
475 /* Can't change pagesize for shared mappings. */
477 } else if (map
.pr_vaddr
+ map
.pr_size
>
478 pstatus
->pr_brkbase
&&
480 pstatus
->pr_brkbase
+ pstatus
->pr_brksize
) {
483 } else if (map
.pr_vaddr
>= pstatus
->pr_stkbase
&&
484 map
.pr_vaddr
+ map
.pr_size
<=
485 pstatus
->pr_stkbase
+ pstatus
->pr_stksize
) {
488 } else if (map
.pr_size
< pgsz
) {
494 * Find the first address in the segment that is page-aligned.
496 if (pgsz
== 0 || ((map
.pr_vaddr
% pgsz
) == 0))
499 addr
= map
.pr_vaddr
+ (pgsz
- (map
.pr_vaddr
% pgsz
));
502 * Calculate how many pages will fit in the segment.
507 size
= map
.pr_size
- (addr
% map
.pr_vaddr
) -
508 ((map
.pr_vaddr
+ map
.pr_size
) % pgsz
);
511 * If no aligned pages fit in the segment, ignore it.
517 rc
= pr_memcntl(Pr
, (caddr_t
)addr
, size
,
518 MC_HAT_ADVISE
, mpss
, 0, 0);
521 * If an error occurs on any segment, report the error here and
522 * then go on to try setting the page size for the remaining
526 (void) fprintf(stderr
, "%s: warning: set page size "
527 "failed (%s) for pid %d for anon segment at "
528 "address: %p\n", command
, strerror(errno
),
529 (int)psinfo
->pr_pid
, (void *)map
.pr_vaddr
);
538 * Discover the optimal page size for the process.
539 * Do this by creating a 4M segment in the target process, set its pagesize
540 * to 0, and read the map file to discover the page size selected by the system.
543 discover_optimal_pagesize(struct ps_prochandle
*Pr
, uint_t dmodel
, pid_t pid
)
546 size_t len
= pgsza
[nelem
- 1];
553 (void) snprintf(path
, PATH_MAX
, "/proc/%d/xmap", (int)pid
);
554 if ((fd
= open(path
, O_RDONLY
)) < 0)
557 if ((addr
= pr_mmap(Pr
, (void *)len
, len
, PROT_READ
| PROT_WRITE
,
558 MAP_PRIVATE
| MAP_ANON
| MAP_ALIGN
, -1, 0)) == MAP_FAILED
) {
562 mha
= setup_mha(MHA_MAPSIZE_VA
, 0, dmodel
);
563 if (pr_memcntl(Pr
, addr
, len
, MC_HAT_ADVISE
, mha
, 0, 0) < 0) {
568 * Touch a page in the segment so the hat mapping gets created.
570 (void) Pwrite(Pr
, &len
, sizeof (len
), (uintptr_t)addr
);
573 * Read through the address map looking for our segment.
576 while (read(fd
, &xmap
, sizeof (xmap
)) == sizeof (xmap
)) {
577 if (xmap
.pr_vaddr
== (uintptr_t)addr
)
580 if (xmap
.pr_vaddr
!= (uintptr_t)addr
)
583 size
= xmap
.pr_hatpagesize
;
586 if (addr
!= MAP_FAILED
) {
587 if (pr_munmap(Pr
, addr
, len
) == -1) {
588 (void) fprintf(stderr
,
589 "%s: couldn't delete segment at %p\n",
599 static struct memcntl_mha gmha
;
601 static struct memcntl_mha32 gmha32
;
606 setup_mha(uint_t command
, size_t pagesize
, int dmodel
)
609 if (dmodel
== PR_MODEL_ILP32
) {
610 gmha32
.mha_cmd
= command
;
611 gmha32
.mha_flags
= 0;
612 gmha32
.mha_pagesize
= pagesize
;
613 return ((caddr_t
)&gmha32
);
616 gmha
.mha_cmd
= command
;
618 gmha
.mha_pagesize
= pagesize
;
619 return ((caddr_t
)&gmha
);