(root)/
util-linux-2.39/
schedutils/
chrt.c
       1  /*
       2   * chrt.c - manipulate a task's real-time attributes
       3   *
       4   * 27-Apr-2002: initial version - Robert Love <rml@tech9.net>
       5   * 04-May-2011: make it thread-aware - Davidlohr Bueso <dave@gnu.org>
       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, version 2, as
       9   * published by the Free Software Foundation
      10   *
      11   * This program is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14   * GNU General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU General Public License along
      17   * with this program; if not, write to the Free Software Foundation, Inc.,
      18   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19   *
      20   * Copyright (C) 2004 Robert Love
      21   */
      22  
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <sched.h>
      26  #include <unistd.h>
      27  #include <getopt.h>
      28  #include <errno.h>
      29  #include <sys/time.h>
      30  #include <sys/resource.h>
      31  
      32  #include "c.h"
      33  #include "nls.h"
      34  #include "closestream.h"
      35  #include "strutils.h"
      36  #include "procfs.h"
      37  #include "sched_attr.h"
      38  
      39  
      40  /* control struct */
      41  struct chrt_ctl {
      42  	pid_t	pid;
      43  	int	policy;				/* SCHED_* */
      44  	int	priority;
      45  
      46  	uint64_t runtime;			/* --sched-* options */
      47  	uint64_t deadline;
      48  	uint64_t period;
      49  
      50  	unsigned int all_tasks : 1,		/* all threads of the PID */
      51  		     reset_on_fork : 1,		/* SCHED_RESET_ON_FORK or SCHED_FLAG_RESET_ON_FORK */
      52  		     altered : 1,		/* sched_set**() used */
      53  		     verbose : 1;		/* verbose output */
      54  };
      55  
      56  static void __attribute__((__noreturn__)) usage(void)
      57  {
      58  	FILE *out = stdout;
      59  
      60  	fputs(_("Show or change the real-time scheduling attributes of a process.\n"), out);
      61  	fputs(USAGE_SEPARATOR, out);
      62  	fputs(_("Set policy:\n"
      63  	" chrt [options] <priority> <command> [<arg>...]\n"
      64  	" chrt [options] --pid <priority> <pid>\n"), out);
      65  	fputs(USAGE_SEPARATOR, out);
      66  	fputs(_("Get policy:\n"
      67  	" chrt [options] -p <pid>\n"), out);
      68  
      69  	fputs(USAGE_SEPARATOR, out);
      70  	fputs(_("Policy options:\n"), out);
      71  	fputs(_(" -b, --batch          set policy to SCHED_BATCH\n"), out);
      72  	fputs(_(" -d, --deadline       set policy to SCHED_DEADLINE\n"), out);
      73  	fputs(_(" -f, --fifo           set policy to SCHED_FIFO\n"), out);
      74  	fputs(_(" -i, --idle           set policy to SCHED_IDLE\n"), out);
      75  	fputs(_(" -o, --other          set policy to SCHED_OTHER\n"), out);
      76  	fputs(_(" -r, --rr             set policy to SCHED_RR (default)\n"), out);
      77  
      78  	fputs(USAGE_SEPARATOR, out);
      79  	fputs(_("Scheduling options:\n"), out);
      80  	fputs(_(" -R, --reset-on-fork       set reset-on-fork flag\n"), out);
      81  	fputs(_(" -T, --sched-runtime <ns>  runtime parameter for DEADLINE\n"), out);
      82  	fputs(_(" -P, --sched-period <ns>   period parameter for DEADLINE\n"), out);
      83  	fputs(_(" -D, --sched-deadline <ns> deadline parameter for DEADLINE\n"), out);
      84  
      85  	fputs(USAGE_SEPARATOR, out);
      86  	fputs(_("Other options:\n"), out);
      87  	fputs(_(" -a, --all-tasks      operate on all the tasks (threads) for a given pid\n"), out);
      88  	fputs(_(" -m, --max            show min and max valid priorities\n"), out);
      89  	fputs(_(" -p, --pid            operate on existing given pid\n"), out);
      90  	fputs(_(" -v, --verbose        display status information\n"), out);
      91  
      92  	fputs(USAGE_SEPARATOR, out);
      93  	printf(USAGE_HELP_OPTIONS(22));
      94  
      95  	printf(USAGE_MAN_TAIL("chrt(1)"));
      96  	exit(EXIT_SUCCESS);
      97  }
      98  
      99  static const char *get_policy_name(int policy)
     100  {
     101  #ifdef SCHED_RESET_ON_FORK
     102  	policy &= ~SCHED_RESET_ON_FORK;
     103  #endif
     104  	switch (policy) {
     105  	case SCHED_OTHER:
     106  		return "SCHED_OTHER";
     107  	case SCHED_FIFO:
     108  		return "SCHED_FIFO";
     109  #ifdef SCHED_IDLE
     110  	case SCHED_IDLE:
     111  		return "SCHED_IDLE";
     112  #endif
     113  	case SCHED_RR:
     114  		return "SCHED_RR";
     115  #ifdef SCHED_BATCH
     116  	case SCHED_BATCH:
     117  		return "SCHED_BATCH";
     118  #endif
     119  #ifdef SCHED_DEADLINE
     120  	case SCHED_DEADLINE:
     121  		return "SCHED_DEADLINE";
     122  #endif
     123  	default:
     124  		break;
     125  	}
     126  
     127  	return _("unknown");
     128  }
     129  
     130  static void show_sched_pid_info(struct chrt_ctl *ctl, pid_t pid)
     131  {
     132  	int policy = -1, reset_on_fork = 0, prio = 0;
     133  #ifdef SCHED_DEADLINE
     134  	uint64_t deadline = 0, runtime = 0, period = 0;
     135  #endif
     136  
     137  	/* don't display "pid 0" as that is confusing */
     138  	if (!pid)
     139  		pid = getpid();
     140  
     141  	errno = 0;
     142  
     143  	/*
     144  	 * New way
     145  	 */
     146  #ifdef HAVE_SCHED_SETATTR
     147  	{
     148  		struct sched_attr sa;
     149  
     150  		if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0) {
     151  			if (errno == ENOSYS)
     152  				goto fallback;
     153  			err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
     154  		}
     155  
     156  		policy = sa.sched_policy;
     157  		prio = sa.sched_priority;
     158  		reset_on_fork = sa.sched_flags & SCHED_FLAG_RESET_ON_FORK;
     159  		deadline = sa.sched_deadline;
     160  		runtime = sa.sched_runtime;
     161  		period = sa.sched_period;
     162  	}
     163  
     164  	/*
     165  	 * Old way
     166  	 */
     167  fallback:
     168  	if (errno == ENOSYS)
     169  #endif
     170  	{
     171  		struct sched_param sp;
     172  
     173  		policy = sched_getscheduler(pid);
     174  		if (policy == -1)
     175  			err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
     176  
     177  		if (sched_getparam(pid, &sp) != 0)
     178  			err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid);
     179  		else
     180  			prio = sp.sched_priority;
     181  # ifdef SCHED_RESET_ON_FORK
     182  		if (policy & SCHED_RESET_ON_FORK)
     183  			reset_on_fork = 1;
     184  # endif
     185  	}
     186  
     187  	if (ctl->altered)
     188  		printf(_("pid %d's new scheduling policy: %s"), pid, get_policy_name(policy));
     189  	else
     190  		printf(_("pid %d's current scheduling policy: %s"), pid, get_policy_name(policy));
     191  
     192  	if (reset_on_fork)
     193  		printf("|SCHED_RESET_ON_FORK");
     194  	putchar('\n');
     195  
     196  	if (ctl->altered)
     197  		printf(_("pid %d's new scheduling priority: %d\n"), pid, prio);
     198  	else
     199  		printf(_("pid %d's current scheduling priority: %d\n"), pid, prio);
     200  
     201  #ifdef SCHED_DEADLINE
     202  	if (policy == SCHED_DEADLINE) {
     203  		if (ctl->altered)
     204  			printf(_("pid %d's new runtime/deadline/period parameters: %ju/%ju/%ju\n"),
     205  					pid, runtime, deadline, period);
     206  		else
     207  			printf(_("pid %d's current runtime/deadline/period parameters: %ju/%ju/%ju\n"),
     208  					pid, runtime, deadline, period);
     209  	}
     210  #endif
     211  }
     212  
     213  
     214  static void show_sched_info(struct chrt_ctl *ctl)
     215  {
     216  	if (ctl->all_tasks) {
     217  #ifdef __linux__
     218  		DIR *sub = NULL;
     219  		pid_t tid;
     220  		struct path_cxt *pc = ul_new_procfs_path(ctl->pid, NULL);
     221  
     222  		while (pc && procfs_process_next_tid(pc, &sub, &tid) == 0)
     223  			show_sched_pid_info(ctl, tid);
     224  
     225  		ul_unref_path(pc);
     226  #else
     227  		err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
     228  #endif
     229  	} else
     230  		show_sched_pid_info(ctl, ctl->pid);
     231  }
     232  
     233  static void show_min_max(void)
     234  {
     235  	unsigned long i;
     236  	int policies[] = {
     237  		SCHED_OTHER,
     238  		SCHED_FIFO,
     239  		SCHED_RR,
     240  #ifdef SCHED_BATCH
     241  		SCHED_BATCH,
     242  #endif
     243  #ifdef SCHED_IDLE
     244  		SCHED_IDLE,
     245  #endif
     246  #ifdef SCHED_DEADLINE
     247  		SCHED_DEADLINE,
     248  #endif
     249  	};
     250  
     251  	for (i = 0; i < ARRAY_SIZE(policies); i++) {
     252  		int plc = policies[i];
     253  		int max = sched_get_priority_max(plc);
     254  		int min = sched_get_priority_min(plc);
     255  
     256  		if (max >= 0 && min >= 0)
     257  			printf(_("%s min/max priority\t: %d/%d\n"),
     258  					get_policy_name(plc), min, max);
     259  		else
     260  			printf(_("%s not supported?\n"), get_policy_name(plc));
     261  	}
     262  }
     263  
     264  static int set_sched_one_by_setscheduler(struct chrt_ctl *ctl, pid_t pid)
     265  {
     266  	struct sched_param sp = { .sched_priority = ctl->priority };
     267  	int policy = ctl->policy;
     268  
     269  	errno = 0;
     270  # ifdef SCHED_RESET_ON_FORK
     271  	if (ctl->reset_on_fork)
     272  		policy |= SCHED_RESET_ON_FORK;
     273  # endif
     274  
     275  #if defined (__linux__) && defined(SYS_sched_setscheduler)
     276  	/* musl libc returns ENOSYS for its sched_setscheduler library
     277  	 * function, because the sched_setscheduler Linux kernel system call
     278  	 * does not conform to Posix; so we use the system call directly
     279  	 */
     280  	return syscall(SYS_sched_setscheduler, pid, policy, &sp);
     281  #else
     282  	return sched_setscheduler(pid, policy, &sp);
     283  #endif
     284  }
     285  
     286  
     287  #ifndef HAVE_SCHED_SETATTR
     288  static int set_sched_one(struct chrt_ctl *ctl, pid_t pid)
     289  {
     290  	return set_sched_one_by_setscheduler(ctl, pid);
     291  }
     292  
     293  #else /* !HAVE_SCHED_SETATTR */
     294  static int set_sched_one(struct chrt_ctl *ctl, pid_t pid)
     295  {
     296  	struct sched_attr sa = { .size = sizeof(struct sched_attr) };
     297  
     298  	/* old API is good enough for non-deadline */
     299  	if (ctl->policy != SCHED_DEADLINE)
     300  		return set_sched_one_by_setscheduler(ctl, pid);
     301  
     302  	/* no changeed by chrt, follow the current setting */
     303  	sa.sched_nice = getpriority(PRIO_PROCESS, pid);
     304  
     305  	/* use main() to check if the setting makes sense */
     306  	sa.sched_policy	  = ctl->policy;
     307  	sa.sched_priority = ctl->priority;
     308  	sa.sched_runtime  = ctl->runtime;
     309  	sa.sched_period   = ctl->period;
     310  	sa.sched_deadline = ctl->deadline;
     311  
     312  # ifdef SCHED_FLAG_RESET_ON_FORK
     313  	/* Don't use SCHED_RESET_ON_FORK for sched_setattr()! */
     314  	if (ctl->reset_on_fork)
     315  		sa.sched_flags |= SCHED_FLAG_RESET_ON_FORK;
     316  # endif
     317  	errno = 0;
     318  	return sched_setattr(pid, &sa, 0);
     319  }
     320  #endif /* HAVE_SCHED_SETATTR */
     321  
     322  static void set_sched(struct chrt_ctl *ctl)
     323  {
     324  	if (ctl->all_tasks) {
     325  #ifdef __linux__
     326  		DIR *sub = NULL;
     327  		pid_t tid;
     328  		struct path_cxt *pc = ul_new_procfs_path(ctl->pid, NULL);
     329  
     330  		if (!pc)
     331  			err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
     332  
     333  		while (procfs_process_next_tid(pc, &sub, &tid) == 0) {
     334  			if (set_sched_one(ctl, tid) == -1)
     335  				err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid);
     336  		}
     337  		ul_unref_path(pc);
     338  #else
     339  		err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
     340  #endif
     341  	} else if (set_sched_one(ctl, ctl->pid) == -1)
     342  		err(EXIT_FAILURE, _("failed to set pid %d's policy"), ctl->pid);
     343  
     344  	ctl->altered = 1;
     345  }
     346  
     347  int main(int argc, char **argv)
     348  {
     349  	struct chrt_ctl _ctl = { .pid = -1, .policy = SCHED_RR }, *ctl = &_ctl;
     350  	int c;
     351  
     352  	static const struct option longopts[] = {
     353  		{ "all-tasks",  no_argument, NULL, 'a' },
     354  		{ "batch",	no_argument, NULL, 'b' },
     355  		{ "deadline",   no_argument, NULL, 'd' },
     356  		{ "fifo",	no_argument, NULL, 'f' },
     357  		{ "idle",	no_argument, NULL, 'i' },
     358  		{ "pid",	no_argument, NULL, 'p' },
     359  		{ "help",	no_argument, NULL, 'h' },
     360  		{ "max",        no_argument, NULL, 'm' },
     361  		{ "other",	no_argument, NULL, 'o' },
     362  		{ "rr",		no_argument, NULL, 'r' },
     363  		{ "sched-runtime",  required_argument, NULL, 'T' },
     364  		{ "sched-period",   required_argument, NULL, 'P' },
     365  		{ "sched-deadline", required_argument, NULL, 'D' },
     366  		{ "reset-on-fork",  no_argument,       NULL, 'R' },
     367  		{ "verbose",	no_argument, NULL, 'v' },
     368  		{ "version",	no_argument, NULL, 'V' },
     369  		{ NULL,		no_argument, NULL, 0 }
     370  	};
     371  
     372  	setlocale(LC_ALL, "");
     373  	bindtextdomain(PACKAGE, LOCALEDIR);
     374  	textdomain(PACKAGE);
     375  	close_stdout_atexit();
     376  
     377  	while((c = getopt_long(argc, argv, "+abdD:fiphmoP:T:rRvV", longopts, NULL)) != -1)
     378  	{
     379  		switch (c) {
     380  		case 'a':
     381  			ctl->all_tasks = 1;
     382  			break;
     383  		case 'b':
     384  #ifdef SCHED_BATCH
     385  			ctl->policy = SCHED_BATCH;
     386  #endif
     387  			break;
     388  
     389  		case 'd':
     390  #ifdef SCHED_DEADLINE
     391  			ctl->policy = SCHED_DEADLINE;
     392  #endif
     393  			break;
     394  		case 'f':
     395  			ctl->policy = SCHED_FIFO;
     396  			break;
     397  		case 'R':
     398  			ctl->reset_on_fork = 1;
     399  			break;
     400  		case 'i':
     401  #ifdef SCHED_IDLE
     402  			ctl->policy = SCHED_IDLE;
     403  #endif
     404  			break;
     405  		case 'm':
     406  			show_min_max();
     407  			return EXIT_SUCCESS;
     408  		case 'o':
     409  			ctl->policy = SCHED_OTHER;
     410  			break;
     411  		case 'p':
     412  			errno = 0;
     413  			ctl->pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument"));
     414  			break;
     415  		case 'r':
     416  			ctl->policy = SCHED_RR;
     417  			break;
     418  		case 'v':
     419  			ctl->verbose = 1;
     420  			break;
     421  		case 'T':
     422  			ctl->runtime = strtou64_or_err(optarg, _("invalid runtime argument"));
     423  			break;
     424  		case 'P':
     425  			ctl->period = strtou64_or_err(optarg, _("invalid period argument"));
     426  			break;
     427  		case 'D':
     428  			ctl->deadline = strtou64_or_err(optarg, _("invalid deadline argument"));
     429  			break;
     430  
     431  		case 'V':
     432  			print_version(EXIT_SUCCESS);
     433  		case 'h':
     434  			usage();
     435  		default:
     436  			errtryhelp(EXIT_FAILURE);
     437  		}
     438  	}
     439  
     440  	if (((ctl->pid > -1) && argc - optind < 1) ||
     441  	    ((ctl->pid == -1) && argc - optind < 2)) {
     442  		warnx(_("bad usage"));
     443  		errtryhelp(EXIT_FAILURE);
     444  }
     445  
     446  	if ((ctl->pid > -1) && (ctl->verbose || argc - optind == 1)) {
     447  		show_sched_info(ctl);
     448  		if (argc - optind == 1)
     449  			return EXIT_SUCCESS;
     450  	}
     451  
     452  	errno = 0;
     453  	ctl->priority = strtos32_or_err(argv[optind], _("invalid priority argument"));
     454  
     455  #ifdef SCHED_DEADLINE
     456  	if ((ctl->runtime || ctl->deadline || ctl->period) && ctl->policy != SCHED_DEADLINE)
     457  		errx(EXIT_FAILURE, _("--sched-{runtime,deadline,period} options "
     458  				     "are supported for SCHED_DEADLINE only"));
     459  	if (ctl->policy == SCHED_DEADLINE) {
     460  		/* The basic rule is runtime <= deadline <= period, so we can
     461  		 * make deadline and runtime optional on command line. Note we
     462  		 * don't check any values or set any defaults, it's kernel
     463  		 * responsibility.
     464  		 */
     465  		if (ctl->deadline == 0)
     466  			ctl->deadline = ctl->period;
     467  		if (ctl->runtime == 0)
     468  			ctl->runtime = ctl->deadline;
     469  	}
     470  #else
     471  	if (ctl->runtime || ctl->deadline || ctl->period)
     472  		errx(EXIT_FAILURE, _("SCHED_DEADLINE is unsupported"));
     473  #endif
     474  	if (ctl->pid == -1)
     475  		ctl->pid = 0;
     476  	if (ctl->priority < sched_get_priority_min(ctl->policy) ||
     477  	    sched_get_priority_max(ctl->policy) < ctl->priority)
     478  		errx(EXIT_FAILURE,
     479  		     _("unsupported priority value for the policy: %d: see --max for valid range"),
     480  		     ctl->priority);
     481  	set_sched(ctl);
     482  
     483  	if (ctl->verbose)
     484  		show_sched_info(ctl);
     485  
     486  	if (!ctl->pid) {
     487  		argv += optind + 1;
     488  		execvp(argv[0], argv);
     489  		errexec(argv[0]);
     490  	}
     491  
     492  	return EXIT_SUCCESS;
     493  }