391 lines
10 KiB
C
391 lines
10 KiB
C
/*
|
|
* 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 = NULL;
|
|
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);
|
|
close_stdout_atexit();
|
|
|
|
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");
|
|
else
|
|
cpu_set = CPU_ALLOC(maxcpus);
|
|
if (!cpu_set)
|
|
err(EXIT_FAILURE, _("cpuset_alloc failed"));
|
|
|
|
setsize = CPU_ALLOC_SIZE(maxcpus);
|
|
|
|
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 '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 'h':
|
|
usage();
|
|
case 'V':
|
|
print_version(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;
|
|
}
|