1 /* uclampset.c - The quota version of "nice".
2 *
3 * Copyright 2021 The Android Open Source Project
4 *
5 * See https://man7.org/linux/man-pages/man1/uclampset.1.html
6
7 USE_UCLAMPSET(NEWTOY(uclampset, "p#am#<-1>1024M#<-1>1024R", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
8
9 config UCLAMPSET
10 bool "uclampset"
11 default y
12 help
13 usage: uclampset [-m MIN] [-M MAX] {-p PID | COMMAND...}
14
15 Set or query process utilization limits ranging from 0 to 1024, or -1 to
16 reset to system default. With no arguments, prints current values.
17
18 -m MIN Reserve at least this much CPU utilization for task
19 -M MAX Limit task to at most this much CPU utilization
20 -p PID Apply to PID rather than new COMMAND
21 -R Reset child processes to default values on fork
22 -a Apply to all threads for the given PID
23 */
24
25 #define FOR_uclampset
26 #include "toys.h"
27
GLOBALS(long M,m,p;)28 GLOBALS(
29 long M, m, p;
30 )
31
32 // Added to 5.3 kernel (commit a509a7cd7974): too new to rely on headers
33 #ifndef SCHED_FLAG_RESET_ON_FORK
34 #define SCHED_FLAG_RESET_ON_FORK 0x01
35 #define SCHED_FLAG_KEEP_POLICY 0x08
36 #define SCHED_FLAG_KEEP_PARAMS 0x10
37 #define SCHED_FLAG_UTIL_CLAMP_MIN 0x20
38 #define SCHED_FLAG_UTIL_CLAMP_MAX 0x40
39 #endif
40
41 static void do_uclampset(pid_t pid)
42 {
43 unsigned *sa = (void *)toybuf; // sa[12] is min, sa[13] is max
44 char *comm, buf[32];
45
46 if (FLAG(R)|FLAG(m)|FLAG(M)) {
47 if (syscall(__NR_sched_setattr, pid, sa, 0))
48 perror_exit("sched_setattr for pid %d", pid);
49 } else {
50 sprintf(buf, "/proc/%u/comm", pid);
51 comm = chomp(xreadfile(buf, 0, 0));
52 if (syscall(__NR_sched_getattr, pid, sa, *sa, 0))
53 perror_exit("sched_getattr for pid %d", pid);
54 printf("%s (%d) util_clamp: min: %u max: %u\n", comm, pid, sa[12], sa[13]);
55 free(comm);
56 }
57 }
58
task_callback(struct dirtree * new)59 static int task_callback(struct dirtree *new)
60 {
61 if (!new->parent) return DIRTREE_RECURSE;
62 if (isdigit(*new->name)) do_uclampset(atoi(new->name));
63
64 return 0;
65 }
66
uclampset_main(void)67 void uclampset_main(void)
68 {
69 unsigned *sa = (void *)toybuf;
70 long long *flags = (void *)(sa+2);
71 char buf[32];
72
73 sa[0] = 14*4; // size
74 sa[12] = TT.m;
75 sa[13] = TT.M;
76 *flags = SCHED_FLAG_KEEP_POLICY | SCHED_FLAG_KEEP_PARAMS;
77 if (FLAG(R)) *flags |= SCHED_FLAG_RESET_ON_FORK;
78 if (FLAG(m)) *flags |= SCHED_FLAG_UTIL_CLAMP_MIN;
79 if (FLAG(M)) *flags |= SCHED_FLAG_UTIL_CLAMP_MAX;
80
81 if (!FLAG(p)) {
82 if (toys.optc < 1) error_exit("Need -p PID or CMD [ARG...]");
83 do_uclampset(getpid());
84 xexec(toys.optargs);
85 } else if (FLAG(a)) {
86 sprintf(buf, "/proc/%lu/task", TT.p);
87 dirtree_read(buf, task_callback);
88 } else do_uclampset(TT.p);
89 }
90