[PATCH v6 1/2] rteval: Added functionality to allow user to set the cstate of specified cpus when running rteval

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We would like to be able to set the idle states of CPUs while running
rteval.

This patch adds the file cpupower.py and option '--idle-set' within
rteval to use cpupower. The set idle state is applied to the cpulist
given by '--measurement-cpulist'.

cpupower.py provides the infrastructure to interface and execute the cpupower
command, and the options in rteval-cmd let the user specify the idle state
to be set and the CPUs to set it on.

Signed-off-by: Anubhav Shelat <ashelat@xxxxxxxxxx>
---
 rteval-cmd                             |  11 ++-
 rteval/cpupower.py                     | 125 +++++++++++++++++++++++++
 rteval/modules/__init__.py             |   2 +-
 rteval/modules/measurement/__init__.py |   2 +
 4 files changed, 138 insertions(+), 2 deletions(-)
 create mode 100644 rteval/cpupower.py

diff --git a/rteval-cmd b/rteval-cmd
index 19c82a0b64b3..6e7d8f5d99cb 100755
--- a/rteval-cmd
+++ b/rteval-cmd
@@ -29,6 +29,7 @@ from rteval.Log import Log
 from rteval import RtEval, rtevalConfig
 from rteval.modules.loads import LoadModules
 from rteval.modules.measurement import MeasurementModules
+from rteval import cpupower
 from rteval.version import RTEVAL_VERSION
 from rteval.systopology import SysTopology, parse_cpulist_from_config
 from rteval.modules.loads.kcompile import ModuleParameters
@@ -149,7 +150,7 @@ def parse_options(cfg, parser, cmdargs):
     parser.add_argument("--noload", dest="rteval___noload",
                         action="store_true", default=False,
                         help="only run the measurements (don't run loads)")
-
+
 
     if not cmdargs:
         cmdargs = ["--help"]
@@ -402,6 +403,10 @@ if __name__ == '__main__':
         if not os.path.isdir(rtevcfg.workdir):
             raise RuntimeError(f"work directory {rtevcfg.workdir} does not exist")
 
+        # if idle-set has been specified, enable the idle state via cpupower
+        if msrcfg.idlestate:
+            cpupower_controller = cpupower.Cpupower(msrcfg.cpulist, msrcfg.idlestate, logger=logger)
+            cpupower_controller.enable_idle_state()
 
         rteval = RtEval(config, loadmods, measuremods, logger)
         rteval.Prepare(rtevcfg.onlyload)
@@ -421,6 +426,10 @@ if __name__ == '__main__':
             ec = rteval.Measure()
             logger.log(Log.DEBUG, f"exiting with exit code: {ec}")
 
+        # restore previous idle state settings
+        if msrcfg.idlestate:
+            cpupower_controller.restore_idle_states()
+
         sys.exit(ec)
     except KeyboardInterrupt:
         sys.exit(0)
