]>
Commit | Line | Data |
---|---|---|
0b57c6c2 HC |
1 | /* |
2 | * chcpu - CPU configuration tool | |
3 | * | |
4 | * Copyright IBM Corp. 2011 | |
5 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it would be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
7cebf0bb SK |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
0b57c6c2 HC |
20 | */ |
21 | ||
22 | #include <ctype.h> | |
23 | #include <dirent.h> | |
24 | #include <errno.h> | |
25 | #include <fcntl.h> | |
26 | #include <getopt.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <sys/utsname.h> | |
31 | #include <unistd.h> | |
32 | #include <stdarg.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/stat.h> | |
35 | ||
36 | #include "cpuset.h" | |
37 | #include "nls.h" | |
38 | #include "xalloc.h" | |
39 | #include "c.h" | |
40 | #include "strutils.h" | |
41 | #include "bitops.h" | |
9bc2b4b1 | 42 | #include "path.h" |
efb8854f | 43 | #include "closestream.h" |
264a6b0a SK |
44 | #include "optutils.h" |
45 | ||
46 | #define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}" | |
0b57c6c2 | 47 | |
48fc00c1 KZ |
48 | /* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */ |
49 | #define CHCPU_EXIT_SOMEOK 64 | |
50 | ||
0b57c6c2 | 51 | #define _PATH_SYS_CPU "/sys/devices/system/cpu" |
0b57c6c2 | 52 | |
5b88ce6a | 53 | static cpu_set_t *onlinecpus; |
4b11df2c | 54 | static int maxcpus; |
5b88ce6a HC |
55 | |
56 | #define is_cpu_online(cpu) (CPU_ISSET_S((cpu), CPU_ALLOC_SIZE(maxcpus), onlinecpus)) | |
57 | #define num_online_cpus() (CPU_COUNT_S(CPU_ALLOC_SIZE(maxcpus), onlinecpus)) | |
58 | ||
0b57c6c2 HC |
59 | enum { |
60 | CMD_CPU_ENABLE = 0, | |
61 | CMD_CPU_DISABLE, | |
62 | CMD_CPU_CONFIGURE, | |
63 | CMD_CPU_DECONFIGURE, | |
64 | CMD_CPU_RESCAN, | |
65 | CMD_CPU_DISPATCH_HORIZONTAL, | |
66 | CMD_CPU_DISPATCH_VERTICAL, | |
67 | }; | |
68 | ||
48fc00c1 KZ |
69 | /* returns: 0 = success |
70 | * < 0 = failure | |
71 | * > 0 = partial success | |
72 | */ | |
a375d911 | 73 | static int cpu_enable(struct path_cxt *sys, cpu_set_t *cpu_set, size_t setsize, int enable) |
0b57c6c2 | 74 | { |
60727494 | 75 | int cpu; |
9bc2b4b1 | 76 | int online, rc; |
5b88ce6a | 77 | int configured = -1; |
60727494 | 78 | int fails = 0; |
0b57c6c2 | 79 | |
60727494 | 80 | for (cpu = 0; cpu < maxcpus; cpu++) { |
538b50cb | 81 | if (!CPU_ISSET_S(cpu, setsize, cpu_set)) |
0b57c6c2 | 82 | continue; |
a375d911 | 83 | if (ul_path_accessf(sys, F_OK, "cpu%d", cpu) != 0) { |
e3ca1312 | 84 | warnx(_("CPU %u does not exist"), cpu); |
48fc00c1 | 85 | fails++; |
0b57c6c2 HC |
86 | continue; |
87 | } | |
a375d911 | 88 | if (ul_path_accessf(sys, F_OK, "cpu%d/online", cpu) != 0) { |
e3ca1312 | 89 | warnx(_("CPU %u is not hot pluggable"), cpu); |
48fc00c1 | 90 | fails++; |
0b57c6c2 HC |
91 | continue; |
92 | } | |
a375d911 KZ |
93 | if (ul_path_readf_s32(sys, &online, "cpu%d/online", cpu) == 0 |
94 | && online == 1 | |
95 | && enable == 1) { | |
e3ca1312 | 96 | printf(_("CPU %u is already enabled\n"), cpu); |
0b57c6c2 HC |
97 | continue; |
98 | } | |
a375d911 | 99 | if (online == 0 && enable == 0) { |
e3ca1312 | 100 | printf(_("CPU %u is already disabled\n"), cpu); |
0b57c6c2 HC |
101 | continue; |
102 | } | |
a375d911 KZ |
103 | if (ul_path_accessf(sys, F_OK, "cpu%d/configure", cpu) == 0) |
104 | ul_path_readf_s32(sys, &configured, "cpu%d/configure", cpu); | |
0b57c6c2 | 105 | if (enable) { |
a375d911 KZ |
106 | rc = ul_path_writef_string(sys, "1", "cpu%d/online", cpu); |
107 | if (rc != 0 && configured == 0) { | |
e3ca1312 | 108 | warn(_("CPU %u enable failed (CPU is deconfigured)"), cpu); |
48fc00c1 | 109 | fails++; |
a375d911 | 110 | } else if (rc != 0) { |
e3ca1312 | 111 | warn(_("CPU %u enable failed"), cpu); |
48fc00c1 KZ |
112 | fails++; |
113 | } else | |
e3ca1312 | 114 | printf(_("CPU %u enabled\n"), cpu); |
0b57c6c2 | 115 | } else { |
5b88ce6a | 116 | if (onlinecpus && num_online_cpus() == 1) { |
e3ca1312 | 117 | warnx(_("CPU %u disable failed (last enabled CPU)"), cpu); |
48fc00c1 | 118 | fails++; |
5b88ce6a HC |
119 | continue; |
120 | } | |
a375d911 KZ |
121 | rc = ul_path_writef_string(sys, "0", "cpu%d/online", cpu); |
122 | if (rc != 0) { | |
e3ca1312 | 123 | warn(_("CPU %u disable failed"), cpu); |
48fc00c1 KZ |
124 | fails++; |
125 | } else { | |
e3ca1312 | 126 | printf(_("CPU %u disabled\n"), cpu); |
5b88ce6a | 127 | if (onlinecpus) |
538b50cb | 128 | CPU_CLR_S(cpu, setsize, onlinecpus); |
5b88ce6a | 129 | } |
0b57c6c2 | 130 | } |
0b57c6c2 | 131 | } |
48fc00c1 | 132 | |
60727494 | 133 | return fails == 0 ? 0 : fails == maxcpus ? -1 : 1; |
0b57c6c2 HC |
134 | } |
135 | ||
a375d911 | 136 | static int cpu_rescan(struct path_cxt *sys) |
0b57c6c2 | 137 | { |
a375d911 | 138 | if (ul_path_access(sys, F_OK, "rescan") != 0) |
0b57c6c2 | 139 | errx(EXIT_FAILURE, _("This system does not support rescanning of CPUs")); |
a375d911 KZ |
140 | |
141 | if (ul_path_write_string(sys, "1", "rescan") != 0) | |
0b57c6c2 | 142 | err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs")); |
a375d911 | 143 | |
5b88ce6a | 144 | printf(_("Triggered rescan of CPUs\n")); |
48fc00c1 | 145 | return 0; |
0b57c6c2 HC |
146 | } |
147 | ||
a375d911 | 148 | static int cpu_set_dispatch(struct path_cxt *sys, int mode) |
0b57c6c2 | 149 | { |
a375d911 | 150 | if (ul_path_access(sys, F_OK, "dispatching") != 0) |
0b57c6c2 HC |
151 | errx(EXIT_FAILURE, _("This system does not support setting " |
152 | "the dispatching mode of CPUs")); | |
0b57c6c2 | 153 | if (mode == 0) { |
a375d911 | 154 | if (ul_path_write_string(sys, "0", "dispatching") != 0) |
0b57c6c2 | 155 | err(EXIT_FAILURE, _("Failed to set horizontal dispatch mode")); |
a375d911 | 156 | |
7007991f | 157 | printf(_("Successfully set horizontal dispatching mode\n")); |
0b57c6c2 | 158 | } else { |
a375d911 | 159 | if (ul_path_write_string(sys, "1", "dispatching") != 0) |
0b57c6c2 | 160 | err(EXIT_FAILURE, _("Failed to set vertical dispatch mode")); |
a375d911 | 161 | |
7007991f | 162 | printf(_("Successfully set vertical dispatching mode\n")); |
0b57c6c2 | 163 | } |
48fc00c1 | 164 | return 0; |
0b57c6c2 HC |
165 | } |
166 | ||
48fc00c1 KZ |
167 | /* returns: 0 = success |
168 | * < 0 = failure | |
169 | * > 0 = partial success | |
170 | */ | |
a375d911 | 171 | static int cpu_configure(struct path_cxt *sys, cpu_set_t *cpu_set, size_t setsize, int configure) |
0b57c6c2 | 172 | { |
60727494 | 173 | int cpu; |
9bc2b4b1 | 174 | int rc, current; |
60727494 | 175 | int fails = 0; |
0b57c6c2 | 176 | |
60727494 | 177 | for (cpu = 0; cpu < maxcpus; cpu++) { |
538b50cb | 178 | if (!CPU_ISSET_S(cpu, setsize, cpu_set)) |
0b57c6c2 | 179 | continue; |
a375d911 | 180 | if (ul_path_accessf(sys, F_OK, "cpu%d", cpu) != 0) { |
e3ca1312 | 181 | warnx(_("CPU %u does not exist"), cpu); |
48fc00c1 | 182 | fails++; |
0b57c6c2 HC |
183 | continue; |
184 | } | |
a375d911 | 185 | if (ul_path_accessf(sys, F_OK, "cpu%d/configure", cpu) != 0) { |
e3ca1312 | 186 | warnx(_("CPU %u is not configurable"), cpu); |
48fc00c1 | 187 | fails++; |
0b57c6c2 HC |
188 | continue; |
189 | } | |
a375d911 KZ |
190 | ul_path_readf_s32(sys, ¤t, "cpu%d/configure", cpu); |
191 | if (current == 1 && configure == 1) { | |
e3ca1312 | 192 | printf(_("CPU %u is already configured\n"), cpu); |
0b57c6c2 HC |
193 | continue; |
194 | } | |
a375d911 | 195 | if (current == 0 && configure == 0) { |
e3ca1312 | 196 | printf(_("CPU %u is already deconfigured\n"), cpu); |
0b57c6c2 HC |
197 | continue; |
198 | } | |
a375d911 | 199 | if (current == 1 && configure == 0 && onlinecpus && |
5b88ce6a | 200 | is_cpu_online(cpu)) { |
e3ca1312 | 201 | warnx(_("CPU %u deconfigure failed (CPU is enabled)"), cpu); |
48fc00c1 | 202 | fails++; |
5b88ce6a HC |
203 | continue; |
204 | } | |
0b57c6c2 | 205 | if (configure) { |
a375d911 KZ |
206 | rc = ul_path_writef_string(sys, "1", "cpu%d/configure", cpu); |
207 | if (rc != 0) { | |
e3ca1312 | 208 | warn(_("CPU %u configure failed"), cpu); |
48fc00c1 KZ |
209 | fails++; |
210 | } else | |
e3ca1312 | 211 | printf(_("CPU %u configured\n"), cpu); |
0b57c6c2 | 212 | } else { |
a375d911 KZ |
213 | rc = ul_path_writef_string(sys, "0", "cpu%d/configure", cpu); |
214 | if (rc != 0) { | |
e3ca1312 | 215 | warn(_("CPU %u deconfigure failed"), cpu); |
48fc00c1 KZ |
216 | fails++; |
217 | } else | |
e3ca1312 | 218 | printf(_("CPU %u deconfigured\n"), cpu); |
0b57c6c2 | 219 | } |
0b57c6c2 | 220 | } |
48fc00c1 | 221 | |
60727494 | 222 | return fails == 0 ? 0 : fails == maxcpus ? -1 : 1; |
0b57c6c2 HC |
223 | } |
224 | ||
59fb133a HC |
225 | static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize) |
226 | { | |
227 | int rc; | |
228 | ||
229 | rc = cpulist_parse(cpu_string, cpu_set, setsize, 1); | |
230 | if (rc == 0) | |
231 | return; | |
232 | if (rc == 2) | |
233 | errx(EXIT_FAILURE, _("invalid CPU number in CPU list: %s"), cpu_string); | |
234 | errx(EXIT_FAILURE, _("failed to parse CPU list: %s"), cpu_string); | |
235 | } | |
236 | ||
6e1eda6f | 237 | static void __attribute__((__noreturn__)) usage(void) |
0b57c6c2 | 238 | { |
6e1eda6f | 239 | FILE *out = stdout; |
0b57c6c2 HC |
240 | fprintf(out, _( |
241 | "\nUsage:\n" | |
242 | " %s [options]\n"), program_invocation_short_name); | |
243 | ||
451dbcfa BS |
244 | fputs(USAGE_SEPARATOR, out); |
245 | fputs(_("Configure CPUs in a multi-processor system.\n"), out); | |
246 | ||
b3054454 RM |
247 | fputs(USAGE_OPTIONS, stdout); |
248 | fputs(_( | |
249 | " -e, --enable <cpu-list> enable cpus\n" | |
250 | " -d, --disable <cpu-list> disable cpus\n" | |
251 | " -c, --configure <cpu-list> configure cpus\n" | |
252 | " -g, --deconfigure <cpu-list> deconfigure cpus\n" | |
253 | " -p, --dispatch <mode> set dispatching mode\n" | |
254 | " -r, --rescan trigger rescan of cpus\n" | |
255 | ), stdout); | |
f45f3ec3 | 256 | printf(USAGE_HELP_OPTIONS(31)); |
0b57c6c2 | 257 | |
b3054454 | 258 | printf(USAGE_MAN_TAIL("chcpu(8)")); |
6e1eda6f | 259 | exit(EXIT_SUCCESS); |
0b57c6c2 HC |
260 | } |
261 | ||
262 | int main(int argc, char *argv[]) | |
263 | { | |
a375d911 | 264 | struct path_cxt *sys = NULL; /* _PATH_SYS_CPU handler */ |
7f8787d0 | 265 | cpu_set_t *cpu_set = NULL; |
0b57c6c2 HC |
266 | size_t setsize; |
267 | int cmd = -1; | |
48fc00c1 | 268 | int c, rc; |
0b57c6c2 HC |
269 | |
270 | static const struct option longopts[] = { | |
87918040 SK |
271 | { "configure", required_argument, NULL, 'c' }, |
272 | { "deconfigure",required_argument, NULL, 'g' }, | |
273 | { "disable", required_argument, NULL, 'd' }, | |
274 | { "dispatch", required_argument, NULL, 'p' }, | |
275 | { "enable", required_argument, NULL, 'e' }, | |
276 | { "help", no_argument, NULL, 'h' }, | |
277 | { "rescan", no_argument, NULL, 'r' }, | |
278 | { "version", no_argument, NULL, 'V' }, | |
279 | { NULL, 0, NULL, 0 } | |
0b57c6c2 HC |
280 | }; |
281 | ||
a7349ee3 | 282 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
20da61dc KZ |
283 | { 'c','d','e','g','p' }, |
284 | { 0 } | |
285 | }; | |
286 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
287 | ||
0b57c6c2 HC |
288 | setlocale(LC_ALL, ""); |
289 | bindtextdomain(PACKAGE, LOCALEDIR); | |
290 | textdomain(PACKAGE); | |
efb8854f | 291 | atexit(close_stdout); |
0b57c6c2 | 292 | |
a375d911 KZ |
293 | ul_path_init_debug(); |
294 | sys = ul_new_path(_PATH_SYS_CPU); | |
295 | if (!sys) | |
296 | err(EXIT_FAILURE, _("failed to initialize sysfs handler")); | |
297 | ||
5b88ce6a | 298 | maxcpus = get_max_number_of_cpus(); |
4b11df2c | 299 | if (maxcpus < 1) |
0b57c6c2 | 300 | errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting")); |
a375d911 KZ |
301 | |
302 | if (ul_path_access(sys, F_OK, "online") == 0) | |
303 | ul_path_readf_cpulist(sys, &cpu_set, maxcpus, "online"); | |
7f8787d0 KZ |
304 | else |
305 | cpu_set = CPU_ALLOC(maxcpus); | |
0b57c6c2 HC |
306 | if (!cpu_set) |
307 | err(EXIT_FAILURE, _("cpuset_alloc failed")); | |
308 | ||
7f8787d0 KZ |
309 | setsize = CPU_ALLOC_SIZE(maxcpus); |
310 | ||
0b57c6c2 | 311 | while ((c = getopt_long(argc, argv, "c:d:e:g:hp:rV", longopts, NULL)) != -1) { |
20da61dc KZ |
312 | |
313 | err_exclusive_options(c, longopts, excl, excl_st); | |
314 | ||
0b57c6c2 HC |
315 | switch (c) { |
316 | case 'c': | |
317 | cmd = CMD_CPU_CONFIGURE; | |
59fb133a | 318 | cpu_parse(argv[optind - 1], cpu_set, setsize); |
0b57c6c2 HC |
319 | break; |
320 | case 'd': | |
321 | cmd = CMD_CPU_DISABLE; | |
59fb133a | 322 | cpu_parse(argv[optind - 1], cpu_set, setsize); |
0b57c6c2 HC |
323 | break; |
324 | case 'e': | |
325 | cmd = CMD_CPU_ENABLE; | |
59fb133a | 326 | cpu_parse(argv[optind - 1], cpu_set, setsize); |
0b57c6c2 HC |
327 | break; |
328 | case 'g': | |
329 | cmd = CMD_CPU_DECONFIGURE; | |
59fb133a | 330 | cpu_parse(argv[optind - 1], cpu_set, setsize); |
0b57c6c2 HC |
331 | break; |
332 | case 'h': | |
6e1eda6f | 333 | usage(); |
0b57c6c2 HC |
334 | case 'p': |
335 | if (strcmp("horizontal", argv[optind - 1]) == 0) | |
336 | cmd = CMD_CPU_DISPATCH_HORIZONTAL; | |
337 | else if (strcmp("vertical", argv[optind - 1]) == 0) | |
338 | cmd = CMD_CPU_DISPATCH_VERTICAL; | |
339 | else | |
340 | errx(EXIT_FAILURE, _("unsupported argument: %s"), | |
341 | argv[optind -1 ]); | |
342 | break; | |
343 | case 'r': | |
344 | cmd = CMD_CPU_RESCAN; | |
345 | break; | |
346 | case 'V': | |
f6277500 | 347 | printf(UTIL_LINUX_VERSION); |
0b57c6c2 HC |
348 | return EXIT_SUCCESS; |
349 | default: | |
677ec86c | 350 | errtryhelp(EXIT_FAILURE); |
0b57c6c2 HC |
351 | } |
352 | } | |
353 | ||
6e1eda6f RM |
354 | if ((argc == 1) || (argc != optind)) { |
355 | warnx(_("bad usage")); | |
356 | errtryhelp(EXIT_FAILURE); | |
357 | } | |
0b57c6c2 HC |
358 | |
359 | switch (cmd) { | |
360 | case CMD_CPU_ENABLE: | |
a375d911 | 361 | rc = cpu_enable(sys, cpu_set, maxcpus, 1); |
48fc00c1 | 362 | break; |
0b57c6c2 | 363 | case CMD_CPU_DISABLE: |
a375d911 | 364 | rc = cpu_enable(sys, cpu_set, maxcpus, 0); |
48fc00c1 | 365 | break; |
0b57c6c2 | 366 | case CMD_CPU_CONFIGURE: |
a375d911 | 367 | rc = cpu_configure(sys, cpu_set, maxcpus, 1); |
48fc00c1 | 368 | break; |
0b57c6c2 | 369 | case CMD_CPU_DECONFIGURE: |
a375d911 | 370 | rc = cpu_configure(sys, cpu_set, maxcpus, 0); |
48fc00c1 | 371 | break; |
0b57c6c2 | 372 | case CMD_CPU_RESCAN: |
a375d911 | 373 | rc = cpu_rescan(sys); |
48fc00c1 | 374 | break; |
0b57c6c2 | 375 | case CMD_CPU_DISPATCH_HORIZONTAL: |
a375d911 | 376 | rc = cpu_set_dispatch(sys, 0); |
48fc00c1 | 377 | break; |
0b57c6c2 | 378 | case CMD_CPU_DISPATCH_VERTICAL: |
a375d911 | 379 | rc = cpu_set_dispatch(sys, 1); |
48fc00c1 KZ |
380 | break; |
381 | default: | |
382 | rc = -EINVAL; | |
383 | break; | |
0b57c6c2 | 384 | } |
48fc00c1 | 385 | |
a375d911 KZ |
386 | ul_unref_path(sys); |
387 | ||
48fc00c1 KZ |
388 | return rc == 0 ? EXIT_SUCCESS : |
389 | rc < 0 ? EXIT_FAILURE : CHCPU_EXIT_SOMEOK; | |
0b57c6c2 | 390 | } |