diff --git a/rteval-Add-missing-docstrings-in-SysTopology.patch b/rteval-Add-missing-docstrings-in-SysTopology.patch new file mode 100644 index 0000000..bc8c215 --- /dev/null +++ b/rteval-Add-missing-docstrings-in-SysTopology.patch @@ -0,0 +1,32 @@ +From 33cd1f747596541dc947e14f3dd25ff7960b7443 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Wed, 26 Jul 2023 10:22:12 +0200 +Subject: [PATCH] rteval: Add missing docstrings in SysTopology + +Add docstrings for isolated_cpus_str and default_cpus_str. + +Signed-off-by: Tomas Glozar +--- + rteval/systopology.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/rteval/systopology.py b/rteval/systopology.py +index 19443f9..a991e70 100644 +--- a/rteval/systopology.py ++++ b/rteval/systopology.py +@@ -407,10 +407,12 @@ class SysTopology: + return cpulist + + def isolated_cpus_str(self): ++ """ return a list of strings of numbers of all isolated cpus """ + cpulist = [str(cpu) for cpu in self.isolated_cpus()] + return cpulist + + def default_cpus_str(self): ++ """ return a list of strings of numbers of all default schedulable cpus """ + cpulist = [str(cpu) for cpu in self.default_cpus()] + return cpulist + +-- +2.41.0 + diff --git a/rteval-Detect-isolcpus-in-systopology.patch b/rteval-Detect-isolcpus-in-systopology.patch new file mode 100644 index 0000000..83470db --- /dev/null +++ b/rteval-Detect-isolcpus-in-systopology.patch @@ -0,0 +1,98 @@ +From 3206001ad1b42cf6fb97c7848f438d2bdbe843bc Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Fri, 30 Jun 2023 11:19:02 +0200 +Subject: [PATCH] rteval: Detect isolcpus in systopology + +Works similarly to online_cpus: +- add CpuList.isolated_cpulist to filter isolated CPUs +- add SysTopology.isolated_cpus to get list of isolated CPUs +- add CpuList.nonisolated_cpulist to do the opposite filter +- add SysTopology.default_cpus to get CPUs that are used for scheduling + by default, i.e. online and non-isolated CPUs + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval/systopology.py | 47 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/rteval/systopology.py b/rteval/systopology.py +index c8f85c5..19443f9 100644 +--- a/rteval/systopology.py ++++ b/rteval/systopology.py +@@ -127,6 +127,11 @@ class CpuList: + return True + return False + ++ @staticmethod ++ def isolated_file_exists(): ++ """ Check whether machine / kernel is configured with isolated file """ ++ return os.path.exists(os.path.join(CpuList.cpupath, "isolated")) ++ + @staticmethod + def longest_sequence(cpulist): + """ return index of last element of a sequence that steps by one """ +@@ -214,6 +219,24 @@ class CpuList: + newlist.append(cpu) + return newlist + ++ @staticmethod ++ def isolated_cpulist(cpulist): ++ """Given a cpulist, return a cpulist of isolated CPUs""" ++ if not CpuList.isolated_file_exists(): ++ return cpulist ++ isolated_cpulist = sysread(CpuList.cpupath, "isolated") ++ isolated_cpulist = CpuList.expand_cpulist(isolated_cpulist) ++ return list(set(isolated_cpulist) & set(cpulist)) ++ ++ @staticmethod ++ def nonisolated_cpulist(cpulist): ++ """Given a cpulist, return a cpulist of non-isolated CPUs""" ++ if not CpuList.isolated_file_exists(): ++ return cpulist ++ isolated_cpulist = sysread(CpuList.cpupath, "isolated") ++ isolated_cpulist = CpuList.expand_cpulist(isolated_cpulist) ++ return list(set(cpulist).difference(set(isolated_cpulist))) ++ + # + # class to abstract access to NUMA nodes in /sys filesystem + # +@@ -362,11 +385,35 @@ class SysTopology: + cpulist.sort() + return cpulist + ++ def isolated_cpus(self): ++ """ return a list of integers of all isolated cpus """ ++ cpulist = [] ++ for n in self.nodes: ++ cpulist += CpuList.isolated_cpulist(self.getcpus(n)) ++ cpulist.sort() ++ return cpulist ++ ++ def default_cpus(self): ++ """ return a list of integers of all default schedulable cpus, i.e. online non-isolated cpus """ ++ cpulist = [] ++ for n in self.nodes: ++ cpulist += CpuList.nonisolated_cpulist(self.getcpus(n)) ++ cpulist.sort() ++ return cpulist ++ + def online_cpus_str(self): + """ return a list of strings of numbers of all online cpus """ + cpulist = [str(cpu) for cpu in self.online_cpus()] + return cpulist + ++ def isolated_cpus_str(self): ++ cpulist = [str(cpu) for cpu in self.isolated_cpus()] ++ return cpulist ++ ++ def default_cpus_str(self): ++ cpulist = [str(cpu) for cpu in self.default_cpus()] ++ return cpulist ++ + def invert_cpulist(self, cpulist): + """ return a list of online cpus not in cpulist """ + return [c for c in self.online_cpus() if c not in cpulist] +-- +2.41.0 + diff --git a/rteval-Exclude-isolcpus-from-kcompile-by-default.patch b/rteval-Exclude-isolcpus-from-kcompile-by-default.patch new file mode 100644 index 0000000..6b39ac9 --- /dev/null +++ b/rteval-Exclude-isolcpus-from-kcompile-by-default.patch @@ -0,0 +1,52 @@ +From 2db552584b292d8a0d030c5d0e3a0a5cdc8af7cc Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Fri, 30 Jun 2023 11:19:04 +0200 +Subject: [PATCH] rteval: Exclude isolcpus from kcompile by default + +Allows correct calculation of make job count that does not include +isolated CPUs, on which the loads won't be scheduled. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval/modules/loads/kcompile.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/rteval/modules/loads/kcompile.py b/rteval/modules/loads/kcompile.py +index 9244246..316791e 100644 +--- a/rteval/modules/loads/kcompile.py ++++ b/rteval/modules/loads/kcompile.py +@@ -37,6 +37,7 @@ from rteval.systopology import CpuList, SysTopology + + expand_cpulist = CpuList.expand_cpulist + compress_cpulist = CpuList.compress_cpulist ++nonisolated_cpulist = CpuList.nonisolated_cpulist + + DEFAULT_KERNEL_PREFIX = "linux-6.1" + +@@ -55,17 +56,20 @@ class KBuildJob: + if not os.path.isdir(self.objdir): + os.mkdir(self.objdir) + ++ # Exclude isolated CPUs if cpulist not set ++ cpus_available = len(nonisolated_cpulist(self.node.cpus.cpulist)) ++ + if os.path.exists('/usr/bin/numactl') and not cpulist: + # Use numactl + self.binder = f'numactl --cpunodebind {int(self.node)}' +- self.jobs = self.calc_jobs_per_cpu() * len(self.node) ++ self.jobs = self.calc_jobs_per_cpu() * cpus_available + elif cpulist: + # Use taskset + self.jobs = self.calc_jobs_per_cpu() * len(cpulist) + self.binder = f'taskset -c {compress_cpulist(cpulist)}' + else: + # Without numactl calculate number of jobs from the node +- self.jobs = self.calc_jobs_per_cpu() * len(self.node) ++ self.jobs = self.calc_jobs_per_cpu() * cpus_available + + self.runcmd = f"make O={self.objdir} -C {self.kdir} -j{self.jobs}" + self.cleancmd = f"make O={self.objdir} -C {self.kdir} clean allmodconfig" +-- +2.41.0 + diff --git a/rteval-Exclude-isolcpus-from-loads-report.patch b/rteval-Exclude-isolcpus-from-loads-report.patch new file mode 100644 index 0000000..9704f33 --- /dev/null +++ b/rteval-Exclude-isolcpus-from-loads-report.patch @@ -0,0 +1,43 @@ +From 1afab85c9aa0795af2ce96e1c6a2ccbe3ccec4b5 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Fri, 30 Jun 2023 11:19:07 +0200 +Subject: [PATCH] rteval: Exclude isolcpus from loads report + +Use SysTopology.default_cpus() to report cores on which measurements and +loads and run by default to reflect the default behavior (no isolcpus). + +Signed-off-by: Tomas Glozar +--- + rteval/modules/loads/__init__.py | 2 +- + rteval/modules/measurement/__init__.py | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/rteval/modules/loads/__init__.py b/rteval/modules/loads/__init__.py +index 761cc8c..74aad58 100644 +--- a/rteval/modules/loads/__init__.py ++++ b/rteval/modules/loads/__init__.py +@@ -138,7 +138,7 @@ class LoadModules(RtEvalModules): + # Convert str to list and remove offline cpus + cpulist = CpuList(cpulist).cpulist + else: +- cpulist = SysTop().online_cpus() ++ cpulist = SysTop().default_cpus() + rep_n.newProp("loadcpus", collapse_cpulist(cpulist)) + + return rep_n +diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py +index d99873e..0e395be 100644 +--- a/rteval/modules/measurement/__init__.py ++++ b/rteval/modules/measurement/__init__.py +@@ -194,7 +194,7 @@ measurement profiles, based on their characteristics""" + # Convert str to list and remove offline cpus + cpulist = CpuList(cpulist).cpulist + else: +- cpulist = SysTop().online_cpus() ++ cpulist = SysTop().default_cpus() + rep_n.newProp("measurecpus", collapse_cpulist(cpulist)) + + for mp in self.__measureprofiles: +-- +2.41.0 + diff --git a/rteval-Exclude-isolcpus-from-stressng-by-default.patch b/rteval-Exclude-isolcpus-from-stressng-by-default.patch new file mode 100644 index 0000000..11118f0 --- /dev/null +++ b/rteval-Exclude-isolcpus-from-stressng-by-default.patch @@ -0,0 +1,41 @@ +From 4ee01e7b82e348d57621a87b9862ebdfd81aefe8 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Fri, 30 Jun 2023 11:19:05 +0200 +Subject: [PATCH] rteval: Exclude isolcpus from stressng by default + +Note: this has little effect now, because the cpus variables is only +used for removing empty nodes unless a cpulist is specified by the user. +However, this can change in the future. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval/modules/loads/stressng.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/rteval/modules/loads/stressng.py b/rteval/modules/loads/stressng.py +index 85cb473..800fdec 100644 +--- a/rteval/modules/loads/stressng.py ++++ b/rteval/modules/loads/stressng.py +@@ -9,6 +9,7 @@ from rteval.Log import Log + from rteval.systopology import CpuList, SysTopology + + expand_cpulist = CpuList.expand_cpulist ++nonisolated_cpulist = CpuList.nonisolated_cpulist + + class Stressng(CommandLineLoad): + " This class creates a load module that runs stress-ng " +@@ -69,6 +70,10 @@ class Stressng(CommandLineLoad): + # if a cpulist was specified, only allow cpus in that list on the node + if self.cpulist: + cpus[n] = [c for c in cpus[n] if c in expand_cpulist(self.cpulist)] ++ # if a cpulist was not specified, exclude isolated cpus ++ else: ++ cpus[n] = CpuList.nonisolated_cpulist(cpus[n]) ++ + + # remove nodes with no cpus available for running + for node, cpu in cpus.items(): +-- +2.41.0 + diff --git a/rteval-Fix-CPU-count-calculation-for-hackbench.patch b/rteval-Fix-CPU-count-calculation-for-hackbench.patch new file mode 100644 index 0000000..91123f6 --- /dev/null +++ b/rteval-Fix-CPU-count-calculation-for-hackbench.patch @@ -0,0 +1,42 @@ +From 9c096f32cb35452b3475198fcab8ad4356151e86 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Fri, 30 Jun 2023 11:19:06 +0200 +Subject: [PATCH] rteval: Fix CPU count calculation for hackbench + +Use count from cpulist when specified and all CPUs on node excluding +offline ones and isolated ones if not specified. + +Signed-off-by: Tomas Glozar +--- + rteval/modules/loads/hackbench.py | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/rteval/modules/loads/hackbench.py b/rteval/modules/loads/hackbench.py +index 14e60d1..f5a547e 100644 +--- a/rteval/modules/loads/hackbench.py ++++ b/rteval/modules/loads/hackbench.py +@@ -38,6 +38,7 @@ from rteval.Log import Log + from rteval.systopology import CpuList, SysTopology + + expand_cpulist = CpuList.expand_cpulist ++isolated_cpulist = CpuList.isolated_cpulist + + class Hackbench(CommandLineLoad): + def __init__(self, config, logger): +@@ -77,9 +78,12 @@ class Hackbench(CommandLineLoad): + # if a cpulist was specified, only allow cpus in that list on the node + if self.cpulist: + self.cpus[n] = [c for c in self.cpus[n] if c in expand_cpulist(self.cpulist)] ++ # if a cpulist was not specified, exclude isolated cpus ++ else: ++ self.cpus[n] = CpuList.nonisolated_cpulist(self.cpus[n]) + + # track largest number of cpus used on a node +- node_biggest = len(sysTop.getcpus(int(n))) ++ node_biggest = len(self.cpus[n]) + if node_biggest > biggest: + biggest = node_biggest + +-- +2.41.0 + diff --git a/rteval-Report-isolated-CPUs.patch b/rteval-Report-isolated-CPUs.patch new file mode 100644 index 0000000..1496fb3 --- /dev/null +++ b/rteval-Report-isolated-CPUs.patch @@ -0,0 +1,83 @@ +From cb7ba0c0db7631021341b7fa692c286fd9d33cd1 Mon Sep 17 00:00:00 2001 +From: Tomas Glozar +Date: Fri, 30 Jun 2023 11:19:03 +0200 +Subject: [PATCH] rteval: Report isolated CPUs + +Add a flag for whether a CPU is isolated in CPUtopology and display +the number of isolated CPUs in text report. + +Signed-off-by: Tomas Glozar +Signed-off-by: John Kacur +--- + rteval/rteval_text.xsl | 4 ++++ + rteval/sysinfo/cputopology.py | 11 +++++++++++ + 2 files changed, 15 insertions(+) + +diff --git a/rteval/rteval_text.xsl b/rteval/rteval_text.xsl +index 7ecfac6..0ef649b 100644 +--- a/rteval/rteval_text.xsl ++++ b/rteval/rteval_text.xsl +@@ -59,6 +59,10 @@ + + (online: + ++ ++ , isolated: ++ ++ + ) + + +diff --git a/rteval/sysinfo/cputopology.py b/rteval/sysinfo/cputopology.py +index 2bb6323..f60b059 100644 +--- a/rteval/sysinfo/cputopology.py ++++ b/rteval/sysinfo/cputopology.py +@@ -25,6 +25,7 @@ + + import os + import libxml2 ++from rteval.systopology import SysTopology + + class CPUtopology: + "Retrieves an overview over the installed CPU cores and the system topology" +@@ -34,6 +35,7 @@ class CPUtopology: + self.__cputop_n = None + self.__cpu_cores = 0 + self.__online_cores = 0 ++ self.__isolated_cores = 0 + self.__cpu_sockets = 0 + + def __read(self, dirname, fname): +@@ -51,6 +53,10 @@ class CPUtopology: + + self.__cputop_n = libxml2.newNode('CPUtopology') + ++ # Get list of isolated CPUs from SysTopology ++ systopology = SysTopology() ++ isolated_cpus = {'cpu' + n for n in systopology.isolated_cpus_str()} ++ + cpusockets = [] + for dirname in os.listdir(self.sysdir): + # Only parse directories which starts with 'cpu' +@@ -82,6 +88,10 @@ class CPUtopology: + 'physical_package_id') + cpu_n.newProp('physical_package_id', str(phys_pkg_id)) + cpusockets.append(phys_pkg_id) ++ is_isolated = dirname in isolated_cpus ++ if is_isolated: ++ self.__isolated_cores += 1 ++ cpu_n.newProp('isolated', str(int(dirname in isolated_cpus))) + break + + # Count unique CPU sockets +@@ -97,6 +107,7 @@ class CPUtopology: + # Summarise the core counts + self.__cputop_n.newProp('num_cpu_cores', str(self.__cpu_cores)) + self.__cputop_n.newProp('num_cpu_cores_online', str(self.__online_cores)) ++ self.__cputop_n.newProp('num_cpu_cores_isolated', str(self.__isolated_cores)) + self.__cputop_n.newProp('num_cpu_sockets', str(self.__cpu_sockets)) + + return self.__cputop_n +-- +2.41.0 + diff --git a/rteval.spec b/rteval.spec index 46cd59d..8134700 100644 --- a/rteval.spec +++ b/rteval.spec @@ -1,6 +1,6 @@ Name: rteval Version: 3.5 -Release: 10%{?dist} +Release: 11%{?dist} Summary: Utility to evaluate system suitability for RT Linux Group: Development/Tools @@ -53,6 +53,13 @@ Patch19: rteval-Use-f-strings-in-tools.py.patch Patch20: rteval-Use-f-strings-in-cputopology.patch Patch21: rteval-Changed-files-to-use-argparse.patch Patch22: rteval-server-edited-files-to-use-optparse.patch +Patch23: rteval-Detect-isolcpus-in-systopology.patch +Patch24: rteval-Report-isolated-CPUs.patch +Patch25: rteval-Exclude-isolcpus-from-kcompile-by-default.patch +Patch26: rteval-Exclude-isolcpus-from-stressng-by-default.patch +Patch27: rteval-Fix-CPU-count-calculation-for-hackbench.patch +Patch28: rteval-Exclude-isolcpus-from-loads-report.patch +Patch29: rteval-Add-missing-docstrings-in-SysTopology.patch %description @@ -88,6 +95,13 @@ to the screen. %patch20 -p1 %patch21 -p1 %patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 %build %{__python3} setup.py build @@ -109,6 +123,11 @@ to the screen. %{_bindir}/rteval %changelog +* Wed Oct 04 2023 Tomas Glozar - 3.5-11 +- Added patch set that enables rteval to do load calculations and reporting +correctly on systems with isolated CPUs +jiraProject== RHEL-8681 + * Wed Oct 04 2023 John Kacur - 3.5.10 - Added patches to use argparse instead of deprecated optparse jiraProject == RHEL-9026