@@ -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)
new file mode 100644
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Copyright 2024 Anubhav Shelat <ashelat@redhat.com>
+""" 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()
@@ -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()
@@ -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):
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@redhat.com> --- 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