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 2015 Ryan Zezeski
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
29 * pbind - bind a process to a processor (non-exclusively)
32 #include <sys/types.h>
33 #include <sys/procset.h>
34 #include <sys/processor.h>
46 #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
47 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
50 #define ERR_OK 0 /* exit status for success */
51 #define ERR_FAIL 1 /* exit status for errors */
52 #define ERR_USAGE 2 /* exit status for usage errors */
54 static char *progname
;
63 #define MAX_PROCFS_PATH 80
67 warn(char *format
, ...)
72 (void) fprintf(stderr
, "%s: ", progname
);
73 va_start(alist
, format
);
74 (void) vfprintf(stderr
, format
, alist
);
76 if (strchr(format
, '\n') == NULL
)
77 (void) fprintf(stderr
, ": %s\n", strerror(err
));
82 die(char *format
, ...)
87 (void) fprintf(stderr
, "%s: ", progname
);
88 va_start(alist
, format
);
89 (void) vfprintf(stderr
, format
, alist
);
91 if (strchr(format
, '\n') == NULL
)
92 (void) fprintf(stderr
, ": %s\n", strerror(err
));
100 query_out(id_t pid
, id_t lwpid
, processorid_t cpu
)
106 (void) snprintf(pidstr
, 20, "%d", (int)pid
);
109 (void) snprintf(pidstr
, 20, "%d/%d", (int)pid
, (int)lwpid
);
113 if (cpu
== PBIND_NONE
)
114 (void) printf(gettext("%s id %s: not bound\n"),
117 (void) printf(gettext("%s id %s: %d\n"),
118 proclwp
, pidstr
, cpu
);
125 bind_err(processorid_t cpu
, id_t pid
, id_t lwpid
, int err
)
131 msg
= gettext("unbind");
134 msg
= gettext("query");
137 msg
= gettext("bind");
141 warn(gettext("cannot %s pid %d: %s\n"), msg
,
142 (int)pid
, strerror(err
));
144 warn(gettext("cannot %s lwpid %d/%d: %s\n"), msg
,
145 (int)pid
, (int)lwpid
, strerror(err
));
152 bind_out(id_t pid
, id_t lwpid
, processorid_t old
, processorid_t
new)
158 (void) snprintf(pidstr
, 20, "%d", (int)pid
);
161 (void) snprintf(pidstr
, 20, "%d/%d", (int)pid
, (int)lwpid
);
165 if (old
== PBIND_NONE
) {
166 if (new == PBIND_NONE
)
167 (void) printf(gettext("%s id %s: was not bound, "
168 "now not bound\n"), proclwp
, pidstr
);
170 (void) printf(gettext("%s id %s: was not bound, "
171 "now %d\n"), proclwp
, pidstr
, new);
173 if (new == PBIND_NONE
)
174 (void) printf(gettext("%s id %s: was %d, "
175 "now not bound\n"), proclwp
, pidstr
, old
);
177 (void) printf(gettext("%s id %s: was %d, "
178 "now %d\n"), proclwp
, pidstr
, old
, new);
182 static struct ps_prochandle
*
186 struct ps_prochandle
*Pr
;
188 if ((Pr
= Pgrab(pid
, 0, &ret
)) == NULL
) {
189 warn(gettext("cannot control process %d: %s\n"),
190 (int)pid
, Pgrab_error(ret
));
196 * Set run-on-last-close flag so the controlled process
197 * runs even if we die on a signal, and create an agent LWP.
199 if (Psetflags(Pr
, PR_RLC
) != 0 || Pcreate_agent(Pr
) != 0) {
200 warn(gettext("cannot control process %d\n"), (int)pid
);
209 rele_proc(struct ps_prochandle
*Pr
)
218 bind_lwp(struct ps_prochandle
*Pr
, id_t pid
, id_t lwpid
, processorid_t cpu
)
220 processorid_t old_cpu
;
222 if (pr_processor_bind(Pr
, P_LWPID
, lwpid
, cpu
, &old_cpu
) < 0) {
223 bind_err(cpu
, pid
, lwpid
, errno
);
227 query_out(pid
, lwpid
, old_cpu
);
229 bind_out(pid
, lwpid
, old_cpu
, cpu
);
234 * Query, set, or clear bindings for the range of LWPs in the given process.
237 do_lwps(id_t pid
, const char *range
, processorid_t cpu
)
239 char procfile
[MAX_PROCFS_PATH
];
240 struct ps_prochandle
*Pr
;
241 struct prheader header
;
242 processorid_t binding
;
243 struct lwpsinfo
*lwp
;
249 * Report bindings for LWPs in process 'pid'.
251 (void) snprintf(procfile
, MAX_PROCFS_PATH
,
252 "/proc/%d/lpsinfo", (int)pid
);
253 if ((fd
= open(procfile
, O_RDONLY
)) < 0) {
256 bind_err(cpu
, pid
, -1, errno
);
259 if (pread(fd
, &header
, sizeof (header
), 0) != sizeof (header
)) {
261 bind_err(cpu
, pid
, -1, errno
);
264 nent
= header
.pr_nent
;
265 size
= header
.pr_entsize
* nent
;
266 ptr
= lpsinfo
= malloc(size
);
267 if (lpsinfo
== NULL
) {
268 bind_err(cpu
, pid
, -1, errno
);
271 if (pread(fd
, lpsinfo
, size
, sizeof (header
)) != size
) {
272 bind_err(cpu
, pid
, -1, errno
);
278 if ((bflag
|| uflag
) && (Pr
= grab_proc(pid
)) == NULL
) {
284 for (i
= 0; i
< nent
; i
++, ptr
+= header
.pr_entsize
) {
286 lwp
= (lwpsinfo_t
*)ptr
;
287 binding
= lwp
->pr_bindpro
;
288 if (!proc_lwp_in_set(range
, lwp
->pr_lwpid
))
292 bind_lwp(Pr
, pid
, lwp
->pr_lwpid
, cpu
);
293 else if (binding
!= PBIND_NONE
)
294 query_out(pid
, lwp
->pr_lwpid
, binding
);
301 warn(gettext("cannot %s lwpid %d/%s: "
302 "No matching LWPs found\n"),
303 bflag
? "bind" : "query", pid
, range
);
311 query_all_proc(psinfo_t
*psinfo
, lwpsinfo_t
*lwpsinfo
, void *arg
)
313 id_t pid
= psinfo
->pr_pid
;
314 processorid_t binding
;
316 if (processor_bind(P_PID
, pid
, PBIND_QUERY
, &binding
) < 0) {
318 * Ignore search errors. The process may have exited
319 * since we read the directory.
323 bind_err(PBIND_QUERY
, pid
, -1, errno
);
327 if (binding
!= PBIND_NONE
)
328 query_out(pid
, -1, binding
);
333 query_all_lwp(psinfo_t
*psinfo
, lwpsinfo_t
*lwpsinfo
, void *arg
)
335 id_t pid
= psinfo
->pr_pid
;
336 id_t lwpid
= lwpsinfo
->pr_lwpid
;
337 processorid_t
*cpuid
= arg
;
338 processorid_t binding
= lwpsinfo
->pr_bindpro
;
340 if (psinfo
->pr_nlwp
== 1)
341 lwpid
= -1; /* report process bindings if only 1 lwp */
342 if ((cpuid
!= NULL
&& *cpuid
== binding
) ||
343 (cpuid
== NULL
&& binding
!= PBIND_NONE
))
344 query_out(pid
, lwpid
, binding
);
349 * Execute the cmd with args while bound to cpu. Does not return:
350 * either executes cmd successfully or dies trying.
353 exec_cmd(processorid_t cpu
, char *cmd
, char **args
)
355 if (processor_bind(P_PID
, P_MYID
, cpu
, NULL
) == -1) {
356 bind_err(cpu
, getpid(), -1, errno
);
360 if (execvp(cmd
, args
) == -1)
361 die(gettext("failed to exec %s\n"), cmd
);
365 * Attempt to parse str as a CPU identifier. Return the identifier or
374 cpu
= strtol(str
, &endstr
, 10);
375 if (endstr
!= NULL
&& *endstr
!= '\0' || cpu
< 0)
376 die(gettext("invalid processor ID %s\n"), optarg
);
384 (void) fprintf(stderr
,
385 gettext("usage: \n\t%1$s -b processor_id pid[/lwpids] ...\n"
386 "\t%1$s -e processor_id cmd [args...]\n"
387 "\t%1$s -U [processor_id] ...\n"
388 "\t%1$s -Q [processor_id] ...\n"
389 "\t%1$s -u pid[/lwpids] ...\n"
390 "\t%1$s [-q] [pid[/lwpids] ...]\n"),
396 main(int argc
, char *argv
[])
401 processorid_t cpu
, old_cpu
;
404 progname
= argv
[0]; /* put actual command name in messages */
406 (void) setlocale(LC_ALL
, ""); /* setup localization */
407 (void) textdomain(TEXT_DOMAIN
);
409 while ((c
= getopt(argc
, argv
, "b:e:qQuU")) != EOF
) {
414 cpu
= parse_cpu(optarg
);
419 cpu
= parse_cpu(optarg
);
448 * Make sure that at most one of the options b, e, q, Q, u, or
451 c
= bflag
+ eflag
+ qflag
+ Qflag
+ uflag
+ Uflag
;
452 if (c
< 1) { /* nothing specified */
453 qflag
= 1; /* default to query */
456 warn(gettext("options -b, -e, -q, -Q, -u and -U "
457 "are mutually exclusive\n"));
466 * Handle query of all processes.
469 if (bflag
|| uflag
) {
470 warn(gettext("must specify at least one pid\n"));
474 warn(gettext("must specify command\n"));
478 if (processor_bind(P_ALL
, 0, PBIND_NONE
, &old_cpu
) != 0)
479 die(gettext("failed to unbind some LWPs"));
482 (void) proc_walk(query_all_lwp
, NULL
, PR_WALK_LWP
);
485 (void) proc_walk(query_all_proc
, NULL
, PR_WALK_PROC
);
491 exec_cmd(cpu
, argv
[0], argv
);
493 if (Qflag
|| Uflag
) {
495 * Go through listed processor IDs.
497 for (; argc
> 0; argv
++, argc
--) {
499 cpu
= (id_t
)strtol(*argv
, &endstr
, 10);
500 if (errno
!= 0 || (endstr
!= NULL
&& *endstr
!= '\0') ||
501 p_online(cpu
, P_STATUS
) == -1) {
502 warn(gettext("invalid processor ID\n"));
506 (void) proc_walk(query_all_lwp
,
511 if (processor_bind(P_CPUID
, cpu
,
512 PBIND_NONE
, &old_cpu
) != 0) {
513 warn(gettext("failed to unbind from "
514 "processor %d"), (int)cpu
);
524 * Go through listed process[/lwp_ranges].
526 for (; argc
> 0; argv
++, argc
--) {
528 pid
= (id_t
)strtol(*argv
, &endstr
, 10);
530 (endstr
!= NULL
&& *endstr
!= '\0' && *endstr
!= '/')) {
531 warn(gettext("invalid process ID: %s\n"), *argv
);
534 if (endstr
!= NULL
&& *endstr
== '/') {
536 * Handle lwp range case
538 const char *lwps
= (const char *)(++endstr
);
540 proc_lwp_range_valid(lwps
) != 0) {
541 warn(gettext("invalid lwp range "
542 "for pid %d\n"), (int)pid
);
547 (void) proc_initstdio();
548 ret
= do_lwps(pid
, lwps
, qflag
? PBIND_QUERY
: cpu
);
550 (void) proc_finistdio();
555 * Handle whole process case.
557 if (processor_bind(P_PID
, pid
, cpu
, &old_cpu
) < 0) {
558 bind_err(cpu
, pid
, -1, errno
);
563 query_out(pid
, -1, old_cpu
);
565 bind_out(pid
, -1, old_cpu
, cpu
);