From e2c4c6e2d5b25de5a8ddc954a7b43b2b336c33d8 Mon Sep 17 00:00:00 2001 From: Anubhav Shelat Date: Tue, 6 Aug 2024 15:45:40 -0400 Subject: [PATCH] Add functionality to allow user to execute cpupower tool to set idle state depth while running rteval. Disable latency trick by using --default-system option with cyclictest when setting idle state depth. Resolves: RHEL-37645 Signed-off-by: Anubhav Shelat --- ...ctionality-to-allow-user-to-set-the-.patch | 221 ++++++++++++++++++ ...ctest-using-default-system-when-sett.patch | 71 ++++++ rteval.spec | 11 +- 3 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 rteval-Added-functionality-to-allow-user-to-set-the-.patch create mode 100644 rteval-run-cyclictest-using-default-system-when-sett.patch diff --git a/rteval-Added-functionality-to-allow-user-to-set-the-.patch b/rteval-Added-functionality-to-allow-user-to-set-the-.patch new file mode 100644 index 0000000..416d5ee --- /dev/null +++ b/rteval-Added-functionality-to-allow-user-to-set-the-.patch @@ -0,0 +1,221 @@ +From b3c96935c82b7410422ca2973398ac9d0272c844 Mon Sep 17 00:00:00 2001 +From: Anubhav Shelat +Date: Fri, 2 Aug 2024 11:46:35 -0400 +Subject: [PATCH 1/2] rteval: Added functionality to allow user to set the + cstate of specified cpus when running rteval + +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 +Signed-off-by: John Kacur +--- + rteval-cmd | 9 ++ + rteval/cpupower.py | 125 +++++++++++++++++++++++++ + rteval/modules/__init__.py | 2 +- + rteval/modules/measurement/__init__.py | 2 + + 4 files changed, 137 insertions(+), 1 deletion(-) + create mode 100644 rteval/cpupower.py + +diff --git a/rteval-cmd b/rteval-cmd +index 19c82a0b64b3..f440a8a22622 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 +@@ -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 ++""" 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 + diff --git a/rteval-run-cyclictest-using-default-system-when-sett.patch b/rteval-run-cyclictest-using-default-system-when-sett.patch new file mode 100644 index 0000000..d72d1e4 --- /dev/null +++ b/rteval-run-cyclictest-using-default-system-when-sett.patch @@ -0,0 +1,71 @@ +From 034fb231ff06aa4615b7531a04e4c3e0ce4aa662 Mon Sep 17 00:00:00 2001 +From: Anubhav Shelat +Date: Fri, 2 Aug 2024 11:46:36 -0400 +Subject: [PATCH 2/2] rteval: run cyclictest using '--default-system' when + setting idle states + +When running cyclictest in rteval, cyclictest automatically disables +idle states. This means whenever the user sets the idle state of a cpu +list using '--idle-set' it is overridden by cyclictest. + +To fix this, the variable 'usingCpupower' is appended to the parameter +dictionary that's passed to the Cyclictest measurement object which executes +cyclictest in rteval. + +If '--idle-set' is specified when running rteval, +'usingCpupower' is set to true and the '--default-system' option is +appended to the cyclictest command, which will prevent cyclictest from +disabling cstates. + +Signed-off-by: Anubhav Shelat +Signed-off-by: John Kacur +--- + rteval-cmd | 4 ++++ + rteval/__init__.py | 1 + + rteval/modules/measurement/cyclictest.py | 3 +++ + 3 files changed, 8 insertions(+) + +diff --git a/rteval-cmd b/rteval-cmd +index f440a8a22622..4e13d312a24a 100755 +--- a/rteval-cmd ++++ b/rteval-cmd +@@ -266,6 +266,10 @@ if __name__ == '__main__': + | (rtevcfg.debugging and Log.DEBUG) + logger.SetLogVerbosity(loglev) + ++ # check if cpupower is being used ++ if sys.argv.count('--idle-set') > 0: ++ rtevcfg.update({'usingCpupower': True}) ++ + # Load modules + loadmods = LoadModules(config, logger=logger) + measuremods = MeasurementModules(config, logger=logger) +diff --git a/rteval/__init__.py b/rteval/__init__.py +index 4d3e0c23e5ab..8ded374d287e 100644 +--- a/rteval/__init__.py ++++ b/rteval/__init__.py +@@ -119,6 +119,7 @@ class RtEval(rtevalReport): + 'memsize':self._sysinfo.mem_get_size(), + 'numanodes':self._sysinfo.mem_get_numa_nodes(), + 'duration': float(self.__rtevcfg.duration), ++ 'usingCpupower': self.__rtevcfg.usingCpupower + } + + if self._loadmods: +diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py +index d919058e927f..2e8f6f1870ed 100644 +--- a/rteval/modules/measurement/cyclictest.py ++++ b/rteval/modules/measurement/cyclictest.py +@@ -251,6 +251,9 @@ class Cyclictest(rtevalModulePrototype): + self.__cmd.append(f'-t{self.__numcores}') + self.__cmd.append(f'-a{self.__cpulist}') + ++ if (self.__cfg.usingCpupower): ++ self.__cmd.append('--default-system') ++ + if 'threads' in self.__cfg and self.__cfg.threads: + self.__cmd.append(f"-t{int(self.__cfg.threads)}") + +-- +2.45.2 + diff --git a/rteval.spec b/rteval.spec index f2388cf..14febb8 100644 --- a/rteval.spec +++ b/rteval.spec @@ -1,6 +1,6 @@ Name: rteval Version: 3.8 -Release: 5%{?dist} +Release: 6%{?dist} Summary: Utility to evaluate system suitability for RT Linux Group: Development/Tools @@ -46,6 +46,8 @@ Patch10: rteval-Enforce-only-one-latency-measurement-module-a.patch Patch11: rteval-Add-noload-option.patch Patch12: rteval-Fix-default-measurement-config.patch Patch13: rteval-measurement-Change-latency-flag-to-latency_te.patch +Patch14: rteval-Added-functionality-to-allow-user-to-set-the-.patch +Patch15: rteval-run-cyclictest-using-default-system-when-sett.patch %description The rteval script is a utility for measuring various aspects of @@ -79,6 +81,13 @@ to the screen. %{_bindir}/rteval %changelog +* Tue Aug 6 2024 Anubhav Shelat - 3.8-6 +- Add functionality to allow user to execute cpupower tool to +set idle state depth while running rteval. +- Disable latency trick by using --default-system option with +cyclictest when setting idle state depth. +Resolves: RHEL-37645 + * Mon Jul 29 2024 Crystal Wood - 3.8-5 - Prevent using cyclictest and timerlat and the same time - Add a --noload option