diff options
Diffstat (limited to 'schedutils')
-rw-r--r-- | schedutils/Makemodule.am | 22 | ||||
-rw-r--r-- | schedutils/chrt.1 | 158 | ||||
-rw-r--r-- | schedutils/chrt.c | 339 | ||||
-rw-r--r-- | schedutils/ionice.1 | 114 | ||||
-rw-r--r-- | schedutils/ionice.c | 238 | ||||
-rw-r--r-- | schedutils/taskset.1 | 128 | ||||
-rw-r--r-- | schedutils/taskset.c | 242 |
7 files changed, 1241 insertions, 0 deletions
diff --git a/schedutils/Makemodule.am b/schedutils/Makemodule.am new file mode 100644 index 0000000..d447536 --- /dev/null +++ b/schedutils/Makemodule.am @@ -0,0 +1,22 @@ +if BUILD_SCHEDUTILS + +usrbin_exec_PROGRAMS += chrt +dist_man_MANS += schedutils/chrt.1 +chrt_SOURCES = schedutils/chrt.c +chrt_LDADD = $(LDADD) libcommon.la + +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 + +endif # BUILD_SCHEDUTILS diff --git a/schedutils/chrt.1 b/schedutils/chrt.1 new file mode 100644 index 0000000..b85717b --- /dev/null +++ b/schedutils/chrt.1 @@ -0,0 +1,158 @@ +.\" chrt(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 as +.\" published by the Free Software Foundation; either version 2 of +.\" the License. +.\" +.\" 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. +.\" +.\" 2002-05-11 Robert Love <rml@tech9.net> +.\" Initial version +.\" +.TH CHRT 1 "June 2010" "util-linux" "User Commands" +.SH NAME +chrt \- manipulate the real-time attributes of a process +.SH SYNOPSIS +.B chrt +.RI [ options ]\ prio +.IR command\ [ arg ]... +.br +.B chrt +.RI [ options ] +.B \-p +.RI [ prio ]\ pid +.SH DESCRIPTION +.PP +.B chrt +sets or retrieves the real-time scheduling attributes of an existing \fIpid\fR, +or runs \fIcommand\fR with the given attributes. Both policy (one of +.BR SCHED_OTHER , +.BR SCHED_FIFO , +.BR SCHED_RR , +.BR SCHED_BATCH , +or +.BR SCHED_IDLE ) +and priority can be set and retrieved. +.PP +The +.BR SCHED_BATCH +policy is supported since Linux 2.6.16. The +.BR SCHED_IDLE +policy is supported since Linux 2.6.23. +.PP +The +.BR SCHED_RESET_ON_FORK +flag for policies SCHED_RR and SCHED_FIFO is supported +since Linux 2.6.31. +.SH OPTIONS +.TP +.B -a, --all-tasks +Set or retrieve the scheduling attributes of all the tasks (threads) for a +given PID. +.TP +.B -b, --batch +Set scheduling policy to +.BR SCHED_BATCH +(Linux specific). +.TP +.B -f, --fifo +Set scheduling policy to +.BR SCHED_FIFO . +.TP +.B -i, --idle +Set scheduling policy to +.BR SCHED_IDLE +(Linux specific). +.TP +.B -m, --max +Show minimum and maximum valid priorities, then exit. +.TP +.B -o, --other +Set policy scheduling policy to +.BR SCHED_OTHER . +.TP +.B -p, --pid +Operate on an existing PID and do not launch a new task. +.TP +.B -r, --rr +Set scheduling policy to +.BR SCHED_RR +(the default). +.TP +.B -R, --reset-on-fork +Add +.B SCHED_RESET_ON_FORK +flag to the +.B SCHED_FIFO +or +.B SCHED_RR +scheduling policy (Linux specific). +.TP +.B -v, --verbose +Show status information. +.TP +.B -h, --help +Display usage information and exit. +.TP +.B -V, --version +Display version information and exit. +.SH USAGE +.TP +The default behavior is to run a new command: +.B chrt +.I prio +.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 \-p +.I prio pid +.SH PERMISSIONS +A user must possess +.BR CAP_SYS_NICE +to change the scheduling attributes of a process. Any user can retrieve the +scheduling information. +.SH NOTES +Only +.BR SCHED_FIFO , +.BR SCHED_OTHER +and +.BR SCHED_RR +are part of POSIX 1003.1b Process Scheduling. The other scheduling attributes +may be ignored on some systems. +.SH AUTHOR +Written by Robert M. Love. +.SH COPYRIGHT +Copyright \(co 2004 Robert M. Love +.br +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 taskset (1), +.BR nice (1), +.BR renice (1) +.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 +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/schedutils/chrt.c b/schedutils/chrt.c new file mode 100644 index 0000000..95a6adc --- /dev/null +++ b/schedutils/chrt.c @@ -0,0 +1,339 @@ +/* + * chrt.c - chrt + * Command-line utility for manipulating a task's real-time attributes + * + * Robert Love <rml@tech9.net> + * 27-Apr-2002: initial version + * 04-May-2011: make 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, v2, 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 "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 + +#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK) +#define SCHED_RESET_ON_FORK 0x40000000 +#endif + + +static void __attribute__((__noreturn__)) show_usage(int rc) +{ + FILE *out = rc == EXIT_SUCCESS ? stdout : stderr; + + fprintf(out, _( + "\nchrt - manipulate real-time attributes of a process\n" + "\nSet policy:\n" + " chrt [options] <policy> <priority> {<pid> | <command> [<arg> ...]}\n" + "\nGet policy:\n" + " chrt [options] {<pid> | <command> [<arg> ...]}\n")); + + fprintf(out, _( + "\nScheduling policies:\n" + " -b | --batch set policy to SCHED_BATCH\n" + " -f | --fifo set policy to SCHED_FIFO\n" + " -i | --idle set policy to SCHED_IDLE\n" + " -o | --other set policy to SCHED_OTHER\n" + " -r | --rr set policy to SCHED_RR (default)\n")); + +#ifdef SCHED_RESET_ON_FORK + fprintf(out, _( + "\nScheduling flags:\n" + " -R | --reset-on-fork set SCHED_RESET_ON_FORK for FIFO or RR\n")); +#endif + fprintf(out, _( + "\nOptions:\n" + " -a | --all-tasks operate on all the tasks (threads) for a given pid\n" + " -h | --help display this help\n" + " -m | --max show min and max valid priorities\n" + " -p | --pid operate on existing given pid\n" + " -v | --verbose display status information\n" + " -V | --version output version information\n\n")); + + exit(rc); +} + +static void show_rt_info(pid_t pid, int isnew) +{ + struct sched_param sp; + int policy; + + /* don't display "pid 0" as that is confusing */ + if (!pid) + pid = getpid(); + + policy = sched_getscheduler(pid); + if (policy == -1) + err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid); + + if (isnew) + printf(_("pid %d's new scheduling policy: "), pid); + else + printf(_("pid %d's current scheduling policy: "), pid); + + switch (policy) { + case SCHED_OTHER: + printf("SCHED_OTHER\n"); + break; + case SCHED_FIFO: + printf("SCHED_FIFO\n"); + break; +#ifdef SCHED_RESET_ON_FORK + case SCHED_FIFO | SCHED_RESET_ON_FORK: + printf("SCHED_FIFO|SCHED_RESET_ON_FORK\n"); + break; +#endif +#ifdef SCHED_IDLE + case SCHED_IDLE: + printf("SCHED_IDLE\n"); + break; +#endif + case SCHED_RR: + printf("SCHED_RR\n"); + break; +#ifdef SCHED_RESET_ON_FORK + case SCHED_RR | SCHED_RESET_ON_FORK: + printf("SCHED_RR|SCHED_RESET_ON_FORK\n"); + break; +#endif +#ifdef SCHED_BATCH + case SCHED_BATCH: + printf("SCHED_BATCH\n"); + break; +#endif + default: + warnx(_("unknown scheduling policy")); + } + + if (sched_getparam(pid, &sp)) + err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid); + + if (isnew) + printf(_("pid %d's new scheduling priority: %d\n"), + pid, sp.sched_priority); + else + printf(_("pid %d's current scheduling priority: %d\n"), + pid, sp.sched_priority); +} + +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 + }; + const char *names[] = { + "OTHER", + "FIFO", + "RR", +#ifdef SCHED_BATCH + "BATCH", +#endif +#ifdef SCHED_IDLE + "IDLE", +#endif + }; + + for (i = 0; i < ARRAY_SIZE(policies); i++) { + int max = sched_get_priority_max(policies[i]); + int min = sched_get_priority_min(policies[i]); + + if (max >= 0 && min >= 0) + printf(_("SCHED_%s min/max priority\t: %d/%d\n"), + names[i], min, max); + else + printf(_("SCHED_%s not supported?\n"), names[i]); + } +} + +int main(int argc, char **argv) +{ + int i, policy = SCHED_RR, priority = 0, verbose = 0, policy_flag = 0, + all_tasks = 0; + struct sched_param sp; + pid_t pid = -1; + + static const struct option longopts[] = { + { "all-tasks", 0, NULL, 'a' }, + { "batch", 0, NULL, 'b' }, + { "fifo", 0, NULL, 'f' }, + { "idle", 0, NULL, 'i' }, + { "pid", 0, NULL, 'p' }, + { "help", 0, NULL, 'h' }, + { "max", 0, NULL, 'm' }, + { "other", 0, NULL, 'o' }, + { "rr", 0, NULL, 'r' }, + { "reset-on-fork", 0, NULL, 'R' }, + { "verbose", 0, NULL, 'v' }, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while((i = getopt_long(argc, argv, "+abfiphmoRrvV", longopts, NULL)) != -1) + { + int ret = EXIT_FAILURE; + + switch (i) { + case 'a': + all_tasks = 1; + break; + case 'b': +#ifdef SCHED_BATCH + policy = SCHED_BATCH; +#endif + break; + case 'f': + policy = SCHED_FIFO; + break; + case 'R': +#ifdef SCHED_RESET_ON_FORK + policy_flag |= SCHED_RESET_ON_FORK; +#endif + break; + case 'i': +#ifdef SCHED_IDLE + policy = SCHED_IDLE; +#endif + break; + case 'm': + show_min_max(); + return EXIT_SUCCESS; + case 'o': + policy = SCHED_OTHER; + break; + case 'p': + errno = 0; + pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument")); + break; + case 'r': + policy = SCHED_RR; + break; + case 'v': + verbose = 1; + break; + case 'V': + printf("%s from %s\n", program_invocation_short_name, + PACKAGE_STRING); + return EXIT_SUCCESS; + case 'h': + ret = EXIT_SUCCESS; + /* fallthrough */ + default: + show_usage(ret); + } + } + + if (((pid > -1) && argc - optind < 1) || + ((pid == -1) && argc - optind < 2)) + show_usage(EXIT_FAILURE); + + if ((pid > -1) && (verbose || argc - optind == 1)) { + if (all_tasks) { + pid_t tid; + struct proc_tasks *ts = proc_open_tasks(pid); + + if (!ts) + err(EXIT_FAILURE, _("cannot obtain the list of tasks")); + while (!proc_next_tid(ts, &tid)) + show_rt_info(tid, FALSE); + proc_close_tasks(ts); + } else + show_rt_info(pid, FALSE); + + if (argc - optind == 1) + return EXIT_SUCCESS; + } + + errno = 0; + priority = strtos32_or_err(argv[optind], _("invalid priority argument")); + +#ifdef SCHED_RESET_ON_FORK + /* sanity check */ + if ((policy_flag & SCHED_RESET_ON_FORK) && + !(policy == SCHED_FIFO || policy == SCHED_RR)) + errx(EXIT_FAILURE, _("SCHED_RESET_ON_FORK flag is suppoted for " + "SCHED_FIFO and SCHED_RR policies only")); +#endif + + policy |= policy_flag; + + if (pid == -1) + pid = 0; + sp.sched_priority = priority; + + if (all_tasks) { + pid_t tid; + struct proc_tasks *ts = proc_open_tasks(pid); + + if (!ts) + err(EXIT_FAILURE, _("cannot obtain the list of tasks")); + while (!proc_next_tid(ts, &tid)) + if (sched_setscheduler(tid, policy, &sp) == -1) + err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid); + proc_close_tasks(ts); + } else if (sched_setscheduler(pid, policy, &sp) == -1) + err(EXIT_FAILURE, _("failed to set pid %d's policy"), pid); + + if (verbose) + show_rt_info(pid, TRUE); + + if (!pid) { + argv += optind + 1; + execvp(argv[0], argv); + perror("execvp"); + err(EXIT_FAILURE, _("failed to execute %s"), argv[0]); + } + + return EXIT_SUCCESS; +} diff --git a/schedutils/ionice.1 b/schedutils/ionice.1 new file mode 100644 index 0000000..163df1d --- /dev/null +++ b/schedutils/ionice.1 @@ -0,0 +1,114 @@ +.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 ] +.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). +.TP +.BR \-p , " \-\-pid " \fIPID\fR... +Specify the process 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 and exit. +.TP +.BR \-V , " \-\-version" +Display version information and exit. +.SH EXAMPLES +.LP +.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 NOTES +Linux supports I/O scheduling priorities and classes since 2.6.13 with the CFQ +I/O scheduler. +.SH AUTHORS +.nf +Jens Axboe <jens@axboe.dk> +Karel Zak <kzak@redhat.com> +.fi +.SH AVAILABILITY +The ionice command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/schedutils/ionice.c b/schedutils/ionice.c new file mode 100644 index 0000000..e25428d --- /dev/null +++ b/schedutils/ionice.c @@ -0,0 +1,238 @@ +/* + * 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) + +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 ioprio = ioprio_get(IOPRIO_WHO_PROCESS, 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_setpid(pid_t pid, int ioclass, int data) +{ + int rc = ioprio_set(IOPRIO_WHO_PROCESS, pid, + IOPRIO_PRIO_VALUE(ioclass, data)); + + if (rc == -1 && !tolerant) + err(EXIT_FAILURE, _("ioprio_set failed")); +} + +static void __attribute__ ((__noreturn__)) usage(FILE * out) +{ + fprintf(out, + _("\n" + "%1$s - sets or gets process io scheduling class and priority.\n" + "\n" + "Usage:\n" + " %1$s [OPTION] -p PID [PID...]\n" + " %1$s [OPTION] COMMAND\n" + "\n" + "Options:\n" + " -c, --class <class> scheduling class name or number\n" + " 0: none, 1: realtime, 2: best-effort, 3: idle\n" + " -n, --classdata <num> scheduling class data\n" + " 0-7 for realtime and best-effort classes\n" + " -p, --pid=PID view or modify already running process\n" + " -t, --ignore ignore failures\n" + " -V, --version output version information and exit\n" + " -h, --help display this help and exit\n\n"), + program_invocation_short_name); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int data = 4, set = 0, ioclass = IOPRIO_CLASS_BE, c; + pid_t pid = 0; + + 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' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while ((c = getopt_long(argc, argv, "+n:c:p: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': + pid = strtos32_or_err(optarg, _("invalid PID argument")); + break; + case 't': + tolerant = 1; + break; + case 'V': + printf(_("%s from %s\n"), + program_invocation_short_name, PACKAGE_STRING); + return EXIT_SUCCESS; + case 'h': + usage(stdout); + default: + usage(stderr); + } + + 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 && !pid && optind == argc) + /* + * ionice without options, print the current ioprio + */ + ioprio_print(0); + + else if (!set && pid) { + /* + * ionice -p PID [PID ...] + */ + ioprio_print(pid); + + for(; argv[optind]; ++optind) { + pid = strtos32_or_err(argv[optind], _("invalid PID argument")); + ioprio_print(pid); + } + } else if (set && pid) { + /* + * ionice -c CLASS -p PID [PID ...] + */ + ioprio_setpid(pid, ioclass, data); + + for(; argv[optind]; ++optind) { + pid = strtos32_or_err(argv[optind], _("invalid PID argument")); + ioprio_setpid(pid, ioclass, data); + } + } else if (argv[optind]) { + /* + * ionice [-c CLASS] COMMAND + */ + ioprio_setpid(0, ioclass, data); + execvp(argv[optind], &argv[optind]); + err(EXIT_FAILURE, _("executing %s failed"), argv[optind]); + } else + usage(stderr); + + + return EXIT_SUCCESS; +} diff --git a/schedutils/taskset.1 b/schedutils/taskset.1 new file mode 100644 index 0000000..efaa0d2 --- /dev/null +++ b/schedutils/taskset.1 @@ -0,0 +1,128 @@ +.\" 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 as +.\" published by the Free Software Foundation; either version 2 of +.\" the License. +.\" +.\" 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. +.\" +.\" 2002-05-11 Robert Love <rml@tech9.net> +.\" Initial version +.\" +.TH TASKSET 1 "April 2003" "util-linux" "User Commands" +.SH NAME +taskset \- retrieve or set a process's CPU affinity +.SH SYNOPSIS +.B taskset +.RI [ options ]\ mask +.IR command\ [ arg ]... +.br +.B taskset +.RI [ options ] +.B \-p +.RI [ mask ]\ pid +.SH DESCRIPTION +.PP +.B taskset +is used to set or retrieve the CPU affinity of a running process given its PID +or to launch a new COMMAND 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 are typically given in hexadecimal. For example, +.TP +.BR 0x00000001 +is processor #0 +.TP +.BR 0x00000003 +is processors #0 and #1 +.TP +.BR 0xFFFFFFFF +is all processors (#0 through #31). +.PP +When +.BR 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 \-p ,\ \-\-pid +Operate on an existing PID and do not launch a new task. +.TP +.BR \-c ,\ \-\-cpu-list +Specify a numerical list of processors instead of a bitmask. The numbers +are separated by commas and may include ranges. For example: +.BR 0,5,7,9-11 . +.TP +.BR \-h ,\ \-\-help +Display usage information and exit. +.TP +.BR \-V ,\ \-\-version +Display version information 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 must possess +.B CAP_SYS_NICE +to change the CPU affinity of a process. Any user can retrieve the affinity +mask. +.SH AUTHOR +Written by Robert M. Love. +.SH COPYRIGHT +Copyright \(co 2004 Robert M. Love +.br +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_setaffinity (2), +.BR sched_getaffinity (2) +.sp +See +.BR sched_setscheduler (2) +for a description of the Linux scheduling scheme. +.SH AVAILABILITY +The taskset command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/schedutils/taskset.c b/schedutils/taskset.c new file mode 100644 index 0000000..c1bb173 --- /dev/null +++ b/schedutils/taskset.c @@ -0,0 +1,242 @@ +/* + * taskset.c - command-line utility for setting and retrieving + * 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, v2, 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(FILE *out) +{ + fprintf(out, + _("Usage: %s [options] [mask | cpu-list] [pid|cmd [args...]]\n\n"), + program_invocation_short_name); + + 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" + " -h, --help display this help\n" + " -V, --version output version information\n\n")); + + 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); + + fprintf(out, _("\nFor more information see taskset(1).\n")); + + exit(out == stderr ? EXIT_FAILURE : 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) + /* this is internal error... */ + errx(EXIT_FAILURE, _("conversion from cpuset to string failed")); + + printf(msg, ts->pid, str); +} + +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(EXIT_FAILURE, _("failed to get pid %d's affinity"), + ts->pid); + print_affinity(ts, FALSE); + } + + if (ts->get_only) + return; + + /* set new mask */ + if (sched_setaffinity(ts->pid, setsize, set) < 0) + err(EXIT_FAILURE, _("failed to set pid %d's affinity"), + ts->pid); + + /* re-read the current mask */ + if (ts->pid) { + if (sched_getaffinity(ts->pid, ts->setsize, ts->set) < 0) + err(EXIT_FAILURE, _("failed to get pid %d's affinity"), + ts->pid); + 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); + atexit(close_stdout); + + 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': + printf("%s from %s\n", program_invocation_short_name, + PACKAGE_STRING); + return EXIT_SUCCESS; + case 'h': + usage(stdout); + break; + default: + usage(stderr); + break; + } + } + + if ((!pid && argc - optind < 2) + || (pid && (argc - optind < 1 || argc - optind > 2))) + usage(stderr); + + 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) { + 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); + err(EXIT_FAILURE, _("executing %s failed"), argv[0]); + } + + return EXIT_SUCCESS; +} |