summaryrefslogtreecommitdiffstats
path: root/schedutils/chrt.c
diff options
context:
space:
mode:
Diffstat (limited to 'schedutils/chrt.c')
-rw-r--r--schedutils/chrt.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/schedutils/chrt.c b/schedutils/chrt.c
new file mode 100644
index 0000000..dbc630f
--- /dev/null
+++ b/schedutils/chrt.c
@@ -0,0 +1,570 @@
+/*
+ * chrt.c - manipulate a task's real-time attributes
+ *
+ * 27-Apr-2002: initial version - Robert Love <rml@tech9.net>
+ * 04-May-2011: make it thread-aware - Davidlohr Bueso <dave@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will 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.
+ *
+ * Copyright (C) 2004 Robert Love
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "c.h"
+#include "nls.h"
+#include "closestream.h"
+#include "strutils.h"
+#include "procutils.h"
+
+/* the SCHED_BATCH is supported since Linux 2.6.16
+ * -- temporary workaround for people with old glibc headers
+ */
+#if defined (__linux__) && !defined(SCHED_BATCH)
+# define SCHED_BATCH 3
+#endif
+
+/* the SCHED_IDLE is supported since Linux 2.6.23
+ * commit id 0e6aca43e08a62a48d6770e9a159dbec167bf4c6
+ * -- temporary workaround for people with old glibc headers
+ */
+#if defined (__linux__) && !defined(SCHED_IDLE)
+# define SCHED_IDLE 5
+#endif
+
+/* flag by sched_getscheduler() */
+#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
+# define SCHED_RESET_ON_FORK 0x40000000
+#endif
+
+/* flag by sched_getattr() */
+#if defined(__linux__) && !defined(SCHED_FLAG_RESET_ON_FORK)
+# define SCHED_FLAG_RESET_ON_FORK 0x01
+#endif
+
+#if defined (__linux__)
+# include <sys/syscall.h>
+#endif
+
+/* usable kernel-headers, but old glibc-headers */
+#if defined (__linux__) && !defined(SYS_sched_setattr) && defined(__NR_sched_setattr)
+# define SYS_sched_setattr __NR_sched_setattr
+#endif
+
+#if defined (__linux__) && !defined(SYS_sched_getattr) && defined(__NR_sched_getattr)
+# define SYS_sched_getattr __NR_sched_getattr
+#endif
+
+#if defined (__linux__) && !defined(HAVE_SCHED_SETATTR) && defined(SYS_sched_setattr)
+# define HAVE_SCHED_SETATTR
+
+struct sched_attr {
+ uint32_t size;
+ uint32_t sched_policy;
+ uint64_t sched_flags;
+
+ /* SCHED_NORMAL, SCHED_BATCH */
+ int32_t sched_nice;
+
+ /* SCHED_FIFO, SCHED_RR */
+ uint32_t sched_priority;
+
+ /* SCHED_DEADLINE (nsec) */
+ uint64_t sched_runtime;
+ uint64_t sched_deadline;
+ uint64_t sched_period;
+};
+
+static int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
+{
+ return syscall(SYS_sched_setattr, pid, attr, flags);
+}
+
+static int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags)
+{
+ return syscall(SYS_sched_getattr, pid, attr, size, flags);
+}
+#endif
+
+/* the SCHED_DEADLINE is supported since Linux 3.14
+ * commit id aab03e05e8f7e26f51dee792beddcb5cca9215a5
+ * -- sched_setattr() is required for this policy!
+ */
+#if defined (__linux__) && !defined(SCHED_DEADLINE) && defined(HAVE_SCHED_SETATTR)
+# define SCHED_DEADLINE 6
+#endif
+
+/* control struct */
+struct chrt_ctl {
+ pid_t pid;
+ int policy; /* SCHED_* */
+ int priority;
+
+ uint64_t runtime; /* --sched-* options */
+ uint64_t deadline;
+ uint64_t period;
+
+ unsigned int all_tasks : 1, /* all threads of the PID */
+ reset_on_fork : 1, /* SCHED_RESET_ON_FORK or SCHED_FLAG_RESET_ON_FORK */
+ altered : 1, /* sched_set**() used */
+ verbose : 1; /* verbose output */
+};
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+
+ fputs(_("Show or change the real-time scheduling attributes of a process.\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Set policy:\n"
+ " chrt [options] <priority> <command> [<arg>...]\n"
+ " chrt [options] --pid <priority> <pid>\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Get policy:\n"
+ " chrt [options] -p <pid>\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Policy options:\n"), out);
+ fputs(_(" -b, --batch set policy to SCHED_BATCH\n"), out);
+ fputs(_(" -d, --deadline set policy to SCHED_DEADLINE\n"), out);
+ fputs(_(" -f, --fifo set policy to SCHED_FIFO\n"), out);
+ fputs(_(" -i, --idle set policy to SCHED_IDLE\n"), out);
+ fputs(_(" -o, --other set policy to SCHED_OTHER\n"), out);
+ fputs(_(" -r, --rr set policy to SCHED_RR (default)\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Scheduling options:\n"), out);
+ fputs(_(" -R, --reset-on-fork set SCHED_RESET_ON_FORK for FIFO or RR\n"), out);
+ fputs(_(" -T, --sched-runtime <ns> runtime parameter for DEADLINE\n"), out);
+ fputs(_(" -P, --sched-period <ns> period parameter for DEADLINE\n"), out);
+ fputs(_(" -D, --sched-deadline <ns> deadline parameter for DEADLINE\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Other options:\n"), out);
+ fputs(_(" -a, --all-tasks operate on all the tasks (threads) for a given pid\n"), out);
+ fputs(_(" -m, --max show min and max valid priorities\n"), out);
+ fputs(_(" -p, --pid operate on existing given pid\n"), out);
+ fputs(_(" -v, --verbose display status information\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(22));
+
+ printf(USAGE_MAN_TAIL("chrt(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+static const char *get_policy_name(int policy)
+{
+ switch (policy) {
+ case SCHED_OTHER:
+ return "SCHED_OTHER";
+ case SCHED_FIFO:
+#ifdef SCHED_RESET_ON_FORK
+ case SCHED_FIFO | SCHED_RESET_ON_FORK:
+#endif
+ return "SCHED_FIFO";
+#ifdef SCHED_IDLE
+ case SCHED_IDLE:
+ return "SCHED_IDLE";
+#endif
+ case SCHED_RR:
+#ifdef SCHED_RESET_ON_FORK
+ case SCHED_RR | SCHED_RESET_ON_FORK:
+#endif
+ return "SCHED_RR";
+#ifdef SCHED_BATCH
+ case SCHED_BATCH:
+ return "SCHED_BATCH";
+#endif
+#ifdef SCHED_DEADLINE
+ case SCHED_DEADLINE:
+ return "SCHED_DEADLINE";
+#endif
+ default:
+ break;
+ }
+
+ return _("unknown");
+}
+
+static void show_sched_pid_info(struct chrt_ctl *ctl, pid_t pid)
+{
+ int policy = -1, reset_on_fork = 0, prio = 0;
+#ifdef SCHED_DEADLINE
+ uint64_t deadline = 0, runtime = 0, period = 0;
+#endif
+
+ /* don't display "pid 0" as that is confusing */
+ if (!pid)
+ pid = getpid();
+
+ errno = 0;
+
+ /*
+ * New way
+ */
+#ifdef HAVE_SCHED_SETATTR
+ {
+ struct sched_attr sa;
+
+ if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0) {
+ if (errno == ENOSYS)
+ goto fallback;
+ err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
+ }
+
+ policy = sa.sched_policy;
+ prio = sa.sched_priority;
+ reset_on_fork = sa.sched_flags & SCHED_FLAG_RESET_ON_FORK;
+ deadline = sa.sched_deadline;
+ runtime = sa.sched_runtime;
+ period = sa.sched_period;
+ }
+
+ /*
+ * Old way
+ */
+fallback:
+ if (errno == ENOSYS)
+#endif
+ {
+ struct sched_param sp;
+
+ policy = sched_getscheduler(pid);
+ if (policy == -1)
+ err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
+
+ if (sched_getparam(pid, &sp) != 0)
+ err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid);
+ else
+ prio = sp.sched_priority;
+# ifdef SCHED_RESET_ON_FORK
+ if (policy == (SCHED_FIFO|SCHED_RESET_ON_FORK) || policy == (SCHED_BATCH|SCHED_RESET_ON_FORK))
+ reset_on_fork = 1;
+# endif
+ }
+
+ if (ctl->altered)
+ printf(_("pid %d's new scheduling policy: %s"), pid, get_policy_name(policy));
+ else
+ printf(_("pid %d's current scheduling policy: %s"), pid, get_policy_name(policy));
+
+ if (reset_on_fork)
+ printf("|SCHED_RESET_ON_FORK");
+ putchar('\n');
+
+ if (ctl->altered)
+ printf(_("pid %d's new scheduling priority: %d\n"), pid, prio);
+ else
+ printf(_("pid %d's current scheduling priority: %d\n"), pid, prio);
+
+#ifdef SCHED_DEADLINE
+ if (policy == SCHED_DEADLINE) {
+ if (ctl->altered)
+ printf(_("pid %d's new runtime/deadline/period parameters: %ju/%ju/%ju\n"),
+ pid, runtime, deadline, period);
+ else
+ printf(_("pid %d's current runtime/deadline/period parameters: %ju/%ju/%ju\n"),
+ pid, runtime, deadline, period);
+ }
+#endif
+}
+
+
+static void show_sched_info(struct chrt_ctl *ctl)
+{
+ if (ctl->all_tasks) {
+ pid_t tid;
+ struct proc_tasks *ts = proc_open_tasks(ctl->pid);
+
+ if (!ts)
+ err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
+
+ while (!proc_next_tid(ts, &tid))
+ show_sched_pid_info(ctl, tid);
+
+ proc_close_tasks(ts);
+ } else
+ show_sched_pid_info(ctl, ctl->pid);
+}
+
+static void show_min_max(void)
+{
+ unsigned long i;
+ int policies[] = {
+ SCHED_OTHER,
+ SCHED_FIFO,
+ SCHED_RR,
+#ifdef SCHED_BATCH
+ SCHED_BATCH,
+#endif
+#ifdef SCHED_IDLE
+ SCHED_IDLE,
+#endif
+#ifdef SCHED_DEADLINE
+ SCHED_DEADLINE,
+#endif
+ };
+
+ for (i = 0; i < ARRAY_SIZE(policies); i++) {
+ int plc = policies[i];
+ int max = sched_get_priority_max(plc);
+ int min = sched_get_priority_min(plc);
+
+ if (max >= 0 && min >= 0)
+ printf(_("%s min/max priority\t: %d/%d\n"),
+ get_policy_name(plc), min, max);
+ else
+ printf(_("%s not supported?\n"), get_policy_name(plc));
+ }
+}
+
+static int set_sched_one_by_setscheduler(struct chrt_ctl *ctl, pid_t pid)
+{
+ struct sched_param sp = { .sched_priority = ctl->priority };
+ int policy = ctl->policy;
+
+ errno = 0;
+# ifdef SCHED_RESET_ON_FORK
+ if (ctl->reset_on_fork)
+ policy |= SCHED_RESET_ON_FORK;
+# endif
+
+#if defined (__linux__) && defined(SYS_sched_setscheduler)
+ /* musl libc returns ENOSYS for its sched_setscheduler library
+ * function, because the sched_setscheduler Linux kernel system call
+ * does not conform to Posix; so we use the system call directly
+ */
+ return syscall(SYS_sched_setscheduler, pid, policy, &sp);
+#else
+ return sched_setscheduler(pid, policy, &sp);
+#endif
+}
+
+
+#ifndef HAVE_SCHED_SETATTR
+static int set_sched_one(struct chrt_ctl *ctl, pid_t pid)
+{
+ return set_sched_one_by_setscheduler(ctl, pid);
+}
+
+#else /* !HAVE_SCHED_SETATTR */
+static int set_sched_one(struct chrt_ctl *ctl, pid_t pid)
+{
+ struct sched_attr sa = { .size = sizeof(struct sched_attr) };
+
+ /* old API is good enough for non-deadline */
+ if (ctl->policy != SCHED_DEADLINE)
+ return set_sched_one_by_setscheduler(ctl, pid);
+
+ /* no changeed by chrt, follow the current setting */
+ sa.sched_nice = getpriority(PRIO_PROCESS, pid);
+
+ /* use main() to check if the setting makes sense */
+ sa.sched_policy = ctl->policy;
+ sa.sched_priority = ctl->priority;
+ sa.sched_runtime = ctl->runtime;
+ sa.sched_period = ctl->period;
+ sa.sched_deadline = ctl->deadline;
+
+# ifdef SCHED_FLAG_RESET_ON_FORK
+ /* Don't use SCHED_RESET_ON_FORK for sched_setattr()! */
+ if (ctl->reset_on_fork)
+ sa.sched_flags |= SCHED_FLAG_RESET_ON_FORK;
+# endif
+ errno = 0;
+ return sched_setattr(pid, &sa, 0);
+}
+#endif /* HAVE_SCHED_SETATTR */
+
+static void set_sched(struct chrt_ctl *ctl)
+{
+ if (ctl->all_tasks) {
+ pid_t tid;
+ struct proc_tasks *ts = proc_open_tasks(ctl->pid);
+
+ if (!ts)
+ err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
+
+ while (!proc_next_tid(ts, &tid))
+ if (set_sched_one(ctl, tid) == -1)
+ err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid);
+
+ proc_close_tasks(ts);
+
+ } else if (set_sched_one(ctl, ctl->pid) == -1)
+ err(EXIT_FAILURE, _("failed to set pid %d's policy"), ctl->pid);
+
+ ctl->altered = 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct chrt_ctl _ctl = { .pid = -1, .policy = SCHED_RR }, *ctl = &_ctl;
+ int c;
+
+ static const struct option longopts[] = {
+ { "all-tasks", no_argument, NULL, 'a' },
+ { "batch", no_argument, NULL, 'b' },
+ { "deadline", no_argument, NULL, 'd' },
+ { "fifo", no_argument, NULL, 'f' },
+ { "idle", no_argument, NULL, 'i' },
+ { "pid", no_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { "max", no_argument, NULL, 'm' },
+ { "other", no_argument, NULL, 'o' },
+ { "rr", no_argument, NULL, 'r' },
+ { "sched-runtime", required_argument, NULL, 'T' },
+ { "sched-period", required_argument, NULL, 'P' },
+ { "sched-deadline", required_argument, NULL, 'D' },
+ { "reset-on-fork", no_argument, NULL, 'R' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, no_argument, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while((c = getopt_long(argc, argv, "+abdD:fiphmoP:T:rRvV", longopts, NULL)) != -1)
+ {
+ switch (c) {
+ case 'a':
+ ctl->all_tasks = 1;
+ break;
+ case 'b':
+#ifdef SCHED_BATCH
+ ctl->policy = SCHED_BATCH;
+#endif
+ break;
+
+ case 'd':
+#ifdef SCHED_DEADLINE
+ ctl->policy = SCHED_DEADLINE;
+#endif
+ break;
+ case 'f':
+ ctl->policy = SCHED_FIFO;
+ break;
+ case 'R':
+ ctl->reset_on_fork = 1;
+ break;
+ case 'i':
+#ifdef SCHED_IDLE
+ ctl->policy = SCHED_IDLE;
+#endif
+ break;
+ case 'm':
+ show_min_max();
+ return EXIT_SUCCESS;
+ case 'o':
+ ctl->policy = SCHED_OTHER;
+ break;
+ case 'p':
+ errno = 0;
+ ctl->pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument"));
+ break;
+ case 'r':
+ ctl->policy = SCHED_RR;
+ break;
+ case 'v':
+ ctl->verbose = 1;
+ break;
+ case 'T':
+ ctl->runtime = strtou64_or_err(optarg, _("invalid runtime argument"));
+ break;
+ case 'P':
+ ctl->period = strtou64_or_err(optarg, _("invalid period argument"));
+ break;
+ case 'D':
+ ctl->deadline = strtou64_or_err(optarg, _("invalid deadline argument"));
+ break;
+
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (((ctl->pid > -1) && argc - optind < 1) ||
+ ((ctl->pid == -1) && argc - optind < 2)) {
+ warnx(_("bad usage"));
+ errtryhelp(EXIT_FAILURE);
+}
+
+ if ((ctl->pid > -1) && (ctl->verbose || argc - optind == 1)) {
+ show_sched_info(ctl);
+ if (argc - optind == 1)
+ return EXIT_SUCCESS;
+ }
+
+ errno = 0;
+ ctl->priority = strtos32_or_err(argv[optind], _("invalid priority argument"));
+
+#ifdef SCHED_RESET_ON_FORK
+ if (ctl->reset_on_fork && ctl->policy != SCHED_FIFO && ctl->policy != SCHED_RR)
+ errx(EXIT_FAILURE, _("--reset-on-fork option is supported for "
+ "SCHED_FIFO and SCHED_RR policies only"));
+#endif
+#ifdef SCHED_DEADLINE
+ if ((ctl->runtime || ctl->deadline || ctl->period) && ctl->policy != SCHED_DEADLINE)
+ errx(EXIT_FAILURE, _("--sched-{runtime,deadline,period} options "
+ "are supported for SCHED_DEADLINE only"));
+ if (ctl->policy == SCHED_DEADLINE) {
+ /* The basic rule is runtime <= deadline <= period, so we can
+ * make deadline and runtime optional on command line. Note we
+ * don't check any values or set any defaults, it's kernel
+ * responsibility.
+ */
+ if (ctl->deadline == 0)
+ ctl->deadline = ctl->period;
+ if (ctl->runtime == 0)
+ ctl->runtime = ctl->deadline;
+ }
+#else
+ if (ctl->runtime || ctl->deadline || ctl->period)
+ errx(EXIT_FAILURE, _("SCHED_DEADLINE is unsupported"));
+#endif
+ if (ctl->pid == -1)
+ ctl->pid = 0;
+ if (ctl->priority < sched_get_priority_min(ctl->policy) ||
+ sched_get_priority_max(ctl->policy) < ctl->priority)
+ errx(EXIT_FAILURE,
+ _("unsupported priority value for the policy: %d: see --max for valid range"),
+ ctl->priority);
+ set_sched(ctl);
+
+ if (ctl->verbose)
+ show_sched_info(ctl);
+
+ if (!ctl->pid) {
+ argv += optind + 1;
+ execvp(argv[0], argv);
+ errexec(argv[0]);
+ }
+
+ return EXIT_SUCCESS;
+}