(root)/
util-linux-2.39/
schedutils/
taskset.c
       1  /*
       2   * taskset.c - set or retrieve a task's CPU affinity
       3   *
       4   * This program is free software; you can redistribute it and/or modify
       5   * it under the terms of the GNU General Public License, version 2, as
       6   * published by the Free Software Foundation
       7   *
       8   * This program is distributed in the hope that it will be useful,
       9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      11   * GNU General Public License for more details.
      12   *
      13   * You should have received a copy of the GNU General Public License along
      14   * with this program; if not, write to the Free Software Foundation, Inc.,
      15   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      16   *
      17   * Copyright (C) 2004 Robert Love
      18   * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
      19   */
      20  
      21  #include <stdio.h>
      22  #include <stdlib.h>
      23  #include <unistd.h>
      24  #include <getopt.h>
      25  #include <errno.h>
      26  #include <sched.h>
      27  #include <stddef.h>
      28  #include <string.h>
      29  
      30  #include "cpuset.h"
      31  #include "nls.h"
      32  #include "strutils.h"
      33  #include "xalloc.h"
      34  #include "procfs.h"
      35  #include "c.h"
      36  #include "closestream.h"
      37  
      38  #ifndef PF_NO_SETAFFINITY
      39  # define PF_NO_SETAFFINITY 0x04000000
      40  #endif
      41  
      42  struct taskset {
      43  	pid_t		pid;		/* task PID */
      44  	cpu_set_t	*set;		/* task CPU mask */
      45  	size_t		setsize;
      46  	char		*buf;		/* buffer for conversion from mask to string */
      47  	size_t		buflen;
      48  	unsigned int	use_list:1,	/* use list rather than masks */
      49  			get_only:1;	/* print the mask, but not modify */
      50  };
      51  
      52  static void __attribute__((__noreturn__)) usage(void)
      53  {
      54  	FILE *out = stdout;
      55  	fprintf(out,
      56  		_("Usage: %s [options] [mask | cpu-list] [pid|cmd [args...]]\n\n"),
      57  		program_invocation_short_name);
      58  
      59  	fputs(USAGE_SEPARATOR, out);
      60  	fputs(_("Show or change the CPU affinity of a process.\n"), out);
      61  	fputs(USAGE_SEPARATOR, out);
      62  
      63  	fprintf(out, _(
      64  		"Options:\n"
      65  		" -a, --all-tasks         operate on all the tasks (threads) for a given pid\n"
      66  		" -p, --pid               operate on existing given pid\n"
      67  		" -c, --cpu-list          display and specify cpus in list format\n"
      68  		));
      69  	printf(USAGE_HELP_OPTIONS(25));
      70  
      71  	fputs(USAGE_SEPARATOR, out);
      72  	fprintf(out, _(
      73  		"The default behavior is to run a new command:\n"
      74  		"    %1$s 03 sshd -b 1024\n"
      75  		"You can retrieve the mask of an existing task:\n"
      76  		"    %1$s -p 700\n"
      77  		"Or set it:\n"
      78  		"    %1$s -p 03 700\n"
      79  		"List format uses a comma-separated list instead of a mask:\n"
      80  		"    %1$s -pc 0,3,7-11 700\n"
      81  		"Ranges in list format can take a stride argument:\n"
      82  		"    e.g. 0-31:2 is equivalent to mask 0x55555555\n"),
      83  		program_invocation_short_name);
      84  
      85  	printf(USAGE_MAN_TAIL("taskset(1)"));
      86  	exit(EXIT_SUCCESS);
      87  }
      88  
      89  static void print_affinity(struct taskset *ts, int isnew)
      90  {
      91  	char *str, *msg;
      92  
      93  	if (ts->use_list) {
      94  		str = cpulist_create(ts->buf, ts->buflen, ts->set, ts->setsize);
      95  		msg = isnew ? _("pid %d's new affinity list: %s\n") :
      96  			      _("pid %d's current affinity list: %s\n");
      97  	} else {
      98  		str = cpumask_create(ts->buf, ts->buflen, ts->set, ts->setsize);
      99  		msg = isnew ? _("pid %d's new affinity mask: %s\n") :
     100  			      _("pid %d's current affinity mask: %s\n");
     101  	}
     102  
     103  	if (!str)
     104  		errx(EXIT_FAILURE, _("internal error: conversion from cpuset to string failed"));
     105  
     106  	printf(msg, ts->pid ? ts->pid : getpid(), str);
     107  }
     108  
     109  static void __attribute__((__noreturn__)) err_affinity(pid_t pid, int set)
     110  {
     111  	char *msg;
     112  
     113  	msg = set ? _("failed to set pid %d's affinity") :
     114  		    _("failed to get pid %d's affinity");
     115  
     116  	err(EXIT_FAILURE, msg, pid ? pid : getpid());
     117  }
     118  
     119  
     120  static void do_taskset(struct taskset *ts, size_t setsize, cpu_set_t *set)
     121  {
     122  	/* read the current mask */
     123  	if (ts->pid) {
     124  		if (sched_getaffinity(ts->pid, ts->setsize, ts->set) < 0)
     125  			err_affinity(ts->pid, 0);
     126  		print_affinity(ts, FALSE);
     127  	}
     128  
     129  	if (ts->get_only)
     130  		return;
     131  
     132  	/* set new mask */
     133  	if (sched_setaffinity(ts->pid, setsize, set) < 0) {
     134  		uintmax_t flags = 0;
     135  		struct path_cxt *pc;
     136  		int errsv = errno;
     137  
     138  		if (errno != EPERM
     139  		    && (pc = ul_new_procfs_path(ts->pid, NULL))
     140  		    && procfs_process_get_stat_nth(pc, 9, &flags) == 0
     141  		    && (flags & PF_NO_SETAFFINITY)) {
     142  			warnx(_("affinity cannot be set due to PF_NO_SETAFFINITY flag set"));
     143  			errno = EINVAL;
     144  		} else
     145  			errno = errsv;
     146  
     147  		err_affinity(ts->pid, 1);
     148  	}
     149  
     150  	/* re-read the current mask */
     151  	if (ts->pid) {
     152  		if (sched_getaffinity(ts->pid, ts->setsize, ts->set) < 0)
     153  			err_affinity(ts->pid, 0);
     154  		print_affinity(ts, TRUE);
     155  	}
     156  }
     157  
     158  int main(int argc, char **argv)
     159  {
     160  	cpu_set_t *new_set;
     161  	pid_t pid = 0;
     162  	int c, all_tasks = 0;
     163  	int ncpus;
     164  	size_t new_setsize, nbits;
     165  	struct taskset ts;
     166  
     167  	static const struct option longopts[] = {
     168  		{ "all-tasks",	0, NULL, 'a' },
     169  		{ "pid",	0, NULL, 'p' },
     170  		{ "cpu-list",	0, NULL, 'c' },
     171  		{ "help",	0, NULL, 'h' },
     172  		{ "version",	0, NULL, 'V' },
     173  		{ NULL,		0, NULL,  0  }
     174  	};
     175  
     176  	setlocale(LC_ALL, "");
     177  	bindtextdomain(PACKAGE, LOCALEDIR);
     178  	textdomain(PACKAGE);
     179  	close_stdout_atexit();
     180  
     181  	memset(&ts, 0, sizeof(ts));
     182  
     183  	while ((c = getopt_long(argc, argv, "+apchV", longopts, NULL)) != -1) {
     184  		switch (c) {
     185  		case 'a':
     186  			all_tasks = 1;
     187  			break;
     188  		case 'p':
     189  			pid = strtos32_or_err(argv[argc - 1],
     190  					    _("invalid PID argument"));
     191  			break;
     192  		case 'c':
     193  			ts.use_list = 1;
     194  			break;
     195  
     196  		case 'V':
     197  			print_version(EXIT_SUCCESS);
     198  		case 'h':
     199  			usage();
     200  		default:
     201  			errtryhelp(EXIT_FAILURE);
     202  		}
     203  	}
     204  
     205  	if ((!pid && argc - optind < 2)
     206  	    || (pid && (argc - optind < 1 || argc - optind > 2))) {
     207  		warnx(_("bad usage"));
     208  		errtryhelp(EXIT_FAILURE);
     209  	}
     210  
     211  	ncpus = get_max_number_of_cpus();
     212  	if (ncpus <= 0)
     213  		errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting"));
     214  
     215  	/*
     216  	 * the ts->set is always used for the sched_getaffinity call
     217  	 * On the sched_getaffinity the kernel demands a user mask of
     218  	 * at least the size of its own cpumask_t.
     219  	 */
     220  	ts.set = cpuset_alloc(ncpus, &ts.setsize, &nbits);
     221  	if (!ts.set)
     222  		err(EXIT_FAILURE, _("cpuset_alloc failed"));
     223  
     224  	/* buffer for conversion from mask to string */
     225  	ts.buflen = 7 * nbits;
     226  	ts.buf = xmalloc(ts.buflen);
     227  
     228  	/*
     229  	 * new_set is always used for the sched_setaffinity call
     230  	 * On the sched_setaffinity the kernel will zero-fill its
     231  	 * cpumask_t if the user's mask is shorter.
     232  	 */
     233  	new_set = cpuset_alloc(ncpus, &new_setsize, NULL);
     234  	if (!new_set)
     235  		err(EXIT_FAILURE, _("cpuset_alloc failed"));
     236  
     237  	if (argc - optind == 1)
     238  		ts.get_only = 1;
     239  
     240  	else if (ts.use_list) {
     241  		if (cpulist_parse(argv[optind], new_set, new_setsize, 0))
     242  			errx(EXIT_FAILURE, _("failed to parse CPU list: %s"),
     243  			     argv[optind]);
     244  	} else if (cpumask_parse(argv[optind], new_set, new_setsize)) {
     245  		errx(EXIT_FAILURE, _("failed to parse CPU mask: %s"),
     246  		     argv[optind]);
     247  	}
     248  
     249  	if (all_tasks && pid) {
     250  		DIR *sub = NULL;
     251  		struct path_cxt *pc = ul_new_procfs_path(pid, NULL);
     252  
     253  		while (pc && procfs_process_next_tid(pc, &sub, &ts.pid) == 0)
     254  			do_taskset(&ts, new_setsize, new_set);
     255  
     256  		ul_unref_path(pc);
     257  	} else {
     258  		ts.pid = pid;
     259  		do_taskset(&ts, new_setsize, new_set);
     260  	}
     261  
     262  	free(ts.buf);
     263  	cpuset_free(ts.set);
     264  	cpuset_free(new_set);
     265  
     266  	if (!pid) {
     267  		argv += optind + 1;
     268  		execvp(argv[0], argv);
     269  		errexec(argv[0]);
     270  	}
     271  
     272  	return EXIT_SUCCESS;
     273  }