xref: /aosp_15_r20/external/toybox/toys/other/uclampset.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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