summaryrefslogtreecommitdiff
path: root/miscutils/taskset.c (plain)
blob: b0fcb2ebabef6a2134438844185039785c9b15e1
1/* vi: set sw=4 ts=4: */
2/*
3 * taskset - retrieve or set a processes' CPU affinity
4 * Copyright (c) 2006 Bernhard Reutner-Fischer
5 *
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7 */
8
9//config:config TASKSET
10//config: bool "taskset"
11//config: default n # doesn't build on some non-x86 targets (m68k)
12//config: help
13//config: Retrieve or set a processes's CPU affinity.
14//config: This requires sched_{g,s}etaffinity support in your libc.
15//config:
16//config:config FEATURE_TASKSET_FANCY
17//config: bool "Fancy output"
18//config: default y
19//config: depends on TASKSET
20//config: help
21//config: Add code for fancy output. This merely silences a compiler-warning
22//config: and adds about 135 Bytes. May be needed for machines with alot
23//config: of CPUs.
24
25//applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP))
26//kbuild:lib-$(CONFIG_TASKSET) += taskset.o
27
28//usage:#define taskset_trivial_usage
29//usage: "[-p] [MASK] [PID | PROG ARGS]"
30//usage:#define taskset_full_usage "\n\n"
31//usage: "Set or get CPU affinity\n"
32//usage: "\n -p Operate on an existing PID"
33//usage:
34//usage:#define taskset_example_usage
35//usage: "$ taskset 0x7 ./dgemm_test&\n"
36//usage: "$ taskset -p 0x1 $!\n"
37//usage: "pid 4790's current affinity mask: 7\n"
38//usage: "pid 4790's new affinity mask: 1\n"
39//usage: "$ taskset 0x7 /bin/sh -c './taskset -p 0x1 $$'\n"
40//usage: "pid 6671's current affinity mask: 1\n"
41//usage: "pid 6671's new affinity mask: 1\n"
42//usage: "$ taskset -p 1\n"
43//usage: "pid 1's current affinity mask: 3\n"
44/*
45 Not yet implemented:
46 * -a/--all-tasks (affect all threads)
47 * -c/--cpu-list (specify CPUs via "1,3,5-7")
48 */
49
50#ifdef BIONIC_ICS
51#define _GNU_SOURCE 1
52#endif
53
54#include <sched.h>
55#include "libbb.h"
56
57#if ENABLE_FEATURE_TASKSET_FANCY
58#define TASKSET_PRINTF_MASK "%s"
59/* craft a string from the mask */
60static char *from_cpuset(cpu_set_t *mask)
61{
62 int i;
63 char *ret = NULL;
64 char *str = xzalloc((CPU_SETSIZE / 4) + 1); /* we will leak it */
65
66 for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) {
67 int val = 0;
68 int off;
69 for (off = 0; off <= 3; ++off)
70 if (CPU_ISSET(i + off, mask))
71 val |= 1 << off;
72 if (!ret && val)
73 ret = str;
74 *str++ = bb_hexdigits_upcase[val] | 0x20;
75 }
76 return ret;
77}
78#else
79#define TASKSET_PRINTF_MASK "%llx"
80static unsigned long long from_cpuset(cpu_set_t *mask)
81{
82 BUILD_BUG_ON(CPU_SETSIZE < 8*sizeof(int));
83
84 /* Take the least significant bits. Assume cpu_set_t is
85 * implemented as an array of unsigned long or unsigned
86 * int.
87 */
88 if (CPU_SETSIZE < 8*sizeof(long))
89 return *(unsigned*)mask;
90 if (CPU_SETSIZE < 8*sizeof(long long))
91 return *(unsigned long*)mask;
92# if BB_BIG_ENDIAN
93 if (sizeof(long long) > sizeof(long)) {
94 /* We can put two long in the long long, but they have to
95 * be swapped: the least significant word comes first in the
96 * array */
97 unsigned long *p = (void*)mask;
98 return p[0] + ((unsigned long long)p[1] << (8*sizeof(long)));
99 }
100# endif
101 return *(unsigned long long*)mask;
102}
103#endif
104
105
106int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
107int taskset_main(int argc UNUSED_PARAM, char **argv)
108{
109 cpu_set_t mask;
110 pid_t pid = 0;
111 unsigned opt_p;
112 const char *current_new;
113 char *pid_str;
114 char *aff = NULL;
115
116 /* NB: we mimic util-linux's taskset: -p does not take
117 * an argument, i.e., "-pN" is NOT valid, only "-p N"!
118 * Indeed, util-linux-2.13-pre7 uses:
119 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
120
121 opt_complementary = "-1"; /* at least 1 arg */
122 opt_p = getopt32(argv, "+p");
123 argv += optind;
124
125 if (opt_p) {
126 pid_str = *argv++;
127 if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
128 aff = pid_str;
129 pid_str = *argv; /* NB: *argv != NULL in this case */
130 }
131 /* else it was just "-p <pid>", and *argv == NULL */
132 pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
133 } else {
134 aff = *argv++; /* <aff> <cmd...> */
135 if (!*argv)
136 bb_show_usage();
137 }
138
139 current_new = "current\0new";
140 if (opt_p) {
141 print_aff:
142 if (sched_getaffinity(pid, sizeof(mask), &mask) < 0)
143 bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid);
144 printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
145 pid, current_new, from_cpuset(&mask));
146 if (!*argv) {
147 /* Either it was just "-p <pid>",
148 * or it was "-p <aff> <pid>" and we came here
149 * for the second time (see goto below) */
150 return EXIT_SUCCESS;
151 }
152 *argv = NULL;
153 current_new += 8; /* "new" */
154 }
155
156 /* Affinity was specified, translate it into cpu_set_t */
157 CPU_ZERO(&mask);
158 if (!ENABLE_FEATURE_TASKSET_FANCY) {
159 unsigned i;
160 unsigned long long m;
161
162 /* Do not allow zero mask: */
163 m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
164 i = 0;
165 do {
166 if (m & 1)
167 CPU_SET(i, &mask);
168 i++;
169 m >>= 1;
170 } while (m != 0);
171 } else {
172 unsigned i;
173 char *last_byte;
174 char *bin;
175 uint8_t bit_in_byte;
176
177 /* Cheap way to get "long enough" buffer */
178 bin = xstrdup(aff);
179
180 if (aff[0] != '0' || (aff[1]|0x20) != 'x') {
181/* TODO: decimal/octal masks are still limited to 2^64 */
182 unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
183 bin += strlen(bin);
184 last_byte = bin - 1;
185 while (m) {
186 *--bin = m & 0xff;
187 m >>= 8;
188 }
189 } else {
190 /* aff is "0x.....", we accept very long masks in this form */
191 last_byte = hex2bin(bin, aff + 2, INT_MAX);
192 if (!last_byte) {
193 bad_aff:
194 bb_error_msg_and_die("bad affinity '%s'", aff);
195 }
196 last_byte--; /* now points to the last byte */
197 }
198
199 i = 0;
200 bit_in_byte = 1;
201 while (last_byte >= bin) {
202 if (bit_in_byte & *last_byte) {
203 if (i >= CPU_SETSIZE)
204 goto bad_aff;
205 CPU_SET(i, &mask);
206 //bb_error_msg("bit %d set", i);
207 }
208 i++;
209 /* bit_in_byte is uint8_t! & 0xff is implied */
210 bit_in_byte = (bit_in_byte << 1);
211 if (!bit_in_byte) {
212 bit_in_byte = 1;
213 last_byte--;
214 }
215 }
216 }
217
218 /* Set pid's or our own (pid==0) affinity */
219 if (sched_setaffinity(pid, sizeof(mask), &mask))
220 bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid);
221
222 if (!argv[0]) /* "-p <aff> <pid> [...ignored...]" */
223 goto print_aff; /* print new affinity and exit */
224
225 BB_EXECVP_or_die(argv);
226}
227