]> git.ipfire.org Git - thirdparty/util-linux.git/blame - schedutils/uclampset.c
Merge branch 'fix-jfs' of https://github.com/mbroz/util-linux
[thirdparty/util-linux.git] / schedutils / uclampset.c
CommitLineData
5407f897
QY
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"
602f734a 30#include "procfs.h"
5407f897
QY
31#include "sched_attr.h"
32#include "strutils.h"
33
34#define NOT_SET -2U
35
36struct 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
50static void __attribute__((__noreturn__)) usage(void)
51{
52 FILE *out = stdout;
53
f4b07719 54 fputs(USAGE_HEADER, out);
5407f897
QY
55 fprintf(out,
56 _(" %1$s [options]\n"
57 " %1$s [options] --pid <pid> | --system | <command> <arg>...\n"),
58 program_invocation_short_name);
59
60 fputs(USAGE_SEPARATOR, out);
f4b07719 61 fputs(_("Show or change the utilization clamping attributes.\n"), out);
5407f897
QY
62
63 fputs(USAGE_OPTIONS, out);
f4b07719
KZ
64 fputs(_(" -m <value> util_min value to set\n"), out);
65 fputs(_(" -M <value> util_max value to set\n"), out);
5407f897 66 fputs(_(" -a, --all-tasks operate on all the tasks (threads) for a given pid\n"), out);
f4b07719 67 fputs(_(" -p, --pid <pid> operate on existing given pid\n"), out);
5407f897
QY
68 fputs(_(" -s, --system operate on system\n"), out);
69 fputs(_(" -R, --reset-on-fork set reset-on-fork flag\n"), out);
70 fputs(_(" -v, --verbose display status information\n"), out);
71
5407f897
QY
72 printf(USAGE_HELP_OPTIONS(22));
73
f4b07719
KZ
74 fputs(USAGE_SEPARATOR, out);
75 fputs(_("Utilization value range is [0:1024]. Use special -1 value to "
76 "reset to system's default.\n"), out);
77
5407f897
QY
78 printf(USAGE_MAN_TAIL("uclampset(1)"));
79 exit(EXIT_SUCCESS);
80}
81
82static void show_uclamp_pid_info(pid_t pid, char *cmd)
83{
84 struct sched_attr sa;
85 char *comm;
86
87 /* don't display "pid 0" as that is confusing */
88 if (!pid)
89 pid = getpid();
90
91 if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0)
92 err(EXIT_FAILURE, _("failed to get pid %d's uclamp values"), pid);
93
94 if (cmd)
95 comm = cmd;
96 else
602f734a 97 comm = pid_get_cmdname(pid);
5407f897
QY
98
99 printf(_("%s (%d) util_clamp: min: %d max: %d\n"),
993556aa 100 comm ? : "unknown", pid, sa.sched_util_min, sa.sched_util_max);
5407f897
QY
101
102 if (!cmd)
103 free(comm);
104}
105
106static unsigned int read_uclamp_sysfs(char *filename)
107{
108 unsigned int val;
109
110 if (ul_path_read_u32(NULL, &val, filename) != 0)
111 err(EXIT_FAILURE, _("cannot read %s"), filename);
112
113 return val;
114}
115
116static void write_uclamp_sysfs(char *filename, unsigned int val)
117{
118 if (ul_path_write_u64(NULL, val, filename) != 0)
119 err(EXIT_FAILURE, _("cannot write %s"), filename);
120}
121
122static void show_uclamp_system_info(void)
123{
124 unsigned int min, max;
125
126 min = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN);
127 max = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX);
128
129 printf(_("System util_clamp: min: %u max: %u\n"), min, max);
130}
131
132static void show_uclamp_info(struct uclampset *ctl)
133{
134 if (ctl->system) {
135 show_uclamp_system_info();
136 } else if (ctl->all_tasks) {
602f734a 137 DIR *sub = NULL;
5407f897 138 pid_t tid;
602f734a 139 struct path_cxt *pc = ul_new_procfs_path(ctl->pid, NULL);
5407f897 140
602f734a 141 if (!pc)
5407f897
QY
142 err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
143
602f734a 144 while (procfs_process_next_tid(pc, &sub, &tid) == 0)
5407f897
QY
145 show_uclamp_pid_info(tid, NULL);
146
602f734a 147 ul_unref_path(pc);
5407f897
QY
148 } else {
149 show_uclamp_pid_info(ctl->pid, ctl->cmd);
150 }
151}
152
153static int set_uclamp_one(struct uclampset *ctl, pid_t pid)
154{
155 struct sched_attr sa;
156
157 if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0)
158 err(EXIT_FAILURE, _("failed to get pid %d's uclamp values"), pid);
159
160 if (ctl->util_min_set)
161 sa.sched_util_min = ctl->util_min;
162 if (ctl->util_max_set)
163 sa.sched_util_max = ctl->util_max;
164
165 sa.sched_flags = SCHED_FLAG_KEEP_POLICY |
166 SCHED_FLAG_KEEP_PARAMS |
167 SCHED_FLAG_UTIL_CLAMP_MIN |
168 SCHED_FLAG_UTIL_CLAMP_MAX;
169
170 if (ctl->reset_on_fork)
171 sa.sched_flags |= SCHED_FLAG_RESET_ON_FORK;
172
173 return sched_setattr(pid, &sa, 0);
174}
175
176static void set_uclamp_pid(struct uclampset *ctl)
177{
178 if (ctl->all_tasks) {
602f734a 179 DIR *sub = NULL;
5407f897 180 pid_t tid;
602f734a 181 struct path_cxt *pc = ul_new_procfs_path(ctl->pid, NULL);
5407f897 182
602f734a 183 if (!pc)
5407f897
QY
184 err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
185
602f734a 186 while (procfs_process_next_tid(pc, &sub, &tid) == 0) {
5407f897
QY
187 if (set_uclamp_one(ctl, tid) == -1)
188 err(EXIT_FAILURE, _("failed to set tid %d's uclamp values"), tid);
602f734a
KZ
189 }
190 ul_unref_path(pc);
5407f897
QY
191
192 } else if (set_uclamp_one(ctl, ctl->pid) == -1) {
193 err(EXIT_FAILURE, _("failed to set pid %d's uclamp values"), ctl->pid);
194 }
195}
196
197static void set_uclamp_system(struct uclampset *ctl)
198{
199 if (!ctl->util_min_set)
200 ctl->util_min = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN);
201
202 if (!ctl->util_max_set)
203 ctl->util_max = read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX);
204
205 if (ctl->util_min > ctl->util_max) {
206 errno = EINVAL;
207 err(EXIT_FAILURE, _("util_min must be <= util_max"));
208 }
209
210 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN, ctl->util_min);
211 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX, ctl->util_max);
212}
213
214static void validate_util(int val)
215{
216 if (val > 1024 || val < -1) {
217 errno = EINVAL;
218 err(EXIT_FAILURE, _("%d out of range"), val);
219 }
220}
221
222int main(int argc, char **argv)
223{
224 struct uclampset _ctl = {
225 .pid = -1,
226 .util_min = NOT_SET,
227 .util_max = NOT_SET,
228 .cmd = NULL
229 };
230 struct uclampset *ctl = &_ctl;
231 int c;
232
233 static const struct option longopts[] = {
234 { "all-tasks", no_argument, NULL, 'a' },
235 { "pid", required_argument, NULL, 'p' },
236 { "system", no_argument, NULL, 's' },
237 { "reset-on-fork", no_argument, NULL, 'R' },
238 { "help", no_argument, NULL, 'h' },
239 { "verbose", no_argument, NULL, 'v' },
240 { "version", no_argument, NULL, 'V' },
241 { NULL, no_argument, NULL, 0 }
242 };
243
244 setlocale(LC_ALL, "");
245 bindtextdomain(PACKAGE, LOCALEDIR);
246 textdomain(PACKAGE);
247 close_stdout_atexit();
248
249 while((c = getopt_long(argc, argv, "+asRp:hm:M:vV", longopts, NULL)) != -1)
250 {
251 switch (c) {
252 case 'a':
253 ctl->all_tasks = 1;
254 break;
255 case 'p':
256 errno = 0;
257 ctl->pid = strtos32_or_err(optarg, _("invalid PID argument"));
5407f897
QY
258 break;
259 case 's':
260 ctl->system = 1;
261 break;
262 case 'R':
263 ctl->reset_on_fork = 1;
264 break;
265 case 'v':
266 ctl->verbose = 1;
267 break;
268 case 'm':
269 ctl->util_min = strtos32_or_err(optarg, _("invalid util_min argument"));
270 ctl->util_min_set = 1;
271 validate_util(ctl->util_min);
5407f897
QY
272 break;
273 case 'M':
274 ctl->util_max = strtos32_or_err(optarg, _("invalid util_max argument"));
275 ctl->util_max_set = 1;
276 validate_util(ctl->util_max);
5407f897
QY
277 break;
278 case 'V':
279 print_version(EXIT_SUCCESS);
280 /* fallthrough */
281 case 'h':
282 usage();
283 default:
284 errtryhelp(EXIT_FAILURE);
285 }
286 }
287
288 if (argc == 1) {
289 usage();
290 exit(EXIT_FAILURE);
291 }
292
293 /* all_tasks implies --pid */
294 if (ctl->all_tasks && ctl->pid == -1) {
295 errno = EINVAL;
296 err(EXIT_FAILURE, _("missing -p option"));
297 }
298
299 if (!ctl->util_min_set && !ctl->util_max_set) {
300 /* -p or -s must be passed */
301 if (!ctl->system && ctl->pid == -1) {
302 usage();
303 exit(EXIT_FAILURE);
304 }
305
306 show_uclamp_info(ctl);
307 return EXIT_SUCCESS;
308 }
309
310 /* ensure there's a command to execute if no -s or -p */
311 if (!ctl->system && ctl->pid == -1) {
312 if (argc <= optind) {
313 errno = EINVAL;
314 err(EXIT_FAILURE, _("no cmd to execute"));
315 }
316
317 argv += optind;
318 ctl->cmd = argv[0];
319 }
320
321 if (ctl->pid == -1)
322 ctl->pid = 0;
323
324 if (ctl->system)
325 set_uclamp_system(ctl);
326 else
327 set_uclamp_pid(ctl);
328
329 if (ctl->verbose)
330 show_uclamp_info(ctl);
331
332 if (ctl->cmd) {
333 execvp(ctl->cmd, argv);
334 errexec(ctl->cmd);
335 }
336
337 return EXIT_SUCCESS;
338}