diff options
Diffstat (limited to '')
-rw-r--r-- | sys-utils/chcpu.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c new file mode 100644 index 0000000..36c47af --- /dev/null +++ b/sys-utils/chcpu.c @@ -0,0 +1,389 @@ +/* + * chcpu - CPU configuration tool + * + * Copyright IBM Corp. 2011 + * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "cpuset.h" +#include "nls.h" +#include "xalloc.h" +#include "c.h" +#include "strutils.h" +#include "bitops.h" +#include "path.h" +#include "closestream.h" +#include "optutils.h" + +#define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}" + +/* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */ +#define CHCPU_EXIT_SOMEOK 64 + +#define _PATH_SYS_CPU "/sys/devices/system/cpu" + +static cpu_set_t *onlinecpus; +static int maxcpus; + +#define is_cpu_online(cpu) (CPU_ISSET_S((cpu), CPU_ALLOC_SIZE(maxcpus), onlinecpus)) +#define num_online_cpus() (CPU_COUNT_S(CPU_ALLOC_SIZE(maxcpus), onlinecpus)) + +enum { + CMD_CPU_ENABLE = 0, + CMD_CPU_DISABLE, + CMD_CPU_CONFIGURE, + CMD_CPU_DECONFIGURE, + CMD_CPU_RESCAN, + CMD_CPU_DISPATCH_HORIZONTAL, + CMD_CPU_DISPATCH_VERTICAL, +}; + +/* returns: 0 = success + * < 0 = failure + * > 0 = partial success + */ +static int cpu_enable(struct path_cxt *sys, cpu_set_t *cpu_set, size_t setsize, int enable) +{ + int cpu; + int online, rc; + int configured = -1; + int fails = 0; + + for (cpu = 0; cpu < maxcpus; cpu++) { + if (!CPU_ISSET_S(cpu, setsize, cpu_set)) + continue; + if (ul_path_accessf(sys, F_OK, "cpu%d", cpu) != 0) { + warnx(_("CPU %u does not exist"), cpu); + fails++; + continue; + } + if (ul_path_accessf(sys, F_OK, "cpu%d/online", cpu) != 0) { + warnx(_("CPU %u is not hot pluggable"), cpu); + fails++; + continue; + } + if (ul_path_readf_s32(sys, &online, "cpu%d/online", cpu) == 0 + && online == 1 + && enable == 1) { + printf(_("CPU %u is already enabled\n"), cpu); + continue; + } + if (online == 0 && enable == 0) { + printf(_("CPU %u is already disabled\n"), cpu); + continue; + } + if (ul_path_accessf(sys, F_OK, "cpu%d/configure", cpu) == 0) + ul_path_readf_s32(sys, &configured, "cpu%d/configure", cpu); + if (enable) { + rc = ul_path_writef_string(sys, "1", "cpu%d/online", cpu); + if (rc != 0 && configured == 0) { + warn(_("CPU %u enable failed (CPU is deconfigured)"), cpu); + fails++; + } else if (rc != 0) { + warn(_("CPU %u enable failed"), cpu); + fails++; + } else + printf(_("CPU %u enabled\n"), cpu); + } else { + if (onlinecpus && num_online_cpus() == 1) { + warnx(_("CPU %u disable failed (last enabled CPU)"), cpu); + fails++; + continue; + } + rc = ul_path_writef_string(sys, "0", "cpu%d/online", cpu); + if (rc != 0) { + warn(_("CPU %u disable failed"), cpu); + fails++; + } else { + printf(_("CPU %u disabled\n"), cpu); + if (onlinecpus) + CPU_CLR_S(cpu, setsize, onlinecpus); + } + } + } + + return fails == 0 ? 0 : fails == maxcpus ? -1 : 1; +} + +static int cpu_rescan(struct path_cxt *sys) +{ + if (ul_path_access(sys, F_OK, "rescan") != 0) + errx(EXIT_FAILURE, _("This system does not support rescanning of CPUs")); + + if (ul_path_write_string(sys, "1", "rescan") != 0) + err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs")); + + printf(_("Triggered rescan of CPUs\n")); + return 0; +} + +static int cpu_set_dispatch(struct path_cxt *sys, int mode) +{ + if (ul_path_access(sys, F_OK, "dispatching") != 0) + errx(EXIT_FAILURE, _("This system does not support setting " + "the dispatching mode of CPUs")); + if (mode == 0) { + if (ul_path_write_string(sys, "0", "dispatching") != 0) + err(EXIT_FAILURE, _("Failed to set horizontal dispatch mode")); + + printf(_("Successfully set horizontal dispatching mode\n")); + } else { + if (ul_path_write_string(sys, "1", "dispatching") != 0) + err(EXIT_FAILURE, _("Failed to set vertical dispatch mode")); + + printf(_("Successfully set vertical dispatching mode\n")); + } + return 0; +} + +/* returns: 0 = success + * < 0 = failure + * > 0 = partial success + */ +static int cpu_configure(struct path_cxt *sys, cpu_set_t *cpu_set, size_t setsize, int configure) +{ + int cpu; + int rc, current; + int fails = 0; + + for (cpu = 0; cpu < maxcpus; cpu++) { + if (!CPU_ISSET_S(cpu, setsize, cpu_set)) + continue; + if (ul_path_accessf(sys, F_OK, "cpu%d", cpu) != 0) { + warnx(_("CPU %u does not exist"), cpu); + fails++; + continue; + } + if (ul_path_accessf(sys, F_OK, "cpu%d/configure", cpu) != 0) { + warnx(_("CPU %u is not configurable"), cpu); + fails++; + continue; + } + ul_path_readf_s32(sys, ¤t, "cpu%d/configure", cpu); + if (current == 1 && configure == 1) { + printf(_("CPU %u is already configured\n"), cpu); + continue; + } + if (current == 0 && configure == 0) { + printf(_("CPU %u is already deconfigured\n"), cpu); + continue; + } + if (current == 1 && configure == 0 && onlinecpus && + is_cpu_online(cpu)) { + warnx(_("CPU %u deconfigure failed (CPU is enabled)"), cpu); + fails++; + continue; + } + if (configure) { + rc = ul_path_writef_string(sys, "1", "cpu%d/configure", cpu); + if (rc != 0) { + warn(_("CPU %u configure failed"), cpu); + fails++; + } else + printf(_("CPU %u configured\n"), cpu); + } else { + rc = ul_path_writef_string(sys, "0", "cpu%d/configure", cpu); + if (rc != 0) { + warn(_("CPU %u deconfigure failed"), cpu); + fails++; + } else + printf(_("CPU %u deconfigured\n"), cpu); + } + } + + return fails == 0 ? 0 : fails == maxcpus ? -1 : 1; +} + +static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize) +{ + int rc; + + rc = cpulist_parse(cpu_string, cpu_set, setsize, 1); + if (rc == 0) + return; + if (rc == 2) + errx(EXIT_FAILURE, _("invalid CPU number in CPU list: %s"), cpu_string); + errx(EXIT_FAILURE, _("failed to parse CPU list: %s"), cpu_string); +} + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + fprintf(out, _( + "\nUsage:\n" + " %s [options]\n"), program_invocation_short_name); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Configure CPUs in a multi-processor system.\n"), out); + + fputs(USAGE_OPTIONS, stdout); + fputs(_( + " -e, --enable <cpu-list> enable cpus\n" + " -d, --disable <cpu-list> disable cpus\n" + " -c, --configure <cpu-list> configure cpus\n" + " -g, --deconfigure <cpu-list> deconfigure cpus\n" + " -p, --dispatch <mode> set dispatching mode\n" + " -r, --rescan trigger rescan of cpus\n" + ), stdout); + printf(USAGE_HELP_OPTIONS(31)); + + printf(USAGE_MAN_TAIL("chcpu(8)")); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + struct path_cxt *sys = NULL; /* _PATH_SYS_CPU handler */ + cpu_set_t *cpu_set; + size_t setsize; + int cmd = -1; + int c, rc; + + static const struct option longopts[] = { + { "configure", required_argument, NULL, 'c' }, + { "deconfigure",required_argument, NULL, 'g' }, + { "disable", required_argument, NULL, 'd' }, + { "dispatch", required_argument, NULL, 'p' }, + { "enable", required_argument, NULL, 'e' }, + { "help", no_argument, NULL, 'h' }, + { "rescan", no_argument, NULL, 'r' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ + { 'c','d','e','g','p' }, + { 0 } + }; + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + ul_path_init_debug(); + sys = ul_new_path(_PATH_SYS_CPU); + if (!sys) + err(EXIT_FAILURE, _("failed to initialize sysfs handler")); + + maxcpus = get_max_number_of_cpus(); + if (maxcpus < 1) + errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting")); + + if (ul_path_access(sys, F_OK, "online") == 0) + ul_path_readf_cpulist(sys, &cpu_set, maxcpus, "online"); + + setsize = CPU_ALLOC_SIZE(maxcpus); + cpu_set = CPU_ALLOC(maxcpus); + if (!cpu_set) + err(EXIT_FAILURE, _("cpuset_alloc failed")); + + while ((c = getopt_long(argc, argv, "c:d:e:g:hp:rV", longopts, NULL)) != -1) { + + err_exclusive_options(c, longopts, excl, excl_st); + + switch (c) { + case 'c': + cmd = CMD_CPU_CONFIGURE; + cpu_parse(argv[optind - 1], cpu_set, setsize); + break; + case 'd': + cmd = CMD_CPU_DISABLE; + cpu_parse(argv[optind - 1], cpu_set, setsize); + break; + case 'e': + cmd = CMD_CPU_ENABLE; + cpu_parse(argv[optind - 1], cpu_set, setsize); + break; + case 'g': + cmd = CMD_CPU_DECONFIGURE; + cpu_parse(argv[optind - 1], cpu_set, setsize); + break; + case 'h': + usage(); + case 'p': + if (strcmp("horizontal", argv[optind - 1]) == 0) + cmd = CMD_CPU_DISPATCH_HORIZONTAL; + else if (strcmp("vertical", argv[optind - 1]) == 0) + cmd = CMD_CPU_DISPATCH_VERTICAL; + else + errx(EXIT_FAILURE, _("unsupported argument: %s"), + argv[optind -1 ]); + break; + case 'r': + cmd = CMD_CPU_RESCAN; + break; + case 'V': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + default: + errtryhelp(EXIT_FAILURE); + } + } + + if ((argc == 1) || (argc != optind)) { + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + + switch (cmd) { + case CMD_CPU_ENABLE: + rc = cpu_enable(sys, cpu_set, maxcpus, 1); + break; + case CMD_CPU_DISABLE: + rc = cpu_enable(sys, cpu_set, maxcpus, 0); + break; + case CMD_CPU_CONFIGURE: + rc = cpu_configure(sys, cpu_set, maxcpus, 1); + break; + case CMD_CPU_DECONFIGURE: + rc = cpu_configure(sys, cpu_set, maxcpus, 0); + break; + case CMD_CPU_RESCAN: + rc = cpu_rescan(sys); + break; + case CMD_CPU_DISPATCH_HORIZONTAL: + rc = cpu_set_dispatch(sys, 0); + break; + case CMD_CPU_DISPATCH_VERTICAL: + rc = cpu_set_dispatch(sys, 1); + break; + default: + rc = -EINVAL; + break; + } + + ul_unref_path(sys); + + return rc == 0 ? EXIT_SUCCESS : + rc < 0 ? EXIT_FAILURE : CHCPU_EXIT_SOMEOK; +} |