(root)/
util-linux-2.39/
sys-utils/
chcpu.c
       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   *
      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.
      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"
      42  #include "path.h"
      43  #include "closestream.h"
      44  #include "optutils.h"
      45  
      46  #define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}"
      47  
      48  /* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */
      49  #define CHCPU_EXIT_SOMEOK	64
      50  
      51  #define _PATH_SYS_CPU		"/sys/devices/system/cpu"
      52  
      53  static cpu_set_t *onlinecpus;
      54  static int maxcpus;
      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  
      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  
      69  /* returns:   0 = success
      70   *          < 0 = failure
      71   *          > 0 = partial success
      72   */
      73  static int cpu_enable(struct path_cxt *sys, cpu_set_t *cpu_set, size_t setsize, int enable)
      74  {
      75  	int cpu;
      76  	int online, rc;
      77  	int configured = -1;
      78  	int fails = 0;
      79  
      80  	for (cpu = 0; cpu < maxcpus; cpu++) {
      81  		if (!CPU_ISSET_S(cpu, setsize, cpu_set))
      82  			continue;
      83  		if (ul_path_accessf(sys, F_OK, "cpu%d", cpu) != 0) {
      84  			warnx(_("CPU %u does not exist"), cpu);
      85  			fails++;
      86  			continue;
      87  		}
      88  		if (ul_path_accessf(sys, F_OK, "cpu%d/online", cpu) != 0) {
      89  			warnx(_("CPU %u is not hot pluggable"), cpu);
      90  			fails++;
      91  			continue;
      92  		}
      93  		if (ul_path_readf_s32(sys, &online, "cpu%d/online", cpu) == 0
      94  		    && online == 1
      95  		    && enable == 1) {
      96  			printf(_("CPU %u is already enabled\n"), cpu);
      97  			continue;
      98  		}
      99  		if (online == 0 && enable == 0) {
     100  			printf(_("CPU %u is already disabled\n"), cpu);
     101  			continue;
     102  		}
     103  		if (ul_path_accessf(sys, F_OK, "cpu%d/configure", cpu) == 0)
     104  			ul_path_readf_s32(sys, &configured, "cpu%d/configure", cpu);
     105  		if (enable) {
     106  			rc = ul_path_writef_string(sys, "1", "cpu%d/online", cpu);
     107  			if (rc != 0 && configured == 0) {
     108  				warn(_("CPU %u enable failed (CPU is deconfigured)"), cpu);
     109  				fails++;
     110  			} else if (rc != 0) {
     111  				warn(_("CPU %u enable failed"), cpu);
     112  				fails++;
     113  			} else
     114  				printf(_("CPU %u enabled\n"), cpu);
     115  		} else {
     116  			if (onlinecpus && num_online_cpus() == 1) {
     117  				warnx(_("CPU %u disable failed (last enabled CPU)"), cpu);
     118  				fails++;
     119  				continue;
     120  			}
     121  			rc = ul_path_writef_string(sys, "0", "cpu%d/online", cpu);
     122  			if (rc != 0) {
     123  				warn(_("CPU %u disable failed"), cpu);
     124  				fails++;
     125  			} else {
     126  				printf(_("CPU %u disabled\n"), cpu);
     127  				if (onlinecpus)
     128  					CPU_CLR_S(cpu, setsize, onlinecpus);
     129  			}
     130  		}
     131  	}
     132  
     133  	return fails == 0 ? 0 : fails == maxcpus ? -1 : 1;
     134  }
     135  
     136  static int cpu_rescan(struct path_cxt *sys)
     137  {
     138  	if (ul_path_access(sys, F_OK, "rescan") != 0)
     139  		errx(EXIT_FAILURE, _("This system does not support rescanning of CPUs"));
     140  
     141  	if (ul_path_write_string(sys, "1", "rescan") != 0)
     142  		err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs"));
     143  
     144  	printf(_("Triggered rescan of CPUs\n"));
     145  	return 0;
     146  }
     147  
     148  static int cpu_set_dispatch(struct path_cxt *sys, int mode)
     149  {
     150  	if (ul_path_access(sys, F_OK, "dispatching") != 0)
     151  		errx(EXIT_FAILURE, _("This system does not support setting "
     152  				     "the dispatching mode of CPUs"));
     153  	if (mode == 0) {
     154  		if (ul_path_write_string(sys, "0", "dispatching") != 0)
     155  			err(EXIT_FAILURE, _("Failed to set horizontal dispatch mode"));
     156  
     157  		printf(_("Successfully set horizontal dispatching mode\n"));
     158  	} else {
     159  		if (ul_path_write_string(sys, "1", "dispatching") != 0)
     160  			err(EXIT_FAILURE, _("Failed to set vertical dispatch mode"));
     161  
     162  		printf(_("Successfully set vertical dispatching mode\n"));
     163  	}
     164  	return 0;
     165  }
     166  
     167  /* returns:   0 = success
     168   *          < 0 = failure
     169   *          > 0 = partial success
     170   */
     171  static int cpu_configure(struct path_cxt *sys, cpu_set_t *cpu_set, size_t setsize, int configure)
     172  {
     173  	int cpu;
     174  	int rc, current;
     175  	int fails = 0;
     176  
     177  	for (cpu = 0; cpu < maxcpus; cpu++) {
     178  		if (!CPU_ISSET_S(cpu, setsize, cpu_set))
     179  			continue;
     180  		if (ul_path_accessf(sys, F_OK, "cpu%d", cpu) != 0) {
     181  			warnx(_("CPU %u does not exist"), cpu);
     182  			fails++;
     183  			continue;
     184  		}
     185  		if (ul_path_accessf(sys, F_OK, "cpu%d/configure", cpu) != 0) {
     186  			warnx(_("CPU %u is not configurable"), cpu);
     187  			fails++;
     188  			continue;
     189  		}
     190  		ul_path_readf_s32(sys, &current, "cpu%d/configure", cpu);
     191  		if (current == 1 && configure == 1) {
     192  			printf(_("CPU %u is already configured\n"), cpu);
     193  			continue;
     194  		}
     195  		if (current == 0 && configure == 0) {
     196  			printf(_("CPU %u is already deconfigured\n"), cpu);
     197  			continue;
     198  		}
     199  		if (current == 1 && configure == 0 && onlinecpus &&
     200  		    is_cpu_online(cpu)) {
     201  			warnx(_("CPU %u deconfigure failed (CPU is enabled)"), cpu);
     202  			fails++;
     203  			continue;
     204  		}
     205  		if (configure) {
     206  			rc = ul_path_writef_string(sys, "1", "cpu%d/configure", cpu);
     207  			if (rc != 0) {
     208  				warn(_("CPU %u configure failed"), cpu);
     209  				fails++;
     210  			} else
     211  				printf(_("CPU %u configured\n"), cpu);
     212  		} else {
     213  			rc = ul_path_writef_string(sys, "0", "cpu%d/configure", cpu);
     214  			if (rc != 0) {
     215  				warn(_("CPU %u deconfigure failed"), cpu);
     216  				fails++;
     217  			} else
     218  				printf(_("CPU %u deconfigured\n"), cpu);
     219  		}
     220  	}
     221  
     222  	return fails == 0 ? 0 : fails == maxcpus ? -1 : 1;
     223  }
     224  
     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  
     237  static void __attribute__((__noreturn__)) usage(void)
     238  {
     239  	FILE *out = stdout;
     240  	fprintf(out, _(
     241  		"\nUsage:\n"
     242  		" %s [options]\n"), program_invocation_short_name);
     243  
     244  	fputs(USAGE_SEPARATOR, out);
     245  	fputs(_("Configure CPUs in a multi-processor system.\n"), out);
     246  
     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);
     256  	printf(USAGE_HELP_OPTIONS(31));
     257  
     258  	printf(USAGE_MAN_TAIL("chcpu(8)"));
     259  	exit(EXIT_SUCCESS);
     260  }
     261  
     262  int main(int argc, char *argv[])
     263  {
     264  	struct path_cxt *sys = NULL;	/* _PATH_SYS_CPU handler */
     265  	cpu_set_t *cpu_set = NULL;
     266  	size_t setsize;
     267  	int cmd = -1;
     268  	int c, rc;
     269  
     270  	static const struct option longopts[] = {
     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 }
     280  	};
     281  
     282  	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
     283  		{ 'c','d','e','g','p' },
     284  		{ 0 }
     285  	};
     286  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     287  
     288  	setlocale(LC_ALL, "");
     289  	bindtextdomain(PACKAGE, LOCALEDIR);
     290  	textdomain(PACKAGE);
     291  	close_stdout_atexit();
     292  
     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  
     298  	maxcpus = get_max_number_of_cpus();
     299  	if (maxcpus < 1)
     300  		errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting"));
     301  
     302  	if (ul_path_access(sys, F_OK, "online") == 0)
     303  		ul_path_readf_cpulist(sys, &cpu_set, maxcpus, "online");
     304  	else
     305  		cpu_set = CPU_ALLOC(maxcpus);
     306  	if (!cpu_set)
     307  		err(EXIT_FAILURE, _("cpuset_alloc failed"));
     308  
     309  	setsize = CPU_ALLOC_SIZE(maxcpus);
     310  
     311  	while ((c = getopt_long(argc, argv, "c:d:e:g:hp:rV", longopts, NULL)) != -1) {
     312  
     313  		err_exclusive_options(c, longopts, excl, excl_st);
     314  
     315  		switch (c) {
     316  		case 'c':
     317  			cmd = CMD_CPU_CONFIGURE;
     318  			cpu_parse(argv[optind - 1], cpu_set, setsize);
     319  			break;
     320  		case 'd':
     321  			cmd = CMD_CPU_DISABLE;
     322  			cpu_parse(argv[optind - 1], cpu_set, setsize);
     323  			break;
     324  		case 'e':
     325  			cmd = CMD_CPU_ENABLE;
     326  			cpu_parse(argv[optind - 1], cpu_set, setsize);
     327  			break;
     328  		case 'g':
     329  			cmd = CMD_CPU_DECONFIGURE;
     330  			cpu_parse(argv[optind - 1], cpu_set, setsize);
     331  			break;
     332  		case 'p':
     333  			if (strcmp("horizontal", argv[optind - 1]) == 0)
     334  				cmd = CMD_CPU_DISPATCH_HORIZONTAL;
     335  			else if (strcmp("vertical", argv[optind - 1]) == 0)
     336  				cmd = CMD_CPU_DISPATCH_VERTICAL;
     337  			else
     338  				errx(EXIT_FAILURE, _("unsupported argument: %s"),
     339  				     argv[optind -1 ]);
     340  			break;
     341  		case 'r':
     342  			cmd = CMD_CPU_RESCAN;
     343  			break;
     344  
     345  		case 'h':
     346  			usage();
     347  		case 'V':
     348  			print_version(EXIT_SUCCESS);
     349  		default:
     350  			errtryhelp(EXIT_FAILURE);
     351  		}
     352  	}
     353  
     354  	if ((argc == 1) || (argc != optind)) {
     355  		warnx(_("bad usage"));
     356  		errtryhelp(EXIT_FAILURE);
     357  	}
     358  
     359  	switch (cmd) {
     360  	case CMD_CPU_ENABLE:
     361  		rc = cpu_enable(sys, cpu_set, maxcpus, 1);
     362  		break;
     363  	case CMD_CPU_DISABLE:
     364  		rc = cpu_enable(sys, cpu_set, maxcpus, 0);
     365  		break;
     366  	case CMD_CPU_CONFIGURE:
     367  		rc = cpu_configure(sys, cpu_set, maxcpus, 1);
     368  		break;
     369  	case CMD_CPU_DECONFIGURE:
     370  		rc = cpu_configure(sys, cpu_set, maxcpus, 0);
     371  		break;
     372  	case CMD_CPU_RESCAN:
     373  		rc = cpu_rescan(sys);
     374  		break;
     375  	case CMD_CPU_DISPATCH_HORIZONTAL:
     376  		rc = cpu_set_dispatch(sys, 0);
     377  		break;
     378  	case CMD_CPU_DISPATCH_VERTICAL:
     379  		rc = cpu_set_dispatch(sys, 1);
     380  		break;
     381  	default:
     382  		rc = -EINVAL;
     383  		break;
     384  	}
     385  
     386  	CPU_FREE(cpu_set);
     387  	ul_unref_path(sys);
     388  
     389  	return rc == 0 ? EXIT_SUCCESS :
     390  	        rc < 0 ? EXIT_FAILURE : CHCPU_EXIT_SOMEOK;
     391  }