diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:14:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 13:14:44 +0000 |
commit | 30ff6afe596eddafacf22b1a5b2d1a3d6254ea15 (patch) | |
tree | 9b788335f92174baf7ee18f03ca8330b8c19ce2b /schedutils | |
parent | Initial commit. (diff) | |
download | util-linux-30ff6afe596eddafacf22b1a5b2d1a3d6254ea15.tar.xz util-linux-30ff6afe596eddafacf22b1a5b2d1a3d6254ea15.zip |
Adding upstream version 2.36.1.upstream/2.36.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'schedutils')
-rw-r--r-- | schedutils/Makemodule.am | 20 | ||||
-rw-r--r-- | schedutils/chrt.1 | 172 | ||||
-rw-r--r-- | schedutils/chrt.c | 570 | ||||
-rw-r--r-- | schedutils/ionice.1 | 141 | ||||
-rw-r--r-- | schedutils/ionice.c | 267 | ||||
-rw-r--r-- | schedutils/taskset.1 | 139 | ||||
-rw-r--r-- | schedutils/taskset.c | 251 |
7 files changed, 1560 insertions, 0 deletions
diff --git a/schedutils/Makemodule.am b/schedutils/Makemodule.am new file mode 100644 index 0000000..f32d2b3 --- /dev/null +++ b/schedutils/Makemodule.am @@ -0,0 +1,20 @@ +if BUILD_CHRT +usrbin_exec_PROGRAMS += chrt +dist_man_MANS += schedutils/chrt.1 +chrt_SOURCES = schedutils/chrt.c +chrt_LDADD = $(LDADD) libcommon.la +endif + +if BUILD_IONICE +usrbin_exec_PROGRAMS += ionice +dist_man_MANS += schedutils/ionice.1 +ionice_SOURCES = schedutils/ionice.c +ionice_LDADD = $(LDADD) libcommon.la +endif + +if BUILD_TASKSET +usrbin_exec_PROGRAMS += taskset +dist_man_MANS += schedutils/taskset.1 +taskset_SOURCES = schedutils/taskset.c +taskset_LDADD = $(LDADD) libcommon.la +endif diff --git a/schedutils/chrt.1 b/schedutils/chrt.1 new file mode 100644 index 0000000..0d9a225 --- /dev/null +++ b/schedutils/chrt.1 @@ -0,0 +1,172 @@ +.\" chrt(1) manpage +.\" +.\" Copyright (C) 2004 Robert Love +.\" Copyright (C) 2015 Karel Zak <kzak@redhat.com> +.\" +.\" This is free documentation; 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. +.\" +.\" The GNU General Public License's references to "object code" +.\" and "executables" are to be interpreted as the output of any +.\" document formatting or typesetting system, including +.\" intermediate and printed output. +.\" +.\" This manual 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. +.\" +.TH CHRT 1 "January 2016" "util-linux" "User Commands" +.SH NAME +chrt \- manipulate the real-time attributes of a process +.SH SYNOPSIS +.B chrt +[options] +.IR priority\ command\ [ argument ...] +.br +.B chrt +[options] +.B \-p +.RI [ priority ]\ pid +.SH DESCRIPTION +.B chrt +sets or retrieves the real-time scheduling attributes of an existing \fIpid\fR, +or runs \fIcommand\fR with the given attributes. + +.SH POLICIES +.TP +\fB\-o\fR, \fB\-\-other\fR +Set scheduling policy to +.BR SCHED_OTHER . +This is the default Linux scheduling policy. +.TP +\fB\-f\fR, \fB\-\-fifo\fR +Set scheduling policy to \fBSCHED_FIFO\fR. +.TP +\fB\-r\fR, \fB\-\-rr\fR +Set scheduling policy to +.BR SCHED_RR . +When no policy is defined, the +.B SCHED_RR +is used as the default. +.TP +\fB\-b\fR, \fB\-\-batch\fR +Set scheduling policy to +.B SCHED_BATCH +(Linux-specific, supported since 2.6.16). The priority argument has to be set to zero. +.TP +\fB\-i\fR, \fB\-\-idle\fR +Set scheduling policy to +.B SCHED_IDLE +(Linux-specific, supported since 2.6.23). The priority argument has to be set to zero. +.TP +.BR \-d ,\ \-\-deadline +Set scheduling policy to +.B SCHED_DEADLINE +(Linux-specific, supported since 3.14). The priority argument has to be set to zero. +See also \fB\-\-sched\-runtime\fR, \fB\-\-sched\-deadline\fR and +\fB\-\-sched\-period\fR. The relation between the options required by the kernel is +runtime <= deadline <= period. +.B chrt +copies \fIperiod\fR to \fIdeadline\fR if \fB\-\-sched\-deadline\fR is not specified and +\fIdeadline\fR to \fIruntime\fR if \fB\-\-sched\-runtime\fR is not specified. +It means that at least \fB\-\-sched\-period\fR has to be specified. See +.BR sched (7) +for more details. + +.SH SCHEDULING OPTIONS +.TP +\fB\-T\fR, \fB\-\-sched\-runtime\fR \fInanoseconds\fR +Specifies runtime parameter for SCHED_DEADLINE policy (Linux-specific). +.TP +\fB\-P\fR, \fB\-\-sched\-period\fR \fInanoseconds\fR +Specifies period parameter for SCHED_DEADLINE policy (Linux-specific). +.TP +\fB\-D\fR, \fB\-\-sched\-deadline\fR \fInanoseconds\fR +Specifies deadline parameter for SCHED_DEADLINE policy (Linux-specific). +.TP +\fB\-R\fR, \fB\-\-reset-on-fork\fR +Add +.B SCHED_RESET_ON_FORK +flag to the +.B SCHED_FIFO +or +.B SCHED_RR +scheduling policy (Linux-specific, supported since 2.6.31). + +.SH OPTIONS +.TP +.BR \-a ,\ \-\-all-tasks +Set or retrieve the scheduling attributes of all the tasks (threads) for a +given PID. +.TP +.BR \-m ,\ \-\-max +Show minimum and maximum valid priorities, then exit. +.TP +.BR \-p ,\ \-\-pid +Operate on an existing PID and do not launch a new task. +.TP +.BR \-v ,\ \-\-verbose +Show status information. +.TP +.BR \-V ,\ \-\-version +Display version information and exit. +.TP +.BR \-h ,\ \-\-help +Display help text and exit. +.SH USAGE +.TP +The default behavior is to run a new command: +.B chrt +.I priority +.IR command\ [ arguments ] +.TP +You can also retrieve the real-time attributes of an existing task: +.B chrt \-p +.I pid +.TP +Or set them: +.B chrt \-r \-p +.I priority pid +.SH PERMISSIONS +A user must possess +.B CAP_SYS_NICE +to change the scheduling attributes of a process. Any user can retrieve the +scheduling information. + +.SH NOTES +Only +.BR SCHED_FIFO , +.B SCHED_OTHER +and +.B SCHED_RR +are part of POSIX 1003.1b Process Scheduling. The other scheduling attributes +may be ignored on some systems. +.P +Linux' default scheduling policy is +.BR SCHED_OTHER . +.SH AUTHORS +.UR rml@tech9.net +Robert Love +.UE +.br +.UR kzak@redhat.com +Karel Zak +.UE +.SH SEE ALSO +.BR nice (1), +.BR renice (1), +.BR taskset (1), +.BR sched (7) +.sp +See +.BR sched_setscheduler (2) +for a description of the Linux scheduling scheme. +.SH AVAILABILITY +The chrt command is part of the util-linux package and is available from +https://www.kernel.org/pub/linux/utils/util-linux/. 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; +} diff --git a/schedutils/ionice.1 b/schedutils/ionice.1 new file mode 100644 index 0000000..bbb6b7e --- /dev/null +++ b/schedutils/ionice.1 @@ -0,0 +1,141 @@ +.TH IONICE 1 "July 2011" "util-linux" "User Commands" +.SH NAME +ionice \- set or get process I/O scheduling class and priority +.SH SYNOPSIS +.B ionice +.RB [ \-c +.IR class ] +.RB [ \-n +.IR level ] +.RB [ \-t ] +.B \-p +.IR PID ... +.br +.B ionice +.RB [ \-c +.IR class ] +.RB [ \-n +.IR level ] +.RB [ \-t ] +.B \-P +.IR PGID ... +.br +.B ionice +.RB [ \-c +.IR class ] +.RB [ \-n +.IR level ] +.RB [ \-t ] +.B \-u +.IR UID ... +.br +.B ionice +.RB [ \-c +.IR class ] +.RB [ \-n +.IR level ] +.RB [ \-t ] +.IR "command " [ argument ...] +.SH DESCRIPTION +This program sets or gets the I/O scheduling class and priority for a program. +If no arguments or just \fB\-p\fR is given, \fBionice\fR will query the current +I/O scheduling class and priority for that process. + +When \fIcommand\fR is given, +.B ionice +will run this command with the given arguments. +If no \fIclass\fR is specified, then +.I command +will be executed with the "best-effort" scheduling class. The default +priority level is 4. + +As of this writing, a process can be in one of three scheduling classes: +.IP "\fBIdle\fP" +A program running with idle I/O priority will only get disk time when no other +program has asked for disk I/O for a defined grace period. The impact of an +idle I/O process on normal system activity should be zero. This scheduling +class does not take a priority argument. Presently, this scheduling class +is permitted for an ordinary user (since kernel 2.6.25). +.IP "\fBBest-effort\fP" +This is the effective scheduling class for any process that has not asked for +a specific I/O priority. +This class takes a priority argument from \fI0-7\fR, with a lower +number being higher priority. Programs running at the same best-effort +priority are served in a round-robin fashion. + +Note that before kernel 2.6.26 a process that has not asked for an I/O priority +formally uses "\fBnone\fP" as scheduling class, but the I/O scheduler will treat +such processes as if it were in the best-effort class. The priority within the +best-effort class will be dynamically derived from the CPU nice level of the +process: io_priority = (cpu_nice + 20) / 5. + +For kernels after 2.6.26 with the CFQ I/O scheduler, a process that has not asked +for an I/O priority inherits its CPU scheduling class. The I/O priority is derived +from the CPU nice level of the process (same as before kernel 2.6.26). + +.IP "\fBRealtime\fP" +The RT scheduling class is given first access to the disk, regardless of +what else is going on in the system. Thus the RT class needs to be used with +some care, as it can starve other processes. As with the best-effort class, +8 priority levels are defined denoting how big a time slice a given process +will receive on each scheduling window. This scheduling class is not +permitted for an ordinary (i.e., non-root) user. +.SH OPTIONS +.TP +.BR \-c , " \-\-class " \fIclass\fR +Specify the name or number of the scheduling class to use; \fI0\fR for none, +\fI1\fR for realtime, \fI2\fR for best-effort, \fI3\fR for idle. +.TP +.BR \-n , " \-\-classdata " \fIlevel\fR +Specify the scheduling class data. This only has an effect if the class +accepts an argument. For realtime and best-effort, \fI0-7\fR are valid data +(priority levels), and \fI0\fR represents the highest priority level. +.TP +.BR \-p , " \-\-pid " \fIPID\fR... +Specify the process IDs of running processes for which to get or set the +scheduling parameters. +.TP +.BR \-P , " \-\-pgid " \fIPGID\fR... +Specify the process group IDs of running processes for which to get or set the +scheduling parameters. +.TP +.BR \-t , " \-\-ignore" +Ignore failure to set the requested priority. If \fIcommand\fR was specified, +run it even in case it was not possible to set the desired scheduling priority, +which can happen due to insufficient privileges or an old kernel version. +.TP +.BR \-h , " \-\-help" +Display help text and exit. +.TP +.BR \-u , " \-\-uid " \fIUID\fR... +Specify the user IDs of running processes for which to get or set the +scheduling parameters. +.TP +.BR \-V , " \-\-version" +Display version information and exit. +.SH NOTES +Linux supports I/O scheduling priorities and classes since 2.6.13 with the CFQ +I/O scheduler. +.SH EXAMPLES +.TP 7 +# \fBionice\fP \-c 3 \-p 89 +.TP 7 +Sets process with PID 89 as an idle I/O process. +.TP 7 +# \fBionice\fP \-c 2 \-n 0 bash +.TP 7 +Runs 'bash' as a best-effort program with highest priority. +.TP 7 +# \fBionice\fP \-p 89 91 +.TP 7 +Prints the class and priority of the processes with PID 89 and 91. +.SH AUTHORS +.nf +Jens Axboe <jens@axboe.dk> +Karel Zak <kzak@redhat.com> +.fi +.SH SEE ALSO +.BR ioprio_set (2) +.SH AVAILABILITY +The ionice command is part of the util-linux package and is available from +https://www.kernel.org/pub/linux/utils/util-linux/. diff --git a/schedutils/ionice.c b/schedutils/ionice.c new file mode 100644 index 0000000..ae2c73e --- /dev/null +++ b/schedutils/ionice.c @@ -0,0 +1,267 @@ +/* + * ionice: set or get process io scheduling class and priority + * + * Copyright (C) 2005 Jens Axboe <jens@axboe.dk> + * + * Released under the terms of the GNU General Public License version 2 + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <ctype.h> + +#include "nls.h" +#include "strutils.h" +#include "c.h" +#include "closestream.h" + +static int tolerant; + +static inline int ioprio_set(int which, int who, int ioprio) +{ + return syscall(SYS_ioprio_set, which, who, ioprio); +} + +static inline int ioprio_get(int which, int who) +{ + return syscall(SYS_ioprio_get, which, who); +} + +enum { + IOPRIO_CLASS_NONE, + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE, +}; + +enum { + IOPRIO_WHO_PROCESS = 1, + IOPRIO_WHO_PGRP, + IOPRIO_WHO_USER, +}; + +#define IOPRIO_CLASS_SHIFT (13) +#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) + +#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) +#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) +#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) + +static const char *to_prio[] = { + [IOPRIO_CLASS_NONE] = "none", + [IOPRIO_CLASS_RT] = "realtime", + [IOPRIO_CLASS_BE] = "best-effort", + [IOPRIO_CLASS_IDLE] = "idle" +}; + +static int parse_ioclass(const char *str) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(to_prio); i++) + if (!strcasecmp(str, to_prio[i])) + return i; + return -1; +} + +static void ioprio_print(int pid, int who) +{ + int ioprio = ioprio_get(who, pid); + + if (ioprio == -1) + err(EXIT_FAILURE, _("ioprio_get failed")); + else { + int ioclass = IOPRIO_PRIO_CLASS(ioprio); + const char *name = _("unknown"); + + if (ioclass >= 0 && (size_t) ioclass < ARRAY_SIZE(to_prio)) + name = to_prio[ioclass]; + + if (ioclass != IOPRIO_CLASS_IDLE) + printf(_("%s: prio %lu\n"), name, + IOPRIO_PRIO_DATA(ioprio)); + else + printf("%s\n", name); + } +} + +static void ioprio_setid(int which, int ioclass, int data, int who) +{ + int rc = ioprio_set(who, which, + IOPRIO_PRIO_VALUE(ioclass, data)); + + if (rc == -1 && !tolerant) + err(EXIT_FAILURE, _("ioprio_set failed")); +} + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + fputs(USAGE_HEADER, out); + fprintf(out, _(" %1$s [options] -p <pid>...\n" + " %1$s [options] -P <pgid>...\n" + " %1$s [options] -u <uid>...\n" + " %1$s [options] <command>\n"), program_invocation_short_name); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Show or change the I/O-scheduling class and priority of a process.\n"), out); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -c, --class <class> name or number of scheduling class,\n" + " 0: none, 1: realtime, 2: best-effort, 3: idle\n"), out); + fputs(_(" -n, --classdata <num> priority (0..7) in the specified scheduling class,\n" + " only for the realtime and best-effort classes\n"), out); + fputs(_(" -p, --pid <pid>... act on these already running processes\n"), out); + fputs(_(" -P, --pgid <pgrp>... act on already running processes in these groups\n"), out); + fputs(_(" -t, --ignore ignore failures\n"), out); + fputs(_(" -u, --uid <uid>... act on already running processes owned by these users\n"), out); + + fputs(USAGE_SEPARATOR, out); + printf(USAGE_HELP_OPTIONS(24)); + + printf(USAGE_MAN_TAIL("ionice(1)")); + + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int data = 4, set = 0, ioclass = IOPRIO_CLASS_BE, c; + int which = 0, who = 0; + const char *invalid_msg = NULL; + + static const struct option longopts[] = { + { "classdata", required_argument, NULL, 'n' }, + { "class", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { "ignore", no_argument, NULL, 't' }, + { "pid", required_argument, NULL, 'p' }, + { "pgid", required_argument, NULL, 'P' }, + { "uid", required_argument, NULL, 'u' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + close_stdout_atexit(); + + while ((c = getopt_long(argc, argv, "+n:c:p:P:u:tVh", longopts, NULL)) != EOF) + switch (c) { + case 'n': + data = strtos32_or_err(optarg, _("invalid class data argument")); + set |= 1; + break; + case 'c': + if (isdigit(*optarg)) + ioclass = strtos32_or_err(optarg, + _("invalid class argument")); + else { + ioclass = parse_ioclass(optarg); + if (ioclass < 0) + errx(EXIT_FAILURE, + _("unknown scheduling class: '%s'"), + optarg); + } + set |= 2; + break; + case 'p': + if (who) + errx(EXIT_FAILURE, + _("can handle only one of pid, pgid or uid at once")); + invalid_msg = _("invalid PID argument"); + which = strtos32_or_err(optarg, invalid_msg); + who = IOPRIO_WHO_PROCESS; + break; + case 'P': + if (who) + errx(EXIT_FAILURE, + _("can handle only one of pid, pgid or uid at once")); + invalid_msg = _("invalid PGID argument"); + which = strtos32_or_err(optarg, invalid_msg); + who = IOPRIO_WHO_PGRP; + break; + case 'u': + if (who) + errx(EXIT_FAILURE, + _("can handle only one of pid, pgid or uid at once")); + invalid_msg = _("invalid UID argument"); + which = strtos32_or_err(optarg, invalid_msg); + who = IOPRIO_WHO_USER; + break; + case 't': + tolerant = 1; + break; + + case 'V': + print_version(EXIT_SUCCESS); + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + + switch (ioclass) { + case IOPRIO_CLASS_NONE: + if ((set & 1) && !tolerant) + warnx(_("ignoring given class data for none class")); + data = 0; + break; + case IOPRIO_CLASS_RT: + case IOPRIO_CLASS_BE: + break; + case IOPRIO_CLASS_IDLE: + if ((set & 1) && !tolerant) + warnx(_("ignoring given class data for idle class")); + data = 7; + break; + default: + if (!tolerant) + warnx(_("unknown prio class %d"), ioclass); + break; + } + + if (!set && !which && optind == argc) + /* + * ionice without options, print the current ioprio + */ + ioprio_print(0, IOPRIO_WHO_PROCESS); + else if (!set && who) { + /* + * ionice -p|-P|-u ID [ID ...] + */ + ioprio_print(which, who); + + for(; argv[optind]; ++optind) { + which = strtos32_or_err(argv[optind], invalid_msg); + ioprio_print(which, who); + } + } else if (set && who) { + /* + * ionice -c CLASS -p|-P|-u ID [ID ...] + */ + ioprio_setid(which, ioclass, data, who); + + for(; argv[optind]; ++optind) { + which = strtos32_or_err(argv[optind], invalid_msg); + ioprio_setid(which, ioclass, data, who); + } + } else if (argv[optind]) { + /* + * ionice [-c CLASS] COMMAND + */ + ioprio_setid(0, ioclass, data, IOPRIO_WHO_PROCESS); + execvp(argv[optind], &argv[optind]); + errexec(argv[optind]); + } else { + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} diff --git a/schedutils/taskset.1 b/schedutils/taskset.1 new file mode 100644 index 0000000..71e7874 --- /dev/null +++ b/schedutils/taskset.1 @@ -0,0 +1,139 @@ +.\" taskset(1) manpage +.\" +.\" Copyright (C) 2004 Robert Love +.\" +.\" This is free documentation; 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. +.\" +.\" The GNU General Public License's references to "object code" +.\" and "executables" are to be interpreted as the output of any +.\" document formatting or typesetting system, including +.\" intermediate and printed output. +.\" +.\" This manual 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. +.\" +.TH TASKSET 1 "August 2014" "util-linux" "User Commands" +.SH NAME +taskset \- set or retrieve a process's CPU affinity +.SH SYNOPSIS +.B taskset +[options] +.IR mask\ command\ [ argument ...] +.br +.B taskset +[options] +.B \-p +.RI [ mask ]\ pid +.SH DESCRIPTION +.B taskset +is used to set or retrieve the CPU affinity of a running process given its +\fIpid\fR, or to launch a new \fIcommand\fR with a given CPU affinity. +CPU affinity is a +scheduler property that "bonds" a process to a given set of CPUs on the system. +The Linux scheduler will honor the given CPU affinity and the process will not +run on any other CPUs. Note that the Linux scheduler also supports natural +CPU affinity: the scheduler attempts to keep processes on the same CPU as long +as practical for performance reasons. Therefore, forcing a specific CPU +affinity is useful only in certain applications. +.sp +The CPU affinity is represented as a bitmask, with the lowest order bit +corresponding to the first logical CPU and the highest order bit corresponding +to the last logical CPU. Not all CPUs may exist on a given system but a mask +may specify more CPUs than are present. A retrieved mask will reflect only the +bits that correspond to CPUs physically on the system. If an invalid mask is +given (i.e., one that corresponds to no valid CPUs on the current system) an +error is returned. The masks may be specified in hexadecimal (with or without +a leading "0x"), or as a CPU list with the +.B \-\-cpu\-list +option. For example, +.RS 4 +.TP 12 +.B 0x00000001 +is processor #0, +.TP +.B 0x00000003 +is processors #0 and #1, +.TP +.B 0xFFFFFFFF +is processors #0 through #31, +.TP +.B 32 +is processors #1, #4, and #5, +.TP +.B \-\-cpu\-list\ 0-2,6 +is processors #0, #1, #2, and #6. +.TP +.B \-\-cpu\-list\ 0-10:2 +is processors #0, #2, #4, #6, #8 and #10. The suffix ":N" specifies stride in +the range, for example 0-10:3 is interpreted as 0,3,6,9 list. +.RE +.PP +When +.B taskset +returns, it is guaranteed that the given program has been scheduled to a legal +CPU. +.SH OPTIONS +.TP +.BR \-a ,\ \-\-all\-tasks +Set or retrieve the CPU affinity of all the tasks (threads) for a given PID. +.TP +.BR \-c ,\ \-\-cpu\-list +Interpret \fImask\fR as numerical list of processors instead of a bitmask. +Numbers are separated by commas and may include ranges. For example: +.BR 0,5,8-11 . +.TP +.BR \-p ,\ \-\-pid +Operate on an existing PID and do not launch a new task. +.TP +.BR \-V ,\ \-\-version +Display version information and exit. +.TP +.BR \-h ,\ \-\-help +Display help text and exit. +.SH USAGE +.TP +The default behavior is to run a new command with a given affinity mask: +.B taskset +.I mask +.IR command\ [ arguments ] +.TP +You can also retrieve the CPU affinity of an existing task: +.B taskset \-p +.I pid +.TP +Or set it: +.B taskset \-p +.I mask pid +.SH PERMISSIONS +A user can change the CPU affinity of a process belonging to the same user. +A user must possess +.B CAP_SYS_NICE +to change the CPU affinity of a process belonging to another user. +A user can retrieve the affinity mask of any process. +.SH AUTHORS +Written by Robert M. Love. +.SH COPYRIGHT +Copyright \(co 2004 Robert M. Love. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH SEE ALSO +.BR chrt (1), +.BR nice (1), +.BR renice (1), +.BR sched_getaffinity (2), +.BR sched_setaffinity (2) +.sp +See +.BR sched (7) +for a description of the Linux scheduling scheme. +.SH AVAILABILITY +The taskset command is part of the util-linux package and is available from +https://www.kernel.org/pub/linux/utils/util-linux/. diff --git a/schedutils/taskset.c b/schedutils/taskset.c new file mode 100644 index 0000000..56db817 --- /dev/null +++ b/schedutils/taskset.c @@ -0,0 +1,251 @@ +/* + * taskset.c - set or retrieve a task's CPU affinity + * + * 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 + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <errno.h> +#include <sched.h> +#include <stddef.h> +#include <string.h> + +#include "cpuset.h" +#include "nls.h" +#include "strutils.h" +#include "xalloc.h" +#include "procutils.h" +#include "c.h" +#include "closestream.h" + +struct taskset { + pid_t pid; /* task PID */ + cpu_set_t *set; /* task CPU mask */ + size_t setsize; + char *buf; /* buffer for conversion from mask to string */ + size_t buflen; + unsigned int use_list:1, /* use list rather than masks */ + get_only:1; /* print the mask, but not modify */ +}; + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + fprintf(out, + _("Usage: %s [options] [mask | cpu-list] [pid|cmd [args...]]\n\n"), + program_invocation_short_name); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Show or change the CPU affinity of a process.\n"), out); + fputs(USAGE_SEPARATOR, out); + + fprintf(out, _( + "Options:\n" + " -a, --all-tasks operate on all the tasks (threads) for a given pid\n" + " -p, --pid operate on existing given pid\n" + " -c, --cpu-list display and specify cpus in list format\n" + )); + printf(USAGE_HELP_OPTIONS(25)); + + fputs(USAGE_SEPARATOR, out); + fprintf(out, _( + "The default behavior is to run a new command:\n" + " %1$s 03 sshd -b 1024\n" + "You can retrieve the mask of an existing task:\n" + " %1$s -p 700\n" + "Or set it:\n" + " %1$s -p 03 700\n" + "List format uses a comma-separated list instead of a mask:\n" + " %1$s -pc 0,3,7-11 700\n" + "Ranges in list format can take a stride argument:\n" + " e.g. 0-31:2 is equivalent to mask 0x55555555\n"), + program_invocation_short_name); + + printf(USAGE_MAN_TAIL("taskset(1)")); + exit(EXIT_SUCCESS); +} + +static void print_affinity(struct taskset *ts, int isnew) +{ + char *str, *msg; + + if (ts->use_list) { + str = cpulist_create(ts->buf, ts->buflen, ts->set, ts->setsize); + msg = isnew ? _("pid %d's new affinity list: %s\n") : + _("pid %d's current affinity list: %s\n"); + } else { + str = cpumask_create(ts->buf, ts->buflen, ts->set, ts->setsize); + msg = isnew ? _("pid %d's new affinity mask: %s\n") : + _("pid %d's current affinity mask: %s\n"); + } + + if (!str) + errx(EXIT_FAILURE, _("internal error: conversion from cpuset to string failed")); + + printf(msg, ts->pid ? ts->pid : getpid(), str); +} + +static void __attribute__((__noreturn__)) err_affinity(pid_t pid, int set) +{ + char *msg; + + msg = set ? _("failed to set pid %d's affinity") : + _("failed to get pid %d's affinity"); + + err(EXIT_FAILURE, msg, pid ? pid : getpid()); +} + +static void do_taskset(struct taskset *ts, size_t setsize, cpu_set_t *set) +{ + /* read the current mask */ + if (ts->pid) { + if (sched_getaffinity(ts->pid, ts->setsize, ts->set) < 0) + err_affinity(ts->pid, 1); + print_affinity(ts, FALSE); + } + + if (ts->get_only) + return; + + /* set new mask */ + if (sched_setaffinity(ts->pid, setsize, set) < 0) + err_affinity(ts->pid, 1); + + /* re-read the current mask */ + if (ts->pid) { + if (sched_getaffinity(ts->pid, ts->setsize, ts->set) < 0) + err_affinity(ts->pid, 0); + print_affinity(ts, TRUE); + } +} + +int main(int argc, char **argv) +{ + cpu_set_t *new_set; + pid_t pid = 0; + int c, all_tasks = 0; + int ncpus; + size_t new_setsize, nbits; + struct taskset ts; + + static const struct option longopts[] = { + { "all-tasks", 0, NULL, 'a' }, + { "pid", 0, NULL, 'p' }, + { "cpu-list", 0, NULL, 'c' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + close_stdout_atexit(); + + memset(&ts, 0, sizeof(ts)); + + while ((c = getopt_long(argc, argv, "+apchV", longopts, NULL)) != -1) { + switch (c) { + case 'a': + all_tasks = 1; + break; + case 'p': + pid = strtos32_or_err(argv[argc - 1], + _("invalid PID argument")); + break; + case 'c': + ts.use_list = 1; + break; + + case 'V': + print_version(EXIT_SUCCESS); + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + } + + if ((!pid && argc - optind < 2) + || (pid && (argc - optind < 1 || argc - optind > 2))) { + warnx(_("bad usage")); + errtryhelp(EXIT_FAILURE); + } + + ncpus = get_max_number_of_cpus(); + if (ncpus <= 0) + errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting")); + + /* + * the ts->set is always used for the sched_getaffinity call + * On the sched_getaffinity the kernel demands a user mask of + * at least the size of its own cpumask_t. + */ + ts.set = cpuset_alloc(ncpus, &ts.setsize, &nbits); + if (!ts.set) + err(EXIT_FAILURE, _("cpuset_alloc failed")); + + /* buffer for conversion from mask to string */ + ts.buflen = 7 * nbits; + ts.buf = xmalloc(ts.buflen); + + /* + * new_set is always used for the sched_setaffinity call + * On the sched_setaffinity the kernel will zero-fill its + * cpumask_t if the user's mask is shorter. + */ + new_set = cpuset_alloc(ncpus, &new_setsize, NULL); + if (!new_set) + err(EXIT_FAILURE, _("cpuset_alloc failed")); + + if (argc - optind == 1) + ts.get_only = 1; + + else if (ts.use_list) { + if (cpulist_parse(argv[optind], new_set, new_setsize, 0)) + errx(EXIT_FAILURE, _("failed to parse CPU list: %s"), + argv[optind]); + } else if (cpumask_parse(argv[optind], new_set, new_setsize)) { + errx(EXIT_FAILURE, _("failed to parse CPU mask: %s"), + argv[optind]); + } + + if (all_tasks && pid) { + struct proc_tasks *tasks = proc_open_tasks(pid); + while (!proc_next_tid(tasks, &ts.pid)) + do_taskset(&ts, new_setsize, new_set); + proc_close_tasks(tasks); + } else { + ts.pid = pid; + do_taskset(&ts, new_setsize, new_set); + } + + free(ts.buf); + cpuset_free(ts.set); + cpuset_free(new_set); + + if (!pid) { + argv += optind + 1; + execvp(argv[0], argv); + errexec(argv[0]); + } + + return EXIT_SUCCESS; +} |