]> git.ipfire.org Git - thirdparty/util-linux.git/blob - schedutils/uclampset.c
Add uclampset schedutil
[thirdparty/util-linux.git] / schedutils / uclampset.c
1 /*
2 * uclampset.c - change utilization clamping attributes of a task or the system
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 *
17 * Copyright (C) 2020-2021 Qais Yousef
18 * Copyright (C) 2020-2021 Arm Ltd
19 */
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <sched.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "closestream.h"
28 #include "path.h"
29 #include "pathnames.h"
30 #include "procutils.h"
31 #include "sched_attr.h"
32 #include "strutils.h"
33
34 #define NOT_SET -2U
35
36 struct uclampset {
37 unsigned int util_min;
38 unsigned int util_max;
39
40 pid_t pid;
41 unsigned int all_tasks:1, /* all threads of the PID */
42 system:1,
43 util_min_set:1, /* indicates -m option was passed */
44 util_max_set:1, /* indicates -M option was passed */
45 reset_on_fork:1,
46 verbose:1;
47 char *cmd;
48 };
49
50 static void __attribute__((__noreturn__)) usage(void)
51 {
52 FILE *out = stdout;
53
54 fprintf(out,
55 _(" %1$s [options]\n"
56 " %1$s [options] --pid <pid> | --system | <command> <arg>...\n"),
57 program_invocation_short_name);
58
59 fputs(USAGE_SEPARATOR, out);
60 fputs(_("Show or change the utilization clamping attributes of a process or the system.\n"), out);
61 fputs(_("Utilization range: [0:1024]\n"), out);
62 fputs(_("Use special -1 value to reset to system's default\n"), out);
63
64 fputs(USAGE_OPTIONS, out);
65 fputs(_(" -m util_min value to set\n"), out);
66 fputs(_(" -M util_max value to set\n"), out);
67 fputs(_(" -a, --all-tasks operate on all the tasks (threads) for a given pid\n"), out);
68 fputs(_(" -p, --pid operate on existing given pid\n"), out);
69 fputs(_(" -s, --system operate on system\n"), out);
70 fputs(_(" -R, --reset-on-fork set reset-on-fork flag\n"), out);
71 fputs(_(" -v, --verbose display status information\n"), out);
72
73 fputs(USAGE_SEPARATOR, out);
74 printf(USAGE_HELP_OPTIONS(22));
75
76 printf(USAGE_MAN_TAIL("uclampset(1)"));
77 exit(EXIT_SUCCESS);
78 }
79
80 static void show_uclamp_pid_info(pid_t pid, char *cmd)
81 {
82 struct sched_attr sa;
83 char *comm;
84
85 /* don't display "pid 0" as that is confusing */
86 if (!pid)
87 pid = getpid();
88
89 if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0)
90 err(EXIT_FAILURE, _("failed to get pid %d's uclamp values"), pid);
91
92 if (cmd)
93 comm = cmd;
94 else
95 comm = proc_get_command_name(pid);
96
97 printf(_("%s (%d) util_clamp: min: %d max: %d\n"),
98 comm ? : "uknown", pid, sa.sched_util_min, sa.sched_util_max);
99
100 if (!cmd)
101 free(comm);
102 }
103
104 static unsigned int read_uclamp_sysfs(char *filename)
105 {
106 unsigned int val;
107
108 if (ul_path_read_u32(NULL, &val, filename) != 0)
109 err(EXIT_FAILURE, _("cannot read %s"), filename);
110
111 return val;
112 }
113
114 static void write_uclamp_sysfs(char *filename, unsigned int val)
115 {
116 if (ul_path_write_u64(NULL, val, filename) != 0)
117 err(EXIT_FAILURE, _("cannot write %s"), filename);
118 }
119
120 static void show_uclamp_system_info(void)
121 {
122 unsigned int min, max;
123
124 min = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN);
125 max = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX);
126
127 printf(_("System util_clamp: min: %u max: %u\n"), min, max);
128 }
129
130 static void show_uclamp_info(struct uclampset *ctl)
131 {
132 if (ctl->system) {
133 show_uclamp_system_info();
134 } else if (ctl->all_tasks) {
135 pid_t tid;
136 struct proc_tasks *ts = proc_open_tasks(ctl->pid);
137
138 if (!ts)
139 err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
140
141 while (!proc_next_tid(ts, &tid))
142 show_uclamp_pid_info(tid, NULL);
143
144 proc_close_tasks(ts);
145 } else {
146 show_uclamp_pid_info(ctl->pid, ctl->cmd);
147 }
148 }
149
150 static int set_uclamp_one(struct uclampset *ctl, pid_t pid)
151 {
152 struct sched_attr sa;
153
154 if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0)
155 err(EXIT_FAILURE, _("failed to get pid %d's uclamp values"), pid);
156
157 if (ctl->util_min_set)
158 sa.sched_util_min = ctl->util_min;
159 if (ctl->util_max_set)
160 sa.sched_util_max = ctl->util_max;
161
162 sa.sched_flags = SCHED_FLAG_KEEP_POLICY |
163 SCHED_FLAG_KEEP_PARAMS |
164 SCHED_FLAG_UTIL_CLAMP_MIN |
165 SCHED_FLAG_UTIL_CLAMP_MAX;
166
167 if (ctl->reset_on_fork)
168 sa.sched_flags |= SCHED_FLAG_RESET_ON_FORK;
169
170 return sched_setattr(pid, &sa, 0);
171 }
172
173 static void set_uclamp_pid(struct uclampset *ctl)
174 {
175 if (ctl->all_tasks) {
176 pid_t tid;
177 struct proc_tasks *ts = proc_open_tasks(ctl->pid);
178
179 if (!ts)
180 err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
181
182 while (!proc_next_tid(ts, &tid))
183 if (set_uclamp_one(ctl, tid) == -1)
184 err(EXIT_FAILURE, _("failed to set tid %d's uclamp values"), tid);
185
186 proc_close_tasks(ts);
187
188 } else if (set_uclamp_one(ctl, ctl->pid) == -1) {
189 err(EXIT_FAILURE, _("failed to set pid %d's uclamp values"), ctl->pid);
190 }
191 }
192
193 static void set_uclamp_system(struct uclampset *ctl)
194 {
195 if (!ctl->util_min_set)
196 ctl->util_min = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN);
197
198 if (!ctl->util_max_set)
199 ctl->util_max = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX);
200
201 if (ctl->util_min > ctl->util_max) {
202 errno = EINVAL;
203 err(EXIT_FAILURE, _("util_min must be <= util_max"));
204 }
205
206 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN, ctl->util_min);
207 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX, ctl->util_max);
208 }
209
210 static void validate_util(int val)
211 {
212 if (val > 1024 || val < -1) {
213 errno = EINVAL;
214 err(EXIT_FAILURE, _("%d out of range"), val);
215 }
216 }
217
218 int main(int argc, char **argv)
219 {
220 struct uclampset _ctl = {
221 .pid = -1,
222 .util_min = NOT_SET,
223 .util_max = NOT_SET,
224 .cmd = NULL
225 };
226 struct uclampset *ctl = &_ctl;
227 int c;
228
229 static const struct option longopts[] = {
230 { "all-tasks", no_argument, NULL, 'a' },
231 { "pid", required_argument, NULL, 'p' },
232 { "system", no_argument, NULL, 's' },
233 { "reset-on-fork", no_argument, NULL, 'R' },
234 { "help", no_argument, NULL, 'h' },
235 { "verbose", no_argument, NULL, 'v' },
236 { "version", no_argument, NULL, 'V' },
237 { NULL, no_argument, NULL, 0 }
238 };
239
240 setlocale(LC_ALL, "");
241 bindtextdomain(PACKAGE, LOCALEDIR);
242 textdomain(PACKAGE);
243 close_stdout_atexit();
244
245 while((c = getopt_long(argc, argv, "+asRp:hm:M:vV", longopts, NULL)) != -1)
246 {
247 switch (c) {
248 case 'a':
249 ctl->all_tasks = 1;
250 break;
251 case 'p':
252 errno = 0;
253 ctl->pid = strtos32_or_err(optarg, _("invalid PID argument"));
254 optind++;
255 break;
256 case 's':
257 ctl->system = 1;
258 break;
259 case 'R':
260 ctl->reset_on_fork = 1;
261 break;
262 case 'v':
263 ctl->verbose = 1;
264 break;
265 case 'm':
266 ctl->util_min = strtos32_or_err(optarg, _("invalid util_min argument"));
267 ctl->util_min_set = 1;
268 validate_util(ctl->util_min);
269 optind++;
270 break;
271 case 'M':
272 ctl->util_max = strtos32_or_err(optarg, _("invalid util_max argument"));
273 ctl->util_max_set = 1;
274 validate_util(ctl->util_max);
275 optind++;
276 break;
277 case 'V':
278 print_version(EXIT_SUCCESS);
279 /* fallthrough */
280 case 'h':
281 usage();
282 default:
283 errtryhelp(EXIT_FAILURE);
284 }
285 }
286
287 if (argc == 1) {
288 usage();
289 exit(EXIT_FAILURE);
290 }
291
292 /* all_tasks implies --pid */
293 if (ctl->all_tasks && ctl->pid == -1) {
294 errno = EINVAL;
295 err(EXIT_FAILURE, _("missing -p option"));
296 }
297
298 if (!ctl->util_min_set && !ctl->util_max_set) {
299 /* -p or -s must be passed */
300 if (!ctl->system && ctl->pid == -1) {
301 usage();
302 exit(EXIT_FAILURE);
303 }
304
305 show_uclamp_info(ctl);
306 return EXIT_SUCCESS;
307 }
308
309 /* ensure there's a command to execute if no -s or -p */
310 if (!ctl->system && ctl->pid == -1) {
311 if (argc <= optind) {
312 errno = EINVAL;
313 err(EXIT_FAILURE, _("no cmd to execute"));
314 }
315
316 argv += optind;
317 ctl->cmd = argv[0];
318 }
319
320 if (ctl->pid == -1)
321 ctl->pid = 0;
322
323 if (ctl->system)
324 set_uclamp_system(ctl);
325 else
326 set_uclamp_pid(ctl);
327
328 if (ctl->verbose)
329 show_uclamp_info(ctl);
330
331 if (ctl->cmd) {
332 execvp(ctl->cmd, argv);
333 errexec(ctl->cmd);
334 }
335
336 return EXIT_SUCCESS;
337 }