222 lines
8.4 KiB
Diff
222 lines
8.4 KiB
Diff
|
From b3c96935c82b7410422ca2973398ac9d0272c844 Mon Sep 17 00:00:00 2001
|
||
|
From: Anubhav Shelat <ashelat@redhat.com>
|
||
|
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 <ashelat@redhat.com>
|
||
|
Signed-off-by: John Kacur <jkacur@redhat.com>
|
||
|
---
|
||
|
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 <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()
|
||
|
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
|
||
|
|