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 */ |
60 | static 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" |
80 | static 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 | |
106 | int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
107 | int 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 |