From 8d29c78f4e0d96188b83beb7ceb509b4051e7e9b Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 4 Dec 2023 07:36:22 -0500 Subject: [PATCH] cpu_plugin: Add property "uncore_max_freq_khz" Add a property "uncore_max_delta_khz" to cpu_plugin, so that uncore maximum frequency can be set. The property is experted per die, hance add extra logic to map per cpu setting to per die setting and check for wrong config. Refer to the following link to get details uncore freqnency scaling: https://docs.kernel.org/admin-guide/pm/intel_uncore_frequency_scaling.html Signed-off-by: Stanislaw Gruszka --- tuned/plugins/plugin_cpu.py | 113 ++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/tuned/plugins/plugin_cpu.py b/tuned/plugins/plugin_cpu.py index e2bb4f22..1f7d1908 100644 --- a/tuned/plugins/plugin_cpu.py +++ b/tuned/plugins/plugin_cpu.py @@ -14,6 +14,11 @@ cpuidle_states_path = "/sys/devices/system/cpu/cpu0/cpuidle" +class Die: + def __init__(self): + self.cpus = set() + self.new_values = dict() + class CPULatencyPlugin(hotplug.Plugin): """ `cpu`:: @@ -185,6 +190,10 @@ class CPULatencyPlugin(hotplug.Plugin): pm_qos_resume_latency_us=100 ---- Allows any C-state with a resume latency less than value. + [cpu] + uncore_max_freq_khz=0 + ---- + Set the maximum uncore frequency that hardware will use. """ def __init__(self, *args, **kwargs): @@ -199,15 +208,64 @@ def __init__(self, *args, **kwargs): self._has_intel_pstate = False self._has_amd_pstate = False self._has_pm_qos_resume_latency_us = None + self._has_topology = False + self._uncore_max_delta_khz_save = None self._min_perf_pct_save = None self._max_perf_pct_save = None self._no_turbo_save = None self._governors_map = {} self._cmd = commands() + self._topology = dict() + self._topology_config_wrong = False + self._flags = None + def _cpu_get_package_die_id(self, device): + dir = '/sys/devices/system/cpu/%s/topology/' % device + if not os.path.exists(dir): + return (-1, -1) + die_id = self._str2int(self._cmd.read_file(dir + "/die_id")) + package_id = self._str2int(self._cmd.read_file(dir + "/physical_package_id")) + return (package_id, die_id) + + def _init_topology(self): + for device in self._free_devices: + package_id, die_id = self._cpu_get_package_die_id(device) + if package_id == -1: + return + + if package_id not in self._topology: + self._topology[package_id] = dict() + if die_id not in self._topology[package_id]: + self._topology[package_id][die_id] = Die() + + self._topology[package_id][die_id].cpus |= {device} + self._has_topology = True + + def _check_topology(self): + if not self._has_topology: + return + + # All assigned devices has to be part of the same die's + assigned_devices = self._assigned_devices + while len(assigned_devices) > 0: + device = assigned_devices.pop() + assigned_devices.add(device) + + package_id, die_id = self._cpu_get_package_die_id(device) + if package_id == -1: + self._topology_config_wrong = True + return + + cpus = self._topology[package_id][die_id].cpus + if len(cpus - assigned_devices) > 0: + self._topology_config_wrong = True + return + + assigned_devices -= cpus + def _init_devices(self): self._devices_supported = True self._free_devices = set() @@ -216,6 +274,7 @@ def _init_devices(self): self._free_devices.add(device.sys_name) self._assigned_devices = set() + self._init_topology() def _get_device_objects(self, devices): return [self._hardware_inventory.get_device("cpu", x) for x in devices] @@ -230,6 +289,7 @@ def _get_config_options(self): "governor" : None, "sampling_down_factor" : None, "energy_perf_bias" : None, + "uncore_max_freq_khz" : None, "min_perf_pct" : None, "max_perf_pct" : None, "no_turbo" : None, @@ -343,6 +403,8 @@ def _instance_init(self, instance): except IndexError: instance._first_device = None + self._check_topology() + def _instance_cleanup(self, instance): if instance._first_instance: if self._has_pm_qos: @@ -582,6 +644,57 @@ def _get_sampling_down_factor(self, device, ignore_missing=False): return None return self._cmd.read_file(path).strip() + @command_set("uncore_max_freq_khz", per_device=True) + def _set_uncore_max_freq_khz(self, uncore_max_freq_khz, device, sim): + if not self._has_topology or self._topology_config_wrong: + return None + + package_id, die_id = self._cpu_get_package_die_id(device) + if package_id == -1: + return None + + die = self._topology[package_id][die_id] + die.new_values[device] = uncore_max_freq_khz + + # Update the sysfs file only when cpus on die are set + if (len(die.new_values) != len(die.cpus)): + return uncore_max_freq_khz + die.new_values = dict() + + dir = '/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/' % (package_id, die_id) + file = dir + 'max_freq_khz' + if not os.path.exists(file): + log.error("Failed to set uncore_max_freq_khz because % file does not exist." % file) + return None + + initial_min_freq_khz = self._str2int(self._cmd.read_file(dir + '/initial_min_freq_khz')) + initial_max_freq_khz = self._str2int(self._cmd.read_file(dir + '/initial_max_freq_khz')) + max_freq_khz = self._str2int(uncore_max_freq_khz) + if initial_min_freq_khz is None or initial_max_freq_khz is None or max_freq_khz is None: + return None + + if max_freq_khz < initial_min_freq_khz or max_freq_khz > initial_max_freq_khz: + log.error("Failed to set uncore_max_freq_khz, value not in range (%d, %d)" % (initial_max_freq_khz, initial_max_freq_khz)) + return None + + self._cmd.write_to_file(file, max_freq_khz) + log.info('Set %s for %s' % (max_freq_khz, file)) + + return uncore_max_freq_khz + + @command_get("uncore_max_freq_khz") + def _get_uncore_max_freq_khz(self, device, ignore_missing=False): + package_id, die_id = self._cpu_get_package_die_id(device) + if package_id == -1: + return None + + file = '/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/max_freq_khz' % (package_id , die_id) + if not os.path.exists(file): + return None + + uncore_max_freq_khz = self._cmd.read_file(file) + return uncore_max_freq_khz + def _try_set_energy_perf_bias(self, cpu_id, value): (retcode, out, err_msg) = self._cmd.execute( ["x86_energy_perf_policy",