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"
31 #include "sched_attr.h"
34 #define NOT_SET 0xdeadbeef
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)
54 fputs(USAGE_HEADER
, out
);
57 " %1$s [options] --pid <pid> | --system | <command> <arg>...\n"),
58 program_invocation_short_name
);
60 fputs(USAGE_SEPARATOR
, out
);
61 fputs(_("Show or change the utilization clamping attributes.\n"), out
);
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
);
72 fprintf(out
, USAGE_HELP_OPTIONS(22));
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
);
78 fprintf(out
, USAGE_MAN_TAIL("uclampset(1)"));
82 static void show_uclamp_pid_info(pid_t pid
, char *cmd
)
87 /* don't display "pid 0" as that is confusing */
91 if (sched_getattr(pid
, &sa
, sizeof(sa
), 0) != 0)
92 err(EXIT_FAILURE
, _("failed to get pid %d's uclamp values"), pid
);
97 comm
= pid_get_cmdname(pid
);
99 printf(_("%s (%d) util_clamp: min: %d max: %d\n"),
100 comm
? : "unknown", pid
, sa
.sched_util_min
, sa
.sched_util_max
);
106 static unsigned int read_uclamp_sysfs(char *filename
)
110 if (ul_path_read_u32(NULL
, &val
, filename
) != 0)
111 err(EXIT_FAILURE
, _("cannot read %s"), filename
);
116 static void write_uclamp_sysfs(char *filename
, unsigned int val
)
118 if (ul_path_write_u64(NULL
, val
, filename
) != 0)
119 err(EXIT_FAILURE
, _("cannot write %s"), filename
);
122 static void show_uclamp_system_info(void)
124 unsigned int min
, max
;
126 min
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN
);
127 max
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX
);
129 printf(_("System util_clamp: min: %u max: %u\n"), min
, max
);
132 static void show_uclamp_info(struct uclampset
*ctl
)
135 show_uclamp_system_info();
136 } else if (ctl
->all_tasks
) {
139 struct path_cxt
*pc
= ul_new_procfs_path(ctl
->pid
, NULL
);
142 err(EXIT_FAILURE
, _("cannot obtain the list of tasks"));
144 while (procfs_process_next_tid(pc
, &sub
, &tid
) == 0)
145 show_uclamp_pid_info(tid
, NULL
);
149 show_uclamp_pid_info(ctl
->pid
, ctl
->cmd
);
153 static int set_uclamp_one(struct uclampset
*ctl
, pid_t pid
)
155 struct sched_attr sa
;
157 if (sched_getattr(pid
, &sa
, sizeof(sa
), 0) != 0)
158 err(EXIT_FAILURE
, _("failed to get pid %d's uclamp values"), pid
);
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
;
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
;
170 if (ctl
->reset_on_fork
)
171 sa
.sched_flags
|= SCHED_FLAG_RESET_ON_FORK
;
173 return sched_setattr(pid
, &sa
, 0);
176 static void set_uclamp_pid(struct uclampset
*ctl
)
178 if (ctl
->all_tasks
) {
181 struct path_cxt
*pc
= ul_new_procfs_path(ctl
->pid
, NULL
);
184 err(EXIT_FAILURE
, _("cannot obtain the list of tasks"));
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
);
192 } else if (set_uclamp_one(ctl
, ctl
->pid
) == -1) {
193 err(EXIT_FAILURE
, _("failed to set pid %d's uclamp values"), ctl
->pid
);
197 static void set_uclamp_system(struct uclampset
*ctl
)
199 if (!ctl
->util_min_set
)
200 ctl
->util_min
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN
);
202 if (!ctl
->util_max_set
)
203 ctl
->util_max
= read_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX
);
205 if (ctl
->util_min
> ctl
->util_max
) {
207 err(EXIT_FAILURE
, _("util_min must be <= util_max"));
210 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MIN
, ctl
->util_min
);
211 write_uclamp_sysfs(_PATH_PROC_UCLAMP_MAX
, ctl
->util_max
);
214 int main(int argc
, char **argv
)
216 struct uclampset _ctl
= {
222 struct uclampset
*ctl
= &_ctl
;
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 }
236 setlocale(LC_ALL
, "");
237 bindtextdomain(PACKAGE
, LOCALEDIR
);
239 close_stdout_atexit();
241 while((c
= getopt_long(argc
, argv
, "+asRp:hm:M:vV", longopts
, NULL
)) != -1)
249 ctl
->pid
= strtos32_or_err(optarg
, _("invalid PID argument"));
255 ctl
->reset_on_fork
= 1;
261 ctl
->util_min
= strtos32_or_err(optarg
, _("invalid util_min argument"));
262 ctl
->util_min_set
= 1;
265 ctl
->util_max
= strtos32_or_err(optarg
, _("invalid util_max argument"));
266 ctl
->util_max_set
= 1;
269 print_version(EXIT_SUCCESS
);
274 errtryhelp(EXIT_FAILURE
);
283 /* all_tasks implies --pid */
284 if (ctl
->all_tasks
&& ctl
->pid
== -1) {
286 err(EXIT_FAILURE
, _("missing -p option"));
289 if (!ctl
->util_min_set
&& !ctl
->util_max_set
) {
290 /* -p or -s must be passed */
291 if (!ctl
->system
&& ctl
->pid
== -1) {
296 show_uclamp_info(ctl
);
300 /* ensure there's a command to execute if no -s or -p */
301 if (!ctl
->system
&& ctl
->pid
== -1) {
302 if (argc
<= optind
) {
304 err(EXIT_FAILURE
, _("no cmd to execute"));
315 set_uclamp_system(ctl
);
320 show_uclamp_info(ctl
);
323 execvp(ctl
->cmd
, argv
);