Skip to content

Commit

Permalink
Merge pull request #622 from zacikpa/reduce-ipi
Browse files Browse the repository at this point in the history
Avoid unnecessary inter-processor interrupts by checking that writes are necessary
  • Loading branch information
yarda authored May 30, 2024
2 parents 4e37d4a + 167c6f5 commit 6de25e2
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 55 deletions.
33 changes: 19 additions & 14 deletions tuned/plugins/plugin_cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ def __init__(self, *args, **kwargs):
self._is_x86 = False
self._is_intel = False
self._is_amd = False
self._has_energy_perf_bias = False
self._has_hwp_epp = False
self._has_energy_perf_policy_and_bias = False
self._has_intel_pstate = False
self._has_amd_pstate = False
self._has_pm_qos_resume_latency_us = None
Expand Down Expand Up @@ -259,25 +260,29 @@ def _check_arch(self):
else:
log.info("We are running on %s (non x86)" % self._arch)

self._has_hwp_epp = consts.CFG_CPU_EPP_FLAG in self._get_cpuinfo_flags()

if self._is_intel:
# Check for x86_energy_perf_policy, ignore if not available / supported
self._check_energy_perf_bias()
# When hwp_epp is not supported, we check for EPB via x86_energy_perf_policy.
# When it is supported, EPB should be accessible via sysfs.
if not self._has_hwp_epp:
self._check_energy_perf_policy_and_bias()
# Check for intel_pstate
self._check_intel_pstate()

if self._is_amd:
# Check for amd-pstate
self._check_amd_pstate()

def _check_energy_perf_bias(self):
self._has_energy_perf_bias = False
def _check_energy_perf_policy_and_bias(self):
"""Check for EPB via x86_energy_perf_policy, warn if the tool is not available or EPB unsupported."""
retcode_unsupported = 1
retcode, out = self._cmd.execute(["x86_energy_perf_policy", "-r"], no_errors = [errno.ENOENT, retcode_unsupported])
# With recent versions of the tool, a zero exit code is
# returned even if EPB is not supported. The output is empty
# in that case, however.
if retcode == 0 and out != "":
self._has_energy_perf_bias = True
self._has_energy_perf_policy_and_bias = True
elif retcode < 0:
log.warning("unable to run x86_energy_perf_policy tool, ignoring CPU energy performance bias, is the tool installed?")
else:
Expand Down Expand Up @@ -359,7 +364,7 @@ def _get_intel_pstate_attr(self, attr):

def _set_intel_pstate_attr(self, attr, val):
if val is not None:
self._cmd.write_to_file("/sys/devices/system/cpu/intel_pstate/%s" % attr, val)
self._cmd.write_to_file("/sys/devices/system/cpu/intel_pstate/%s" % attr, val, ignore_same=True)

def _getset_intel_pstate_attr(self, attr, value):
if value is None:
Expand Down Expand Up @@ -524,7 +529,7 @@ def _set_governor(self, governors, device, sim, remove):
log.info("setting governor '%s' on cpu '%s'"
% (governor, device))
self._cmd.write_to_file("/sys/devices/system/cpu/%s/cpufreq/scaling_governor"
% device, governor, no_error = [errno.ENOENT] if remove else False)
% device, governor, no_error = [errno.ENOENT] if remove else False, ignore_same=True)
break
elif not sim:
log.debug("Ignoring governor '%s' on cpu '%s', it is not supported"
Expand Down Expand Up @@ -614,14 +619,14 @@ def _set_energy_perf_bias(self, energy_perf_bias, device, sim, remove):

# It should be writen straight to sysfs energy_perf_bias file if requested on newer processors
# see rhbz#2095829
if consts.CFG_CPU_EPP_FLAG in self._get_cpuinfo_flags():
if self._has_hwp_epp:
energy_perf_bias_path = self._energy_perf_bias_path(cpu_id)
if os.path.exists(energy_perf_bias_path):
if not sim:
for val in vals:
val = val.strip()
if self._cmd.write_to_file(energy_perf_bias_path, val, \
no_error = [errno.ENOENT] if remove else False):
no_error = [errno.ENOENT] if remove else False, ignore_same=True):
log.info("energy_perf_bias successfully set to '%s' on cpu '%s'"
% (val, device))
break
Expand All @@ -634,7 +639,7 @@ def _set_energy_perf_bias(self, energy_perf_bias, device, sim, remove):
log.error("Failed to set energy_perf_bias on cpu '%s' because energy_perf_bias file does not exist."
% device)
return None
elif self._has_energy_perf_bias:
elif self._has_energy_perf_policy_and_bias:
if not sim:
for val in vals:
val = val.strip()
Expand Down Expand Up @@ -690,11 +695,11 @@ def _get_energy_perf_bias(self, device, ignore_missing=False):
log.debug("%s is not online, skipping" % device)
return None
cpu_id = device.lstrip("cpu")
if consts.CFG_CPU_EPP_FLAG in self._get_cpuinfo_flags():
if self._has_hwp_epp:
energy_perf_bias_path = self._energy_perf_bias_path(cpu_id)
if os.path.exists(energy_perf_bias_path):
energy_perf_bias = self._energy_perf_policy_to_human_v2(self._cmd.read_file(energy_perf_bias_path))
elif self._has_energy_perf_bias:
elif self._has_energy_perf_policy_and_bias:
retcode, lines = self._cmd.execute(["x86_energy_perf_policy", "-c", cpu_id, "-r"])
if retcode == 0:
for line in lines.splitlines():
Expand Down Expand Up @@ -756,7 +761,7 @@ def _set_energy_performance_preference(self, energy_performance_preference, devi
for val in vals:
if val in avail_vals:
self._cmd.write_to_file(self._pstate_preference_path(cpu_id), val, \
no_error = [errno.ENOENT] if remove else False)
no_error = [errno.ENOENT] if remove else False, ignore_same=True)
log.info("Setting energy_performance_preference value '%s' for cpu '%s'" % (val, device))
break
else:
Expand Down
45 changes: 9 additions & 36 deletions tuned/plugins/plugin_sysctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,45 +166,18 @@ def _get_sysctl_path(self, option):

