diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-01-29 18:19:29 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-01-29 18:19:29 +0100 |
commit | ef0e76cc2783e83c6cbf90bf264b36b6bb60605b (patch) | |
tree | aa4b9f1f75e430ed3de5782fd405bcf6e15f722e | |
parent | 205d48e9480e3a60f2bf1094f67d837f1b44aea5 (diff) | |
download | busybox-w32-ef0e76cc2783e83c6cbf90bf264b36b6bb60605b.tar.gz busybox-w32-ef0e76cc2783e83c6cbf90bf264b36b6bb60605b.tar.bz2 busybox-w32-ef0e76cc2783e83c6cbf90bf264b36b6bb60605b.zip |
taskset: rewrite to be task size-agnostic
function old new delta
packed_usage 31130 31190 +60
taskset_main 623 525 -98
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 60/-98) Total: -38 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | miscutils/taskset.c | 205 |
1 files changed, 103 insertions, 102 deletions
diff --git a/miscutils/taskset.c b/miscutils/taskset.c index fb352ab8d..a87b9bd43 100644 --- a/miscutils/taskset.c +++ b/miscutils/taskset.c | |||
@@ -8,7 +8,7 @@ | |||
8 | 8 | ||
9 | //config:config TASKSET | 9 | //config:config TASKSET |
10 | //config: bool "taskset" | 10 | //config: bool "taskset" |
11 | //config: default n # doesn't build on some non-x86 targets (m68k) | 11 | //config: default y |
12 | //config: help | 12 | //config: help |
13 | //config: Retrieve or set a processes's CPU affinity. | 13 | //config: Retrieve or set a processes's CPU affinity. |
14 | //config: This requires sched_{g,s}etaffinity support in your libc. | 14 | //config: This requires sched_{g,s}etaffinity support in your libc. |
@@ -18,15 +18,15 @@ | |||
18 | //config: default y | 18 | //config: default y |
19 | //config: depends on TASKSET | 19 | //config: depends on TASKSET |
20 | //config: help | 20 | //config: help |
21 | //config: Add code for fancy output. This merely silences a compiler-warning | 21 | //config: Needed for machines with more than 32-64 CPUs: |
22 | //config: and adds about 135 Bytes. May be needed for machines with alot | 22 | //config: affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily long |
23 | //config: of CPUs. | 23 | //config: in this case. Otherwise, it is limited to sizeof(long). |
24 | 24 | ||
25 | //applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP)) | 25 | //applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP)) |
26 | //kbuild:lib-$(CONFIG_TASKSET) += taskset.o | 26 | //kbuild:lib-$(CONFIG_TASKSET) += taskset.o |
27 | 27 | ||
28 | //usage:#define taskset_trivial_usage | 28 | //usage:#define taskset_trivial_usage |
29 | //usage: "[-p] [MASK] [PID | PROG ARGS]" | 29 | //usage: "[-p] [HEXMASK] [PID | PROG ARGS]" |
30 | //usage:#define taskset_full_usage "\n\n" | 30 | //usage:#define taskset_full_usage "\n\n" |
31 | //usage: "Set or get CPU affinity\n" | 31 | //usage: "Set or get CPU affinity\n" |
32 | //usage: "\n -p Operate on an existing PID" | 32 | //usage: "\n -p Operate on an existing PID" |
@@ -42,71 +42,80 @@ | |||
42 | //usage: "$ taskset -p 1\n" | 42 | //usage: "$ taskset -p 1\n" |
43 | //usage: "pid 1's current affinity mask: 3\n" | 43 | //usage: "pid 1's current affinity mask: 3\n" |
44 | /* | 44 | /* |
45 | Not yet implemented: | 45 | * Not yet implemented: |
46 | * -a/--all-tasks (affect all threads) | 46 | * -a/--all-tasks (affect all threads) |
47 | * needs to get TIDs from /proc/PID/task/ and use _them_ as "pid" in sched_setaffinity(pid) | ||
47 | * -c/--cpu-list (specify CPUs via "1,3,5-7") | 48 | * -c/--cpu-list (specify CPUs via "1,3,5-7") |
48 | */ | 49 | */ |
49 | 50 | ||
50 | #include <sched.h> | 51 | #include <sched.h> |
51 | #include "libbb.h" | 52 | #include "libbb.h" |
52 | 53 | ||
54 | typedef unsigned long ul; | ||
55 | #define SZOF_UL (unsigned)(sizeof(ul)) | ||
56 | #define BITS_UL (unsigned)(sizeof(ul)*8) | ||
57 | #define MASK_UL (unsigned)(sizeof(ul)*8 - 1) | ||
58 | |||
53 | #if ENABLE_FEATURE_TASKSET_FANCY | 59 | #if ENABLE_FEATURE_TASKSET_FANCY |
54 | #define TASKSET_PRINTF_MASK "%s" | 60 | #define TASKSET_PRINTF_MASK "%s" |
55 | /* craft a string from the mask */ | 61 | /* craft a string from the mask */ |
56 | static char *from_cpuset(cpu_set_t *mask) | 62 | static char *from_mask(const ul *mask, unsigned sz_in_bytes) |
57 | { | 63 | { |
58 | int i; | 64 | char *str = xzalloc((sz_in_bytes+1) * 2); /* we will leak it */ |
59 | char *ret = NULL; | 65 | char *p = str; |
60 | char *str = xzalloc((CPU_SETSIZE / 4) + 1); /* we will leak it */ | 66 | for (;;) { |
61 | 67 | ul v = *mask++; | |
62 | for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) { | 68 | if (SZOF_UL == 4) |
63 | int val = 0; | 69 | p += sprintf(p, "%08lx", v); |
64 | int off; | 70 | if (SZOF_UL == 8) |
65 | for (off = 0; off <= 3; ++off) | 71 | p += sprintf(p, "%016lx", v); |
66 | if (CPU_ISSET(i + off, mask)) | 72 | if (SZOF_UL == 16) |
67 | val |= 1 << off; | 73 | p += sprintf(p, "%032lx", v); /* :) */ |
68 | if (!ret && val) | 74 | sz_in_bytes -= SZOF_UL; |
69 | ret = str; | 75 | if ((int)sz_in_bytes <= 0) |
70 | *str++ = bb_hexdigits_upcase[val] | 0x20; | 76 | break; |
71 | } | 77 | } |
72 | return ret; | 78 | while (str[0] == '0' && str[1]) |
79 | str++; | ||
80 | return str; | ||
73 | } | 81 | } |
74 | #else | 82 | #else |
75 | #define TASKSET_PRINTF_MASK "%llx" | 83 | #define TASKSET_PRINTF_MASK "%lx" |
76 | static unsigned long long from_cpuset(cpu_set_t *mask) | 84 | static unsigned long long from_mask(ul *mask, unsigned sz_in_bytes UNUSED_PARAM) |
77 | { | 85 | { |
78 | BUILD_BUG_ON(CPU_SETSIZE < 8*sizeof(int)); | 86 | return *mask; |
79 | |||
80 | /* Take the least significant bits. Assume cpu_set_t is | ||
81 | * implemented as an array of unsigned long or unsigned | ||
82 | * int. | ||
83 | */ | ||
84 | if (CPU_SETSIZE < 8*sizeof(long)) | ||
85 | return *(unsigned*)mask; | ||
86 | if (CPU_SETSIZE < 8*sizeof(long long)) | ||
87 | return *(unsigned long*)mask; | ||
88 | # if BB_BIG_ENDIAN | ||
89 | if (sizeof(long long) > sizeof(long)) { | ||
90 | /* We can put two long in the long long, but they have to | ||
91 | * be swapped: the least significant word comes first in the | ||
92 | * array */ | ||
93 | unsigned long *p = (void*)mask; | ||
94 | return p[0] + ((unsigned long long)p[1] << (8*sizeof(long))); | ||
95 | } | ||
96 | # endif | ||
97 | return *(unsigned long long*)mask; | ||
98 | } | 87 | } |
99 | #endif | 88 | #endif |
100 | 89 | ||
90 | static unsigned long *get_aff(int pid, unsigned *sz) | ||
91 | { | ||
92 | int r; | ||
93 | unsigned long *mask = NULL; | ||
94 | unsigned sz_in_bytes = *sz; | ||
95 | |||
96 | for (;;) { | ||
97 | mask = xrealloc(mask, sz_in_bytes); | ||
98 | r = sched_getaffinity(pid, sz_in_bytes, (void*)mask); | ||
99 | if (r == 0) | ||
100 | break; | ||
101 | sz_in_bytes *= 2; | ||
102 | if (errno == EINVAL && (int)sz_in_bytes > 0) | ||
103 | continue; | ||
104 | bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid); | ||
105 | } | ||
106 | //bb_error_msg("get mask[0]:%lx sz_in_bytes:%d", mask[0], sz_in_bytes); | ||
107 | *sz = sz_in_bytes; | ||
108 | return mask; | ||
109 | } | ||
101 | 110 | ||
102 | int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 111 | int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
103 | int taskset_main(int argc UNUSED_PARAM, char **argv) | 112 | int taskset_main(int argc UNUSED_PARAM, char **argv) |
104 | { | 113 | { |
105 | cpu_set_t mask; | 114 | ul *mask; |
115 | unsigned mask_size_in_bytes; | ||
106 | pid_t pid = 0; | 116 | pid_t pid = 0; |
107 | unsigned opt_p; | 117 | unsigned opt_p; |
108 | const char *current_new; | 118 | const char *current_new; |
109 | char *pid_str; | ||
110 | char *aff = aff; /* for compiler */ | 119 | char *aff = aff; /* for compiler */ |
111 | 120 | ||
112 | /* NB: we mimic util-linux's taskset: -p does not take | 121 | /* NB: we mimic util-linux's taskset: -p does not take |
@@ -119,7 +128,7 @@ int taskset_main(int argc UNUSED_PARAM, char **argv) | |||
119 | argv += optind; | 128 | argv += optind; |
120 | 129 | ||
121 | if (opt_p) { | 130 | if (opt_p) { |
122 | pid_str = *argv++; | 131 | char *pid_str = *argv++; |
123 | if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */ | 132 | if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */ |
124 | aff = pid_str; | 133 | aff = pid_str; |
125 | pid_str = *argv; /* NB: *argv != NULL in this case */ | 134 | pid_str = *argv; /* NB: *argv != NULL in this case */ |
@@ -132,13 +141,14 @@ int taskset_main(int argc UNUSED_PARAM, char **argv) | |||
132 | bb_show_usage(); | 141 | bb_show_usage(); |
133 | } | 142 | } |
134 | 143 | ||
144 | mask_size_in_bytes = SZOF_UL; | ||
145 | mask = NULL; | ||
135 | current_new = "current\0new"; | 146 | current_new = "current\0new"; |
136 | if (opt_p) { | ||
137 | print_aff: | 147 | print_aff: |
138 | if (sched_getaffinity(pid, sizeof(mask), &mask) < 0) | 148 | mask = get_aff(pid, &mask_size_in_bytes); |
139 | bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid); | 149 | if (opt_p) { |
140 | printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n", | 150 | printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n", |
141 | pid, current_new, from_cpuset(&mask)); | 151 | pid, current_new, from_mask(mask, mask_size_in_bytes)); |
142 | if (!*argv) { | 152 | if (!*argv) { |
143 | /* Either it was just "-p <pid>", | 153 | /* Either it was just "-p <pid>", |
144 | * or it was "-p <aff> <pid>" and we came here | 154 | * or it was "-p <aff> <pid>" and we came here |
@@ -148,72 +158,63 @@ int taskset_main(int argc UNUSED_PARAM, char **argv) | |||
148 | *argv = NULL; | 158 | *argv = NULL; |
149 | current_new += 8; /* "new" */ | 159 | current_new += 8; /* "new" */ |
150 | } | 160 | } |
161 | memset(mask, 0, mask_size_in_bytes); | ||
151 | 162 | ||
152 | /* Affinity was specified, translate it into cpu_set_t */ | 163 | /* Affinity was specified, translate it into mask */ |
153 | CPU_ZERO(&mask); | 164 | /* it is always in hex, skip "0x" if it exists */ |
154 | if (!ENABLE_FEATURE_TASKSET_FANCY) { | 165 | if (aff[0] == '0' && (aff[1]|0x20) == 'x') |
155 | unsigned i; | 166 | aff += 2; |
156 | unsigned long long m; | ||
157 | 167 | ||
168 | if (!ENABLE_FEATURE_TASKSET_FANCY) { | ||
158 | /* Do not allow zero mask: */ | 169 | /* Do not allow zero mask: */ |
159 | m = xstrtoull_range(aff, 0, 1, ULLONG_MAX); | 170 | mask[0] = xstrtoul_range(aff, 16, 1, ULONG_MAX); |
160 | i = 0; | ||
161 | do { | ||
162 | if (m & 1) | ||
163 | CPU_SET(i, &mask); | ||
164 | i++; | ||
165 | m >>= 1; | ||
166 | } while (m != 0); | ||
167 | } else { | 171 | } else { |
168 | unsigned i; | 172 | unsigned i; |
169 | char *last_byte; | 173 | char *last_char; |
170 | char *bin; | 174 | |
171 | uint8_t bit_in_byte; | 175 | i = 0; /* bit pos in mask[] */ |
172 | 176 | ||
173 | /* Cheap way to get "long enough" buffer */ | 177 | /* aff is ASCII hex string, accept very long masks in this form. |
174 | bin = xstrdup(aff); | 178 | * Process hex string AABBCCDD... to ulong mask[] |
175 | 179 | * from the rightmost nibble, which is least-significant. | |
176 | if (aff[0] != '0' || (aff[1]|0x20) != 'x') { | 180 | * Bits not fitting into mask[] are ignored: (example: 1234 |
177 | /* TODO: decimal/octal masks are still limited to 2^64 */ | 181 | * in 12340000000000000000000000000000000000000ff) |
178 | unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX); | 182 | */ |
179 | bin += strlen(bin); | 183 | last_char = strchrnul(aff, '\0'); |
180 | last_byte = bin - 1; | 184 | while (last_char > aff) { |
181 | while (m) { | 185 | char c; |
182 | *--bin = m & 0xff; | 186 | ul val; |
183 | m >>= 8; | 187 | |
184 | } | 188 | last_char--; |
185 | } else { | 189 | c = *last_char; |
186 | /* aff is "0x.....", we accept very long masks in this form */ | 190 | if (isdigit(c)) |
187 | last_byte = hex2bin(bin, aff + 2, INT_MAX); | 191 | val = c - '0'; |
188 | if (!last_byte) { | 192 | else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') |
189 | bad_aff: | 193 | val = (c|0x20) - ('a' - 10); |
194 | else | ||
190 | bb_error_msg_and_die("bad affinity '%s'", aff); | 195 | bb_error_msg_and_die("bad affinity '%s'", aff); |
191 | } | ||
192 | last_byte--; /* now points to the last byte */ | ||
193 | } | ||
194 | 196 | ||
195 | i = 0; | 197 | if (i < mask_size_in_bytes * 8) { |
196 | bit_in_byte = 1; | 198 | mask[i / BITS_UL] |= val << (i & MASK_UL); |
197 | while (last_byte >= bin) { | ||
198 | if (bit_in_byte & *last_byte) { | ||
199 | if (i >= CPU_SETSIZE) | ||
200 | goto bad_aff; | ||
201 | CPU_SET(i, &mask); | ||
202 | //bb_error_msg("bit %d set", i); | 199 | //bb_error_msg("bit %d set", i); |
203 | } | 200 | } |
204 | i++; | 201 | /* else: |
205 | /* bit_in_byte is uint8_t! & 0xff is implied */ | 202 | * We can error out here, but we don't. |
206 | bit_in_byte = (bit_in_byte << 1); | 203 | * For one, kernel itself ignores bits in mask[] |
207 | if (!bit_in_byte) { | 204 | * which do not map to any CPUs. |
208 | bit_in_byte = 1; | 205 | * If mask has one 32-bit long, |
209 | last_byte--; | 206 | * but you have only 8 CPUs, all bits beyond first 8 |
210 | } | 207 | * are ignored, silently. |
208 | * No point in making bits past 31th to be errors. | ||
209 | */ | ||
210 | i += 4; | ||
211 | } | 211 | } |
212 | } | 212 | } |
213 | 213 | ||
214 | /* Set pid's or our own (pid==0) affinity */ | 214 | /* Set pid's or our own (pid==0) affinity */ |
215 | if (sched_setaffinity(pid, sizeof(mask), &mask)) | 215 | if (sched_setaffinity(pid, mask_size_in_bytes, (void*)mask)) |
216 | bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid); | 216 | bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid); |
217 | //bb_error_msg("set mask[0]:%lx", mask[0]); | ||
217 | 218 | ||
218 | if (!argv[0]) /* "-p <aff> <pid> [...ignored...]" */ | 219 | if (!argv[0]) /* "-p <aff> <pid> [...ignored...]" */ |
219 | goto print_aff; /* print new affinity and exit */ | 220 | goto print_aff; /* print new affinity and exit */ |