]> git.ipfire.org Git - thirdparty/util-linux.git/blob - schedutils/uclampset.c
Make the ways of using output stream consistent in usage()
[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 "procfs.h"
31 #include "sched_attr.h"
32 #include "strutils.h"
33
34 #define NOT_SET 0xdeadbeef
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 fputs(USAGE_HEADER, out);
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);
61 fputs(_("Show or change the utilization clamping attributes.\n"), out);
62
63 fputs(USAGE_OPTIONS, out);
64 fputs(_(" -m <value> util_min value to set\n"), out);
65 fputs(_(" -M <value> util_max value to set\n"), out);
66 fputs(_(" -a, --all-tasks operate on all the tasks (threads) for a given pid\n"), out);
67 fputs(_(" -p, --pid <pid> operate on existing given pid\n"), out);
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
72 fprintf(out, USAGE_HELP_OPTIONS(22));
73
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
78 fprintf(out, USAGE_MAN_TAIL("uclampset(1)"));
79 exit(EXIT_SUCCESS);
80 }
81
82 static 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
97 comm = pid_get_cmdname(pid);
98
99 printf(_("%s (%d) util_clamp: min: %d max: %d\n"),
100 comm ? : "unknown", pid, sa.sched_util_min, sa.sched_util_max);
101
102 if (!cmd)
103 free(comm);
104 }
105
106 static 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
116 static 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
122 static 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
132 static void show_uclamp_info(struct uclampset *ctl)
133 {
134 if (ctl->system) {
135 show_uclamp_system_info();
136 } else if (ctl->all_tasks) {
137 DIR *sub = NULL;
138 pid_t tid;
139 struct path_cxt *pc = ul_new_procfs_path(ctl->pid, NULL);
140
141 if (!pc)
142 err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
143
144 while (procfs_process_next_tid(pc, &sub, &tid) == 0)
145 show_uclamp_pid_info(tid, NULL);
146
147 ul_unref_path(pc);
148 } else {
149 show_uclamp_pid_info(ctl->pid, ctl->cmd);
150 }
151 }
152
153 static 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
176 static void set_uclamp_pid(struct uclampset *ctl)
177 {
178 if (ctl->all_tasks) {
179 DIR *sub = NULL;
180 pid_t tid;
181 struct path_cxt *pc = ul_new_procfs_path(ctl->pid, NULL);
182
183 if (!pc)
184 err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
185
186 while (procfs_process_next_tid(pc, &sub, &tid) == 0) {
187 if (set_uclamp_one(ctl, tid) == -1)
188 err(EXIT_FAILURE, _("failed to set tid %d's uclamp values"), tid);
189 }
190 ul_unref_path(pc);
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
197 static 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
214 int main(int argc, char **argv)
215 {
216 struct uclampset _ctl = {
217 .pid = -1,
218 .util_min = NOT_SET,
219 .util_max = NOT_SET,
220 .cmd = NULL
221 };
222 struct uclampset *ctl = &_ctl;
223 int c;
224
225 static const struct option longopts[] = {
226 { "all-tasks", no_argument, NULL, 'a' },
227 { "pid", required_argument, NULL, 'p' },
228 { "system", no_argument, NULL, 's' },
229 { "reset-on-fork", no_argument, NULL, 'R' },
230 { "help", no_argument, NULL, 'h' },
231 { "verbose", no_argument, NULL, 'v' },
232 { "version", no_argument, NULL, 'V' },
233 { NULL, no_argument, NULL, 0 }
234 };
235
236 setlocale(LC_ALL, "");
237 bindtextdomain(PACKAGE, LOCALEDIR);
238 textdomain(PACKAGE);
239 close_stdout_atexit();
240
241 while((c = getopt_long(argc, argv, "+asRp:hm:M:vV", longopts, NULL)) != -1)
242 {
243 switch (c) {
244 case 'a':
245 ctl->all_tasks = 1;
246 break;
247 case 'p':
248 errno = 0;
249 ctl->pid = strtos32_or_err(optarg, _("invalid PID argument"));
250 break;
251 case 's':
252 ctl->system = 1;
253 break;
254 case 'R':
255 ctl->reset_on_fork = 1;
256 break;
257 case 'v':
258 ctl->verbose = 1;
259 break;
260 case 'm':
261 ctl->util_min = strtos32_or_err(optarg, _("invalid util_min argument"));
262 ctl->util_min_set = 1;
263 break;
264 case 'M':
265 ctl->util_max = strtos32_or_err(optarg, _("invalid util_max argument"));
266 ctl->util_max_set = 1;
267 break;
268 case 'V':
269 print_version(EXIT_SUCCESS);
270 /* fallthrough */
271 case 'h':
272 usage();
273 default:
274 errtryhelp(EXIT_FAILURE);
275 }
276 }
277
278 if (argc == 1) {
279 usage();
280 exit(EXIT_FAILURE);
281 }
282
283 /* all_tasks implies --pid */
284 if (ctl->all_tasks && ctl->pid == -1) {
285 errno = EINVAL;
286 err(EXIT_FAILURE, _("missing -p option"));
287 }
288
289 if (!ctl->util_min_set && !ctl->util_max_set) {
290 /* -p or -s must be passed */
291 if (!ctl->system && ctl->pid == -1) {
292 usage();
293 exit(EXIT_FAILURE);
294 }
295
296 show_uclamp_info(ctl);
297 return EXIT_SUCCESS;
298 }
299
300 /* ensure there's a command to execute if no -s or -p */
301 if (!ctl->system && ctl->pid == -1) {
302 if (argc <= optind) {
303 errno = EINVAL;
304 err(EXIT_FAILURE, _("no cmd to execute"));
305 }
306
307 argv += optind;
308 ctl->cmd = argv[0];
309 }
310
311 if (ctl->pid == -1)
312 ctl->pid = 0;
313
314 if (ctl->system)
315 set_uclamp_system(ctl);
316 else
317 set_uclamp_pid(ctl);
318
319 if (ctl->verbose)
320 show_uclamp_info(ctl);
321
322 if (ctl->cmd) {
323 execvp(ctl->cmd, argv);
324 errexec(ctl->cmd);
325 }
326
327 return EXIT_SUCCESS;
328 }