2 * uclampset.c - change utilization clamping attributes of a task or the system
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
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.
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.
17 * Copyright (C) 2020-2021 Qais Yousef
18 * Copyright (C) 2020-2021 Arm Ltd
27 #include "closestream.h"
29 #include "pathnames.h"
30 #include "procutils.h"
31 #include "sched_attr.h"
37 unsigned int util_min
;
38 unsigned int util_max
;
41 unsigned int all_tasks
:1, /* all threads of the PID */
43 util_min_set
:1, /* indicates -m option was passed */
44 util_max_set
:1, /* indicates -M option was passed */
50 static void __attribute__((__noreturn__
)) usage(void)
56 " %1$s [options] --pid <pid> | --system | <command> <arg>...\n"),
57 program_invocation_short_name
);
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
);
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
);
73 fputs(USAGE_SEPARATOR
, out
);
74 printf(USAGE_HELP_OPTIONS(22));
76 printf(USAGE_MAN_TAIL("uclampset(1)"));
80 static void show_uclamp_pid_info(pid_t pid
, char *cmd
)
85 /* don't display "pid 0" as that is confusing */
89 if (sched_getattr(pid
, &sa
, sizeof(sa
), 0) != 0)
90 err(EXIT_FAILURE
, _("failed to get pid %d's uclamp values"), pid
);
95 comm
= proc_get_command_name(pid
);
97 printf(_("%s (%d) util_clamp: min: %d max: %d\n"),
98 comm
? : "uknown", pid
, sa
.sched_util_min
, sa
.sched_util_max
);
104 static unsigned int read_uclamp_sysfs(char *filename
)
108 if (ul_path_read_u32(NULL
, &val
, filename
) != 0)
109 err(EXIT_FAILURE
, _("cannot read %s"), filename
);
114 static void write_uclamp_sysfs(char *filename
, unsigned int val
)
116 if (ul_path_write_u64(NULL
, val
, filename
) != 0)
117 err(EXIT_FAILURE
, _("cannot write %s"), filename
);
120 static void show_uclamp_system_info(void)
122 unsigned int min
, max
;
124 min
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN
);
125 max
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX
);
127 printf(_("System util_clamp: min: %u max: %u\n"), min
, max
);
130 static void show_uclamp_info(struct uclampset
*ctl
)
133 show_uclamp_system_info();
134 } else if (ctl
->all_tasks
) {
136 struct proc_tasks
*ts
= proc_open_tasks(ctl
->pid
);
139 err(EXIT_FAILURE
, _("cannot obtain the list of tasks"));
141 while (!proc_next_tid(ts
, &tid
))
142 show_uclamp_pid_info(tid
, NULL
);
144 proc_close_tasks(ts
);
146 show_uclamp_pid_info(ctl
->pid
, ctl
->cmd
);
150 static int set_uclamp_one(struct uclampset
*ctl
, pid_t pid
)
152 struct sched_attr sa
;
154 if (sched_getattr(pid
, &sa
, sizeof(sa
), 0) != 0)
155 err(EXIT_FAILURE
, _("failed to get pid %d's uclamp values"), pid
);
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
;
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
;
167 if (ctl
->reset_on_fork
)
168 sa
.sched_flags
|= SCHED_FLAG_RESET_ON_FORK
;
170 return sched_setattr(pid
, &sa
, 0);
173 static void set_uclamp_pid(struct uclampset
*ctl
)
175 if (ctl
->all_tasks
) {
177 struct proc_tasks
*ts
= proc_open_tasks(ctl
->pid
);
180 err(EXIT_FAILURE
, _("cannot obtain the list of tasks"));
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
);
186 proc_close_tasks(ts
);
188 } else if (set_uclamp_one(ctl
, ctl
->pid
) == -1) {
189 err(EXIT_FAILURE
, _("failed to set pid %d's uclamp values"), ctl
->pid
);
193 static void set_uclamp_system(struct uclampset
*ctl
)
195 if (!ctl
->util_min_set
)
196 ctl
->util_min
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN
);
198 if (!ctl
->util_max_set
)
199 ctl
->util_max
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX
);
201 if (ctl
->util_min
> ctl
->util_max
) {
203 err(EXIT_FAILURE
, _("util_min must be <= util_max"));
206 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN
, ctl
->util_min
);
207 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX
, ctl
->util_max
);
210 static void validate_util(int val
)
212 if (val
> 1024 || val
< -1) {
214 err(EXIT_FAILURE
, _("%d out of range"), val
);
218 int main(int argc
, char **argv
)
220 struct uclampset _ctl
= {
226 struct uclampset
*ctl
= &_ctl
;
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 }
240 setlocale(LC_ALL
, "");
241 bindtextdomain(PACKAGE
, LOCALEDIR
);
243 close_stdout_atexit();
245 while((c
= getopt_long(argc
, argv
, "+asRp:hm:M:vV", longopts
, NULL
)) != -1)
253 ctl
->pid
= strtos32_or_err(optarg
, _("invalid PID argument"));
260 ctl
->reset_on_fork
= 1;
266 ctl
->util_min
= strtos32_or_err(optarg
, _("invalid util_min argument"));
267 ctl
->util_min_set
= 1;
268 validate_util(ctl
->util_min
);
272 ctl
->util_max
= strtos32_or_err(optarg
, _("invalid util_max argument"));
273 ctl
->util_max_set
= 1;
274 validate_util(ctl
->util_max
);
278 print_version(EXIT_SUCCESS
);
283 errtryhelp(EXIT_FAILURE
);
292 /* all_tasks implies --pid */
293 if (ctl
->all_tasks
&& ctl
->pid
== -1) {
295 err(EXIT_FAILURE
, _("missing -p option"));
298 if (!ctl
->util_min_set
&& !ctl
->util_max_set
) {
299 /* -p or -s must be passed */
300 if (!ctl
->system
&& ctl
->pid
== -1) {
305 show_uclamp_info(ctl
);
309 /* ensure there's a command to execute if no -s or -p */
310 if (!ctl
->system
&& ctl
->pid
== -1) {
311 if (argc
<= optind
) {
313 err(EXIT_FAILURE
, _("no cmd to execute"));
324 set_uclamp_system(ctl
);
329 show_uclamp_info(ctl
);
332 execvp(ctl
->cmd
, argv
);