def _read_sysctl(self, option):
path = self._get_sysctl_path(option)
try:
with open(path, "r") as f:
line = ""
for i, line in enumerate(f):
if i > 0:
log.error("Failed to read sysctl parameter '%s', multi-line values are unsupported"
% option)
return None
value = line.strip()
log.debug("Value of sysctl parameter '%s' is '%s'"
% (option, value))
return value
except (OSError, IOError) as e:
if e.errno == errno.ENOENT:
log.error("Failed to read sysctl parameter '%s', the parameter does not exist"
% option)
else:
log.error("Failed to read sysctl parameter '%s': %s"
% (option, str(e)))
content = self._cmd.read_file(path, err_ret=None)
if content is None:
return None
content = content.strip()
if len(content.split("\n")) > 1:
log.error("Failed to read sysctl parameter '%s', multi-line values are unsupported" % option)
return None
return content

def _write_sysctl(self, option, value, ignore_missing = False):
path = self._get_sysctl_path(option)
if os.path.basename(path) in DEPRECATED_SYSCTL_OPTIONS:
log.error("Refusing to set deprecated sysctl option %s"
% option)
return False
try:
log.debug("Setting sysctl parameter '%s' to '%s'"
% (option, value))
with open(path, "w") as f:
f.write(value)
return True
except (OSError, IOError) as e:
if e.errno == errno.ENOENT:
log_func = log.debug if ignore_missing else log.error
log_func("Failed to set sysctl parameter '%s' to '%s', the parameter does not exist"
% (option, value))
else:
log.error("Failed to set sysctl parameter '%s' to '%s': %s"
% (option, value, str(e)))
log.error("Refusing to set deprecated sysctl option %s" % option)
return False
return self._cmd.write_to_file(path, value, no_error=[errno.ENOENT] if ignore_missing else False, ignore_same=True)
2 changes: 1 addition & 1 deletion tuned/plugins/plugin_sysfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ def _read_sysfs(self, sysfs_file):
return None

def _write_sysfs(self, sysfs_file, value):
return self._cmd.write_to_file(sysfs_file, value)
return self._cmd.write_to_file(sysfs_file, value, ignore_same=True)
12 changes: 8 additions & 4 deletions tuned/utils/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ def re_lookup(self, d, s, r = None):
return list(d.values())[mo.lastindex - 1]
return None

def write_to_file(self, f, data, makedir = False, no_error = False):
def write_to_file(self, f, data, makedir = False, no_error = False, ignore_same = False):
"""Write data to a file.
Parameters:
f -- filename where to write
data -- data to write
makedir -- if True and the path doesn't exist, it will be created
no_error -- if True errors are silenced, it can be also list of ignored errnos
ignore_same -- if True and the write would not change the file, it is skipped
Return:
bool -- True on success
Expand All @@ -110,6 +111,9 @@ def write_to_file(self, f, data, makedir = False, no_error = False):
try:
if makedir:
os.makedirs(d)
if ignore_same and self.read_file(f, no_error=True).strip() == str(data):
self._debug("Skipping the write to file '%s', the content would not change" % f)
return True
fd = open(f, "w")
fd.write(str(data))
fd.close()
Expand All @@ -124,9 +128,9 @@ def write_to_file(self, f, data, makedir = False, no_error = False):
def read_file(self, f, err_ret = "", no_error = False):
old_value = err_ret
try:
f = open(f, "r")
old_value = f.read()
f.close()
fd = open(f, "r")
old_value = fd.read()
fd.close()
except (OSError,IOError) as e:
if not no_error:
self._error("Error when reading file '%s': '%s'" % (f, e))
Expand Down

0 comments on commit 6de25e2

Please sign in to comment.