(root)/
util-linux-2.39/
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		-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  	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  	printf(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  	printf(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  static 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  
     222  int 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"));
     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);
     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);
     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  }