toybox/toys/other/uclampset.c
<<
>>
Prefs
   1/* uclampset.c - The quota version of "nice".
   2 *
   3 * Copyright 2021 The Android Open Source Project
   4 *
   5 * See https://man7.org/linux/man-pages/man1/uclampset.1.html
   6
   7USE_UCLAMPSET(NEWTOY(uclampset, "p#am#<-1>1024M#<-1>1024R", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
   8
   9config UCLAMPSET
  10  bool "uclampset"
  11  default y
  12  help
  13    usage: uclampset [-m MIN] [-M MAX] {-p PID | COMMAND...}
  14
  15    Set or query process utilization limits ranging from 0 to 1024, or -1 to
  16    reset to system default. With no arguments, prints current values.
  17
  18    -m MIN      Reserve at least this much CPU utilization for task
  19    -M MAX      Limit task to at most this much CPU utilization
  20    -p PID      Apply to PID rather than new COMMAND
  21    -R  Reset child processes to default values on fork
  22    -a  Apply to all threads for the given PID
  23*/
  24
  25#define FOR_uclampset
  26#include "toys.h"
  27
  28GLOBALS(
  29  long M, m, p;
  30)
  31
  32// Added to 5.3 kernel (commit a509a7cd7974): too new to rely on headers
  33#ifndef SCHED_FLAG_RESET_ON_FORK
  34#define SCHED_FLAG_RESET_ON_FORK 0x01
  35#define SCHED_FLAG_KEEP_POLICY 0x08
  36#define SCHED_FLAG_KEEP_PARAMS 0x10
  37#define SCHED_FLAG_UTIL_CLAMP_MIN 0x20
  38#define SCHED_FLAG_UTIL_CLAMP_MAX 0x40
  39#endif
  40
  41static void do_uclampset(pid_t pid)
  42{
  43  unsigned *sa = (void *)toybuf; // sa[12] is min, sa[13] is max
  44  char *comm, buf[32];
  45
  46  if (FLAG(R)|FLAG(m)|FLAG(M)) {
  47    if (syscall(__NR_sched_setattr, pid, sa, 0))
  48      perror_exit("sched_setattr for pid %d", pid);
  49  } else {
  50    sprintf(buf, "/proc/%u/comm", pid);
  51    comm = chomp(xreadfile(buf, 0, 0));
  52    if (syscall(__NR_sched_getattr, pid, sa, *sa, 0))
  53      perror_exit("sched_getattr for pid %d", pid);
  54    printf("%s (%d) util_clamp: min: %u max: %u\n", comm, pid, sa[12], sa[13]);
  55    free(comm);
  56  }
  57}
  58
  59static int task_callback(struct dirtree *new)
  60{
  61  if (!new->parent) return DIRTREE_RECURSE;
  62  if (isdigit(*new->name)) do_uclampset(atoi(new->name));
  63
  64  return 0;
  65}
  66
  67void uclampset_main(void)
  68{
  69  unsigned *sa = (void *)toybuf;
  70  long long *flags = (void *)(sa+2);
  71  char buf[32];
  72
  73  sa[0]  = 14*4; // size
  74  sa[12] = TT.m;
  75  sa[13] = TT.M;
  76  *flags = SCHED_FLAG_KEEP_POLICY | SCHED_FLAG_KEEP_PARAMS;
  77  if (FLAG(R)) *flags |= SCHED_FLAG_RESET_ON_FORK;
  78  if (FLAG(m)) *flags |= SCHED_FLAG_UTIL_CLAMP_MIN;
  79  if (FLAG(M)) *flags |= SCHED_FLAG_UTIL_CLAMP_MAX;
  80
  81  if (!FLAG(p)) {
  82    if (toys.optc < 1) error_exit("Need -p PID or CMD [ARG...]");
  83    do_uclampset(getpid());
  84    xexec(toys.optargs);
  85  } else if (FLAG(a)) {
  86    sprintf(buf, "/proc/%lu/task", TT.p);
  87    dirtree_read(buf, task_callback);
  88  } else do_uclampset(TT.p);
  89}
  90