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