diff --git a/rteval/cpupower.py b/rteval/cpupower.py
new file mode 100644
index 000000000000..37c4d33f1df4
--- /dev/null
+++ b/rteval/cpupower.py
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Copyright 2024    Anubhav Shelat <ashelat@xxxxxxxxxx>
+""" Object to execute cpupower tool """
+
+import subprocess
+import os
+import shutil
+import sys
+from rteval.Log import Log
+from rteval.systopology import SysTopology as SysTop
+from rteval import cpulist_utils
+
+PATH = '/sys/devices/system/cpu/'
+
+class Cpupower:
+    """ class to store data for executing cpupower and restoring idle state configuration """
+    def __init__(self, cpulist, idlestate, logger=None):
+        if not self.cpupower_present():
+            print('cpupower not found')
+            sys.exit(1)
+
+        self.__idle_state = int(idlestate)
+        self.__states = os.listdir(PATH + 'cpu0/cpuidle/')
+        # self.__idle_states is a dict with cpus as keys,
+        # and another dict as the value. The value dict
+        # has idle states as keys and a boolean as the
+        # value indicating if the state is disabled.
+        self.__idle_states = {}
+        self.__name = "cpupower"
+        self.__online_cpus = SysTop().online_cpus()
+        self.__cpulist = cpulist
+        self.__logger = logger
+
+
+    def _log(self, logtype, msg):
+        """ Common log function for rteval modules """
+        if self.__logger:
+            self.__logger.log(logtype, f"[{self.__name}] {msg}")
+
+
+    def enable_idle_state(self):
+        """ Use cpupower to set the idle state """
+        self.get_idle_states()
+
+        # ensure that idle state is in range of available idle states
+        if self.__idle_state > len(self.__states) - 1 or self.__idle_state < 0:
+            print(f'Idle state {self.__idle_state} is out of range')
+            sys.exit(1)
+
+        # enable all idle states to a certain depth, and disable any deeper idle states
+        with open(os.devnull, 'wb') as buffer:
+            for state in self.__states:
+                s = state.strip("state")
+                if int(s) > self.__idle_state:
+                    self.run_cpupower(['cpupower', '-c', self.__cpulist,'idle-set', '-d', s], buffer)
+                else:
+                    self.run_cpupower(['cpupower', '-c', self.__cpulist,'idle-set', '-e', s], buffer)
+
+        self._log(Log.DEBUG, f'Idle state depth {self.__idle_state} enabled on CPUs {self.__cpulist}')
+
+
+    def run_cpupower(self, args, output_buffer=None):
+        """ execute cpupower """
+        try:
+            subprocess.run(args, check=True, stdout=output_buffer)
+        except subprocess.CalledProcessError:
+            print('cpupower failed')
+            sys.exit(1)
+
+
+    def get_idle_states(self):
+        """ Store the current idle state setting """
+        for cpu in self.__online_cpus:
+            self.__idle_states[cpu] = {}
+            for state in self.__states:
+                fp = os.path.join(PATH, 'cpu' + str(cpu) + '/cpuidle/' + state + '/disable')
+                self.__idle_states[cpu][state] = self.read_idle_state(fp)
+
+
+    def restore_idle_states(self):
+        """ restore the idle state setting """
+        for cpu, states in self.__idle_states.items():
+            for state, disabled in states.items():
+                fp = os.path.join(PATH, 'cpu' + str(cpu) + '/cpuidle/' + state + '/disable')
+                self.write_idle_state(fp, disabled)
+        self._log(Log.DEBUG, 'Idle state settings restored')
+
+
+    def read_idle_state(self, file):
+        """ read the disable value for an idle state """
+        with open(file, 'r', encoding='utf-8') as f:
+            return f.read(1)
+
+
+    def write_idle_state(self, file, state):
+        """ write the disable value for and idle state """
+        with open(file, 'w', encoding='utf-8') as f:
+            f.write(state)
+
+
+    def get_idle_info(self):
+        """ execute cpupower idle-info """
+        self.run_cpupower(['cpupower', 'idle-info'])
+
+
+    def cpupower_present(self):
+        """ check if cpupower is downloaded """
+        return shutil.which("cpupower") is not None
+
+
+if __name__ == '__main__':
+    l = Log()
+    l.SetLogVerbosity(Log.DEBUG)
+
+    online_cpus = cpulist_utils.collapse_cpulist(SysTop().online_cpus())
+    idlestate = '1'
+    info = True
+
+    cpupower = Cpupower(online_cpus, idlestate, logger=l)
+    if idlestate:
+        cpupower.enable_idle_state()
+        cpupower.restore_idle_states()
+    print()
+    cpupower.get_idle_info()
diff --git a/rteval/modules/__init__.py b/rteval/modules/__init__.py
index d7792108d5b8..acd6330788e2 100644
--- a/rteval/modules/__init__.py
+++ b/rteval/modules/__init__.py
@@ -282,7 +282,7 @@ reference from the first import"""
         grparser.add_argument(f'--{self.__modtype}-cpulist',
                             dest=f'{self.__modtype}___cpulist', action='store', default="",
                             help=f'CPU list where {self.__modtype} modules will run',
-                            metavar='LIST')
+                            metavar='CPULIST')
 
         for (modname, mod) in list(self.__modsloaded.items()):
             opts = mod.ModuleParameters()
diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py
index ecadd0885991..9314d1cb6bbc 100644
--- a/rteval/modules/measurement/__init__.py
+++ b/rteval/modules/measurement/__init__.py
@@ -38,6 +38,8 @@ class MeasurementModules(RtEvalModules):
                               default=self._cfg.GetSection("measurement").setdefault("run-on-isolcpus", "false").lower()
                                       == "true",
                               help="Include isolated CPUs in default cpulist")
+        grparser.add_argument('--idle-set', dest='measurement___idlestate', metavar='IDLESTATE',
+                        default=None, help='Idle state depth to set on cpus running measurement modules')
 
 
     def Setup(self, modparams):
-- 
2.45.2





[Index of Archives]     [RT Stable]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]

  Powered by Linux