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