1
0
Fork 0

Merge branch 'master' into rhbz2118786_pm_qos_resume_latency_us_option

This commit is contained in:
Honza Žerdík 2023-02-08 12:26:41 +01:00 committed by GitHub
commit 613c7afd57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 506 additions and 68 deletions

41
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,41 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "48 13 * * 1"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View file

@ -23,11 +23,12 @@ else
GIT_PSUFFIX = .$(GIT_SUFFIX)
RPM_VERSION = $(NAME)-$(VERSION)-1$(GIT_PSUFFIX)
endif
PKG_CONFIG = pkg-config
UNITDIR_FALLBACK = /usr/lib/systemd/system
UNITDIR_DETECT = $(shell pkg-config systemd --variable systemdsystemunitdir || rpm --eval '%{_unitdir}' 2>/dev/null || echo $(UNITDIR_FALLBACK))
UNITDIR_DETECT = $(shell $(PKG_CONFIG) systemd --variable systemdsystemunitdir || rpm --eval '%{_unitdir}' 2>/dev/null || echo $(UNITDIR_FALLBACK))
UNITDIR = $(UNITDIR_DETECT:%{_unitdir}=$(UNITDIR_FALLBACK))
TMPFILESDIR_FALLBACK = /usr/lib/tmpfiles.d
TMPFILESDIR_DETECT = $(shell pkg-config systemd --variable tmpfilesdir || rpm --eval '%{_tmpfilesdir}' 2>/dev/null || echo $(TMPFILESDIR_FALLBACK))
TMPFILESDIR_DETECT = $(shell $(PKG_CONFIG) systemd --variable tmpfilesdir || rpm --eval '%{_tmpfilesdir}' 2>/dev/null || echo $(TMPFILESDIR_FALLBACK))
TMPFILESDIR = $(TMPFILESDIR_DETECT:%{_tmpfilesdir}=$(TMPFILESDIR_FALLBACK))
VERSIONED_NAME = $(NAME)-$(VERSION)$(PRERELEASE)$(GIT_PSUFFIX)
@ -40,7 +41,7 @@ ifeq ($(PYTHON),python2)
PYLINT = pylint-2
endif
SHEBANG_REWRITE_REGEX= '1s|^\#!/usr/bin/\<python3\>|\#!$(PYTHON)|'
PYTHON_SITELIB = $(shell $(PYTHON) -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib());')
PYTHON_SITELIB = $(shell $(PYTHON) -c 'from sysconfig import get_path; print(get_path("purelib"));')
ifeq ($(PYTHON_SITELIB),)
$(error Failed to determine python library directory)
endif
@ -176,6 +177,8 @@ install: install-dirs
$(DESTDIR)$(SYSCONFDIR)/tuned/realtime-virtual-host-variables.conf
mv $(DESTDIR)$(TUNED_PROFILESDIR)/cpu-partitioning/cpu-partitioning-variables.conf \
$(DESTDIR)$(SYSCONFDIR)/tuned/cpu-partitioning-variables.conf
mv $(DESTDIR)$(TUNED_PROFILESDIR)/cpu-partitioning-powersave/cpu-partitioning-powersave-variables.conf \
$(DESTDIR)$(SYSCONFDIR)/tuned/cpu-partitioning-powersave-variables.conf
install -pm 0644 recommend.conf $(DESTDIR)$(TUNED_RECOMMEND_DIR)/50-tuned.conf
# bash completion

View file

@ -4,7 +4,7 @@
<policyconfig>
<vendor>TuneD</vendor>
<vendor_url>https://fedorahosted.org/tuned/</vendor_url>
<vendor_url>https://tuned-project.org/</vendor_url>
<icon_name>tuned</icon_name>
<action id="com.redhat.tuned.active_profile">
@ -216,4 +216,15 @@
<allow_active>yes</allow_active>
</defaults>
</action>
<action id="com.redhat.tuned.instance_acquire_devices">
<description>Acquire devices from other instances and assign them to the instance</description>
<message>Authentication is required to get hints for parameters of TuneD plugin</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
</action>
</policyconfig>

View file

@ -0,0 +1,67 @@
.\"/*
.\" * All rights reserved
.\" * Copyright (C) 2022 Red Hat, Inc.
.\" * Authors: Christophe Fontaine
.\" *
.\" * This program is free software; you can redistribute it and/or
.\" * modify it under the terms of the GNU General Public License
.\" * as published by the Free Software Foundation; either version 2
.\" * of the License, or (at your option) any later version.
.\" *
.\" * This program is distributed in the hope that it will be useful,
.\" * but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" * GNU General Public License for more details.
.\" *
.\" * You should have received a copy of the GNU General Public License
.\" * along with this program; if not, write to the Free Software
.\" * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
.\" */
.\"
.TH TUNED_PROFILES_CPU_PARTITIONING "7" "22 Nov 2022" "TuneD"
.SH NAME
tuned\-profiles\-cpu\-partitioning\-powersave - Partition CPUs into isolated and housekeeping with C-States enabled
.SH DESCRIPTION
The cpu\-partitioning\-powersave profile is similar to cpu\-partitioning profile,
but gives more flexibility on the C-States configuration.
.SH CONFIGURATION
The cpu-partitioning-powersave profile is configured by editing the
.I /etc/tuned/cpu-partitioning-powersave-variables.conf
file. There are three configuration options:
.TP
.B isolated_cores=<CPU\-LIST>
List of CPUs to isolate. This option is mandatory. Any CPUs not in this list
is automatically considered a housekeeping CPU.
.TP
.B no_balance_cores=<CPU\-LIST>
List of CPUs not be considered by the kernel when doing system wide
process load\-balancing. Usually, this list should be the same as
isolated_cores=. This option is optional.
.TP
.B max_power_state=<MAX_CSTATE>
Maximum c-state the cores are allowed to enter. Can be expressed as it's name (C1E)
or minimum wake-up latency, in micro-seconds.
This parameter is provided as-is to `force_latency`.
Default is set to "cstate.name:C1|10" to behave as cpu\-partitioning profile.
.SH IMPORTANT NOTES
.IP *
Same recommendations as tuned\-profiles\-cpu\-partitioning (7) apply.
.SH "FILES"
.nf
.I /etc/tuned/cpu\-partitioning\-variables.conf
.I /etc/tuned/tuned\-main.conf
.SH "SEE ALSO"
.BR tuned (8)
.BR tuned\-adm (8)
.BR tuned\-profiles\-cpu\-partitioning (7)
.SH AUTHOR
.nf
Christophe Fontaine <cfontain@redhat.com>

View file

@ -18,7 +18,7 @@
.\" * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
.\" */
.\"
.TH TUNED_PROFILES_OPENSHIFT "8" "02 Aug 2021" "Fedora Power Management SIG" "TuneD"
.TH TUNED_PROFILES_OPENSHIFT "7" "02 Aug 2021" "Fedora Power Management SIG" "TuneD"
.SH NAME
tuned\-profiles\-openshift - description of profiles provided for the OpenShift platform

21
profiles/aws/tuned.conf Normal file
View file

@ -0,0 +1,21 @@
#
# tuned configuration
#
[main]
summary=Optimize for aws ec2 instances
include=throughput-performance
# Marvell ThunderX
[bootloader.thunderx]
# rhbz:1836058
type=bootloader
uname_regex=aarch64
cmdline=+iommu.strict=0
[bootloader]
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html#timeout-nvme-ebs-volumes
# set nvme_core.io_timeout to 4294967295
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-ena.html
# set net.ifnames to 0
cmdline=+net.ifnames=0 nvme_core.io_timeout=4294967295

View file

@ -0,0 +1,14 @@
# Examples:
# isolated_cores=2,4-7
# isolated_cores=2-23
#
# Reserve 1 core per socket for housekeeping, isolate the rest.
isolated_cores=${f:calc_isolated_cores:1}
# To disable the kernel load balancing in certain isolated CPUs:
# no_balance_cores=5-10
# Specifies the maximum powerstate for idling cores.
# given to force_latency tuned parameter. To have the same behavior
# as cpu-partitioning profile, set to "cstate.name:C1|10"
max_power_state=cstate.name:C1|10

View file

@ -0,0 +1,31 @@
# tuned configuration
#
[main]
summary=Optimize for CPU partitioning with additional powersave
include=cpu-partitioning
[variables]
# User is responsible for updating variables.conf with variable content such as isolated_cores=X-Y
include=/etc/tuned/cpu-partitioning-powersave-variables.conf
isolated_cores_assert_check = \\${isolated_cores}
# Make sure isolated_cores is defined before any of the variables that
# use it (such as assert1) are defined, so that child profiles can set
# isolated_cores directly in the profile (tuned.conf)
isolated_cores = ${isolated_cores}
# Fail if isolated_cores are not set
assert1=${f:assertion_non_equal:isolated_cores are set:${isolated_cores}:${isolated_cores_assert_check}}
max_power_state_assert_check = \\${max_power_state}
max_power_state = ${max_power_state}
# Fail if max_power_state is not set
assert2=${f:assertion_non_equal:max_power_state is set:${max_power_state}:${max_power_state_assert_check}}
[cpu]
force_latency=${max_power_state}
no_turbo=true
[bootloader]
cmdline_cpu_part=+nohz=on${cmd_isolcpus} nohz_full=${isolated_cores} rcu_nocbs=${isolated_cores} tuned.non_isolcpus=${not_isolated_cpumask} intel_pstate=passive nosoftlockup

View file

@ -58,6 +58,11 @@ vm.dirty_background_ratio = 10
# and move them to swap cache
vm.swappiness=10
# The default kernel value 128 was over twenty years old default,
# kernel-5.4 increased it to 4096, thus do not have it lower than 2048
# on older kernels
net.core.somaxconn=>2048
# Marvell ThunderX
[sysctl.thunderx]
type=sysctl

View file

@ -206,7 +206,7 @@ class Base(object):
def is_tuned_connection_ok(self):
"""
Result True, False depends on if tuned daemon is running. If its not runing this method try to start tuned.
Result True, False depends on if tuned daemon is running. If its not running this method try to start tuned.
"""
try:
@ -236,7 +236,7 @@ class Base(object):
This add rows to object listbox_summary_of_active_profile.
Row consist of grid. Inside grid on first possition is label, second possition is vertical grid.
label = name of plugin
verical grid consist of labels where are stored values for plugin option and value.
vertical grid consist of labels where are stored values for plugin option and value.
This method is emited after change profile and on startup of app.
"""
@ -356,14 +356,14 @@ class Base(object):
try:
if self._get_active_profile_name() == profile:
self.error_dialog('You can not remove active profile',
'Please deactivate profile by choosind another!'
'Please deactivate profile by choosing another!'
)
return
if profile is None:
self.error_dialog('No profile selected!', '')
return
if self._gobj('windowProfileEditor').is_active():
self.error_dialog('You are ediding '
self.error_dialog('You are editing '
+ self.editing_profile_name
+ ' profile.',
'Please close edit window and try again.'

View file

@ -406,6 +406,7 @@ fi
%exclude %{_sysconfdir}/tuned/realtime-virtual-guest-variables.conf
%exclude %{_sysconfdir}/tuned/realtime-virtual-host-variables.conf
%exclude %{_sysconfdir}/tuned/cpu-partitioning-variables.conf
%exclude %{_sysconfdir}/tuned/cpu-partitioning-powersave-variables.conf
%exclude %{_prefix}/lib/tuned/default
%exclude %{_prefix}/lib/tuned/desktop-powersave
%exclude %{_prefix}/lib/tuned/laptop-ac-powersave
@ -423,6 +424,7 @@ fi
%exclude %{_prefix}/lib/tuned/realtime-virtual-guest
%exclude %{_prefix}/lib/tuned/realtime-virtual-host
%exclude %{_prefix}/lib/tuned/cpu-partitioning
%exclude %{_prefix}/lib/tuned/cpu-partitioning-powersave
%exclude %{_prefix}/lib/tuned/spectrumscale-ece
%exclude %{_prefix}/lib/tuned/postgresql
%exclude %{_prefix}/lib/tuned/openshift
@ -524,8 +526,11 @@ fi
%files profiles-cpu-partitioning
%config(noreplace) %{_sysconfdir}/tuned/cpu-partitioning-variables.conf
%config(noreplace) %{_sysconfdir}/tuned/cpu-partitioning-powersave-variables.conf
%{_prefix}/lib/tuned/cpu-partitioning
%{_prefix}/lib/tuned/cpu-partitioning-powersave
%{_mandir}/man7/tuned-profiles-cpu-partitioning.7*
%{_mandir}/man7/tuned-profiles-cpu-partitioning-powersave.7*
%files profiles-spectrumscale
%{_prefix}/lib/tuned/spectrumscale-ece

View file

@ -100,6 +100,7 @@ CFG_LOG_FILE_COUNT = "log_file_count"
CFG_LOG_FILE_MAX_SIZE = "log_file_max_size"
CFG_UNAME_STRING = "uname_string"
CFG_CPUINFO_STRING = "cpuinfo_string"
CFG_CPU_EPP_FLAG = "hwp_epp"
# no_daemon mode
CFG_DEF_DAEMON = True

View file

@ -308,3 +308,42 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
if caller == "":
return False
return self._daemon.get_plugin_hints(str(plugin_name))
# devices - devices to migrate from other instances, string of form "dev1,dev2,dev3,..."
# or "cpulist:CPULIST", where CPULIST is e.g. "0-3,6,8-9"
# instance_name - instance where to migrate devices
@exports.export("ss", "(bs)")
def instance_acquire_devices(self, devices, instance_name, caller = None):
if caller == "":
return (False, "Unauthorized")
found = False
for instance_target in self._daemon._unit_manager.instances:
if instance_target.name == instance_name:
log.debug("Found instance '%s'." % instance_target.name)
found = True
break
if not found:
rets = "Instance '%s' not found" % instance_name
log.error(rets)
return (False, rets)
devs = set(self._cmd.devstr2devs(devices))
log.debug("Instance '%s' trying to acquire devices '%s'." % (instance_target.name, str(devs)))
for instance in self._daemon._unit_manager.instances:
devs_moving = instance.processed_devices & devs
if len(devs_moving):
devs -= devs_moving
log.info("Moving devices '%s' from instance '%s' to instance '%s'." % (str(devs_moving),
instance.name, instance_target.name))
if (instance.plugin.name != instance_target.plugin.name):
rets = "Target instance '%s' is of type '%s', but devices '%s' are currently handled by " \
"instance '%s' which is of type '%s'." % (instance_target.name,
instance_target.plugin.name, str(devs_moving), instance.name, instance.plugin.name)
log.error(rets)
return (False, rets)
instance.plugin._remove_devices_nocheck(instance, devs_moving)
instance_target.plugin._add_devices_nocheck(instance_target, devs_moving)
if (len(devs)):
rets = "Ignoring devices not handled by any instance '%s'." % str(devs)
log.info(rets)
return (False, rets)
return (True, "OK")

View file

@ -6,10 +6,14 @@ import threading
import signal
import tuned.logs
import tuned.consts as consts
import traceback
import logging
from inspect import ismethod
from tuned.utils.polkit import polkit
from gi.repository import GLib
from types import FunctionType
from dbus.exceptions import DBusException
from dbus.lowlevel import ErrorMessage
try:
# Python3 version
@ -26,6 +30,29 @@ except ImportError:
log = tuned.logs.get()
# This is mostly copy of the code from the dbus.service module without the
# code that sends tracebacks through the D-Bus (i.e. no library tracebacks
# are exposed on the D-Bus now).
def _method_reply_error(connection, message, exception):
name = getattr(exception, '_dbus_error_name', None)
if name is not None:
pass
elif getattr(exception, '__module__', '') in ('', '__main__'):
name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
else:
name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
if isinstance(exception, DBusException):
contents = exception.get_dbus_message()
else:
contents = ''.join(traceback.format_exception_only(exception.__class__,
exception))
reply = ErrorMessage(message, name, contents)
if not message.get_no_reply():
connection.send_message(reply)
class DBusExporter(interfaces.ExporterInterface):
"""
Export method calls through DBus Interface.
@ -37,6 +64,14 @@ class DBusExporter(interfaces.ExporterInterface):
"""
def __init__(self, bus_name, interface_name, object_name):
# Monkey patching of the D-Bus library _method_reply_error() to reply
# tracebacks via D-Bus only if in the debug mode. It doesn't seem there is a
# more simple way how to cover all possible exceptions that could occur in
# the D-Bus library. Just setting the exception.include_traceback to False doesn't
# seem to help because there is only a subset of exceptions that support this flag.
if log.getEffectiveLevel() != logging.DEBUG:
dbus.service._method_reply_error = _method_reply_error
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
self._dbus_object_cls = None

View file

@ -76,7 +76,7 @@ class GuiPluginLoader():
"""
try:
config_parser = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
config_parser = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config_parser.optionxform = str
with open(file_name) as f:
config_parser.read_string("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read(), file_name)

View file

@ -62,7 +62,7 @@ class GuiProfileLoader(object):
if profilePath == tuned.consts.LOAD_DIRECTORIES[1]:
file_path = profilePath + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
config_parser = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
config_parser = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config_parser.optionxform = str
config_parser.read_string(config)
@ -84,7 +84,7 @@ class GuiProfileLoader(object):
def load_profile_config(self, profile_name, path):
conf_path = path + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config.optionxform = str
profile_config = collections.OrderedDict()
with open(conf_path) as f:

View file

@ -11,7 +11,7 @@ if __name__ == "__main__":
if not os.path.exists(profile_dict['filename']):
os.makedirs(os.path.dirname(profile_dict['filename']))
profile_configobj = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
profile_configobj = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
profile_configobj.optionxform = str
for section, options in profile_dict['main'].items():
profile_configobj.add_section(section)

View file

@ -17,10 +17,10 @@ class Plugin(base.Plugin):
self._hardware_events_cleanup()
def _hardware_events_init(self):
raise NotImplementedError()
pass
def _hardware_events_cleanup(self):
raise NotImplementedError()
pass
def _init_devices(self):
self._hardware_events_init()
@ -28,49 +28,79 @@ class Plugin(base.Plugin):
def _hardware_events_callback(self, event, device):
if event == "add":
log.info("device '%s' added" % device.sys_name)
self._add_device(device)
self._add_device(device.sys_name)
elif event == "remove":
log.info("device '%s' removed" % device.sys_name)
self._remove_device(device)
self._remove_device(device.sys_name)
def _add_device(self, device):
device_name = device.sys_name
def _add_device_process(self, instance, device_name):
log.info("instance %s: adding new device %s" % (instance.name, device_name))
self._assigned_devices.add(device_name)
self._call_device_script(instance, instance.script_pre, "apply", [device_name])
self._added_device_apply_tuning(instance, device_name)
self._call_device_script(instance, instance.script_post, "apply", [device_name])
instance.processed_devices.add(device_name)
def _add_device(self, device_name):
if device_name in (self._assigned_devices | self._free_devices):
return
for instance_name, instance in list(self._instances.items()):
if len(self._get_matching_devices(instance, [device_name])) == 1:
log.info("instance %s: adding new device %s" % (instance_name, device_name))
self._assigned_devices.add(device_name)
self._call_device_script(instance, instance.script_pre, "apply", [device_name])
self._added_device_apply_tuning(instance, device_name)
self._call_device_script(instance, instance.script_post, "apply", [device_name])
instance.processed_devices.add(device_name)
self._add_device_process(instance, device_name)
break
else:
log.debug("no instance wants %s" % device_name)
self._free_devices.add(device_name)
def _remove_device(self, device):
device_name = device.sys_name
def _add_devices_nocheck(self, instance, device_names):
"""
Add devices specified by the set to the instance, no check is performed.
"""
for dev in device_names:
self._add_device_process(instance, dev)
# This can be a bit racy (we can overcount),
# but it shouldn't affect the boolean result
instance.active = len(instance.processed_devices) \
+ len(instance.assigned_devices) > 0
def _remove_device_process(self, instance, device_name):
if device_name in instance.processed_devices:
self._call_device_script(instance, instance.script_post, "unapply", [device_name])
self._removed_device_unapply_tuning(instance, device_name)
self._call_device_script(instance, instance.script_pre, "unapply", [device_name])
instance.processed_devices.remove(device_name)
# This can be a bit racy (we can overcount),
# but it shouldn't affect the boolean result
instance.active = len(instance.processed_devices) \
+ len(instance.assigned_devices) > 0
self._assigned_devices.remove(device_name)
return True
return False
def _remove_device(self, device_name):
"""Remove device from the instance
Parameters:
device_name -- name of the device
"""
if device_name not in (self._assigned_devices | self._free_devices):
return
for instance in list(self._instances.values()):
if device_name in instance.processed_devices:
self._call_device_script(instance, instance.script_post, "unapply", [device_name])
self._removed_device_unapply_tuning(instance, device_name)
self._call_device_script(instance, instance.script_pre, "unapply", [device_name])
instance.processed_devices.remove(device_name)
# This can be a bit racy (we can overcount),
# but it shouldn't affect the boolean result
instance.active = len(instance.processed_devices) \
+ len(instance.assigned_devices) > 0
self._assigned_devices.remove(device_name)
if self._remove_device_process(instance, device_name):
break
else:
self._free_devices.remove(device_name)
def _remove_devices_nocheck(self, instance, device_names):
"""
Remove devices specified by the set from the instance, no check is performed.
"""
for dev in device_names:
self._remove_device_process(instance, dev)
def _added_device_apply_tuning(self, instance, device_name):
self._execute_all_device_commands(instance, [device_name])
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):

View file

@ -1,4 +1,4 @@
from . import base
from . import hotplug
from .decorators import *
import tuned.logs
from tuned.utils.commands import commands
@ -10,7 +10,7 @@ import glob
log = tuned.logs.get()
cmd = commands()
class AudioPlugin(base.Plugin):
class AudioPlugin(hotplug.Plugin):
"""
`audio`::

View file

@ -690,6 +690,8 @@ class BootloaderPlugin(base.Plugin):
log.warn("requested changes to initrd will not be applied!")
if len(self._cmdline_val) > 0:
log.warn("requested changes to cmdline will not be applied!")
# ensure that the desired cmdline is always written to BOOT_CMDLINE_FILE (/etc/tuned/bootcmdline)
self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : self._cmdline_val, consts.BOOT_CMDLINE_INITRD_ADD_VAR : self._initrd_val})
elif enabling and self.update_grub2_cfg:
if self._rpm_ostree:
self._rpm_ostree_update()

View file

@ -1,4 +1,4 @@
from . import base
from . import hotplug
from .decorators import *
import tuned.logs
from tuned.utils.commands import commands
@ -14,7 +14,7 @@ log = tuned.logs.get()
cpuidle_states_path = "/sys/devices/system/cpu/cpu0/cpuidle"
class CPULatencyPlugin(base.Plugin):
class CPULatencyPlugin(hotplug.Plugin):
"""
`cpu`::
@ -65,7 +65,8 @@ class CPULatencyPlugin(base.Plugin):
Bias (EPB) values are supported. The alternative values are separated
using the '|' character. The following EPB values are supported
starting with kernel 4.13: "performance", "balance-performance",
"normal", "balance-power" and "power".
"normal", "balance-power" and "power". On newer processors is value
writen straight to file (see rhbz#2095829)
+
.Specifying alternative Energy Performance Bias values
====
@ -78,6 +79,26 @@ class CPULatencyPlugin(base.Plugin):
try to set it to 'power'.
====
`energy_performance_preference`:::
[option]`energy_performance_preference` supports managing energy
vs. performance hints on some newer Intel processors. Multiple alternative
Energy Performance Preferences (EPP) values are supported. The alternative
values are separated using the '|' character. Available values can be found
in `energy_performance_available_preferences` file in `CPUFreq` policy
directory in `sysfs`.
in
+
.Specifying alternative Energy Performance Hints values
====
----
[cpu]
energy_performance_preference=balance_power|power
----
*TuneD* will try to set EPP to 'balance_power'. If that fails, it will
try to set it to 'power'.
====
`latency_low, latency_high, load_threshold`:::
+
If the CPU load is lower than the value specified by
@ -182,6 +203,8 @@ class CPULatencyPlugin(base.Plugin):
self._governors_map = {}
self._cmd = commands()
self._flags = None
def _init_devices(self):
self._devices_supported = True
self._free_devices = set()
@ -208,6 +231,7 @@ class CPULatencyPlugin(base.Plugin):
"max_perf_pct" : None,
"no_turbo" : None,
"pm_qos_resume_latency_us": None,
"energy_performance_preference" : None,
}
def _check_arch(self):
@ -256,6 +280,11 @@ class CPULatencyPlugin(base.Plugin):
if self._has_intel_pstate:
log.info("intel_pstate detected")
def _get_cpuinfo_flags(self):
if self._flags is None:
self._flags = procfs.cpuinfo().tags.get("flags", [])
return self._flags
def _is_cpu_online(self, device):
sd = str(device)
return self._cmd.is_cpu_online(str(device).replace("cpu", ""))
@ -550,15 +579,43 @@ class CPULatencyPlugin(base.Plugin):
return_err = True)
return (retcode, err_msg)
def _intel_preference_path(self, cpu_id, available = False):
return "/sys/devices/system/cpu/cpufreq/policy%s/energy_performance_%s" % (cpu_id, "available_preferences" if available else "preference")
def _energy_perf_bias_path(self, cpu_id):
return "/sys/devices/system/cpu/cpu%s/power/energy_perf_bias" % cpu_id
@command_set("energy_perf_bias", per_device=True)
def _set_energy_perf_bias(self, energy_perf_bias, device, sim):
if not self._is_cpu_online(device):
log.debug("%s is not online, skipping" % device)
return None
if self._has_energy_perf_bias:
cpu_id = device.lstrip("cpu")
vals = energy_perf_bias.split('|')
# 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():
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):
log.info("energy_perf_bias successfully set to '%s' on cpu '%s'"
% (val, device))
break
else:
log.error("Failed to set energy_perf_bias on cpu '%s'. Is the value in the profile correct?"
% device)
return str(energy_perf_bias)
else:
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:
if not sim:
cpu_id = device.lstrip("cpu")
vals = energy_perf_bias.split('|')
for val in vals:
val = val.strip()
log.debug("Trying to set energy_perf_bias to '%s' on cpu '%s'"
@ -612,8 +669,12 @@ class CPULatencyPlugin(base.Plugin):
if not self._is_cpu_online(device):
log.debug("%s is not online, skipping" % device)
return None
if self._has_energy_perf_bias:
cpu_id = device.lstrip("cpu")
cpu_id = device.lstrip("cpu")
if consts.CFG_CPU_EPP_FLAG in self._get_cpuinfo_flags():
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:
retcode, lines = self._cmd.execute(["x86_energy_perf_policy", "-c", cpu_id, "-r"])
if retcode == 0:
for line in lines.splitlines():
@ -656,3 +717,41 @@ class CPULatencyPlugin(base.Plugin):
log.debug("%s is not online, skipping" % device)
return None
return self._cmd.read_file(self._pm_qos_resume_latency_us_path(device), no_error=ignore_missing).strip()
@command_set("energy_performance_preference", per_device=True)
def _set_energy_performance_preference(self, energy_performance_preference, device, sim):
if not self._is_cpu_online(device):
log.debug("%s is not online, skipping" % device)
return None
cpu_id = device.lstrip("cpu")
if os.path.exists(self._intel_preference_path(cpu_id, True)):
vals = energy_performance_preference.split('|')
if not sim:
avail_vals = set(self._cmd.read_file(self._intel_preference_path(cpu_id, True)).split())
for val in vals:
if val in avail_vals:
self._cmd.write_to_file(self._intel_preference_path(cpu_id), val)
log.info("Setting energy_performance_preference value '%s' for cpu '%s'" % (val, device))
break
else:
log.warn("energy_performance_preference value '%s' unavailable for cpu '%s'" % (val, device))
else:
log.error("Failed to set energy_performance_preference on cpu '%s'. Is the value in the profile correct?"
% device)
return str(energy_performance_preference)
else:
log.debug("energy_performance_available_preferences file missing, which can happen if the system is booted without the intel_pstate driver.")
return None
@command_get("energy_performance_preference")
def _get_energy_performance_preference(self, device, ignore_missing=False):
if not self._is_cpu_online(device):
log.debug("%s is not online, skipping" % device)
return None
cpu_id = device.lstrip("cpu")
# intel_pstate CPU scaling driver
if os.path.exists(self._intel_preference_path(cpu_id, True)):
return self._cmd.read_file(self._intel_preference_path(cpu_id)).strip()
else:
log.debug("energy_performance_available_preferences file missing, which can happen if the system is booted without the intel_pstate driver.")
return None

View file

@ -1,5 +1,5 @@
import errno
from . import base
from . import hotplug
from .decorators import *
import tuned.logs
from tuned.utils.nettool import ethcard
@ -11,7 +11,7 @@ log = tuned.logs.get()
WOL_VALUES = "pumbagsd"
class NetTuningPlugin(base.Plugin):
class NetTuningPlugin(hotplug.Plugin):
"""
`net`::
@ -323,7 +323,7 @@ class NetTuningPlugin(base.Plugin):
# 0.6 is just a magical constant (empirical value): Typical workload on netcard won't exceed
# that and if it does, then the code is smart enough to adapt it.
# 1024 * 1024 as for MB -> B
# speed / 7 Mb -> MB
# speed / 8 Mb -> MB
return (int) (0.6 * 1024 * 1024 * speed / 8)
# parse features/coalesce config parameters (those defined in profile configuration)

View file

@ -526,7 +526,8 @@ class SchedulerPlugin(base.Plugin):
instance._runtime_tuning = False
def _instance_cleanup(self, instance):
pass
for fd in instance._evlist.get_pollfd():
os.close(fd.name)
@classmethod
def _get_config_options(cls):

View file

@ -84,7 +84,7 @@ class SysctlPlugin(base.Plugin):
if self._global_cfg.get_bool(consts.CFG_REAPPLY_SYSCTL, consts.CFG_DEF_REAPPLY_SYSCTL):
log.info("reapplying system sysctl")
_apply_system_sysctl()
_apply_system_sysctl(instance._sysctl)
def _instance_verify_static(self, instance, ignore_missing, devices):
ret = True
@ -103,7 +103,7 @@ class SysctlPlugin(base.Plugin):
_write_sysctl(option, value)
def _apply_system_sysctl():
def _apply_system_sysctl(instance_sysctl):
files = {}
for d in SYSCTL_CONFIG_DIRS:
try:
@ -119,15 +119,15 @@ def _apply_system_sysctl():
for fname in sorted(files.keys()):
d = files[fname]
path = "%s/%s" % (d, fname)
_apply_sysctl_config_file(path)
_apply_sysctl_config_file("/etc/sysctl.conf")
_apply_sysctl_config_file(path, instance_sysctl)
_apply_sysctl_config_file("/etc/sysctl.conf", instance_sysctl)
def _apply_sysctl_config_file(path):
def _apply_sysctl_config_file(path, instance_sysctl):
log.debug("Applying sysctl settings from file %s" % path)
try:
with open(path, "r") as f:
for lineno, line in enumerate(f, 1):
_apply_sysctl_config_line(path, lineno, line)
_apply_sysctl_config_line(path, lineno, line, instance_sysctl)
log.debug("Finished applying sysctl settings from file %s"
% path)
except (OSError, IOError) as e:
@ -135,7 +135,7 @@ def _apply_sysctl_config_file(path):
log.error("Error reading sysctl settings from file %s: %s"
% (path, str(e)))
def _apply_sysctl_config_line(path, lineno, line):
def _apply_sysctl_config_line(path, lineno, line, instance_sysctl):
line = line.strip()
if len(line) == 0 or line[0] == "#" or line[0] == ";":
return
@ -151,6 +151,10 @@ def _apply_sysctl_config_line(path, lineno, line):
% (path, lineno))
return
value = value.strip()
if option in instance_sysctl:
log.info("Overriding sysctl parameter '%s' from '%s' to '%s'"
% (option, instance_sysctl[option], value))
_write_sysctl(option, value, ignore_missing = True)
def _get_sysctl_path(option):

View file

@ -0,0 +1,17 @@
import tuned.logs
from . import base
log = tuned.logs.get()
class cpulist2devs(base.Function):
"""
Conversion function: converts CPU list to device strings
"""
def __init__(self):
# arbitrary number of arguments
super(cpulist2devs, self).__init__("cpulist2devs", 0)
def execute(self, args):
if not super(cpulist2devs, self).execute(args):
return None
return self._cmd.cpulist2string(self._cmd.cpulist_unpack(",".join(args)), prefix = "cpu")

View file

@ -96,11 +96,11 @@ class Loader(object):
def _load_config_data(self, file_name):
try:
config_obj = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
config_obj = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config_obj.optionxform=str
with open(file_name) as f:
config_obj.read_file(f, file_name)
except Error as e:
except Error.__bases__ as e:
raise InvalidProfileException("Cannot parse '%s'." % file_name, e)
config = collections.OrderedDict()

View file

@ -55,7 +55,7 @@ class Locator(object):
if config_file is None:
return None
try:
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), allow_no_value=True)
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), allow_no_value=True, strict=False)
config.optionxform = str
with open(config_file) as f:
config.read_string("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read())

View file

@ -45,7 +45,7 @@ class Variables():
log.error("unable to find variables_file: '%s'" % filename)
return
try:
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), allow_no_value=True)
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), allow_no_value=True, strict=False)
config.optionxform = str
with open(filename) as f:
config.read_string("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read(), filename)

View file

@ -414,8 +414,20 @@ class commands:
m |= pow(2, v)
return m
def cpulist2string(self, l):
return ",".join(str(v) for v in l)
def cpulist2string(self, l, prefix = ""):
return ",".join((prefix + str(v)) for v in l)
# Converts string s consisting of "dev1,dev2,dev3,..." to list ["dev1", "dev2, "dev3", ...],
# whitespaces are ignored, cpu lists are supported with the prefix "cpulist:", e.g.
# "cpulist:0-2,4" is converted to ["cpu0", "cpu1", "cpu2", "cpu4"]. If device name starts
# with "cpulist:" write it as "cpulist:cpulist:". Escape commas in name with the "\,".
def devstr2devs(self, s):
if s[0:8].lower() == "cpulist:":
s = s[8:]
if s[0:8].lower() != "cpulist:":
return [("cpu" + str(v)) for v in self.cpulist_unpack(s)]
l = re.split(r"\s*(?<!\\),\s*", s)
return [str(v).replace("\,", ",") for v in l]
# Do not make balancing on patched Python 2 interpreter (rhbz#1028122).
# It means less CPU usage on patchet interpreter. On non-patched interpreter

View file

@ -21,7 +21,7 @@ else:
class ConfigParser(cp.ConfigParser):
def __init__(self, delimiters=None, inline_comment_prefixes=None, strict=True, *args, **kwargs):
def __init__(self, delimiters=None, inline_comment_prefixes=None, *args, **kwargs):
delims = "".join(list(delimiters))
# REs taken from the python-2.7 ConfigParser
self.OPTCRE = re.compile(

View file

@ -39,7 +39,7 @@ class GlobalConfig():
"""
log.debug("reading and parsing global configuration file '%s'" % file_name)
try:
config_parser = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
config_parser = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config_parser.optionxform = str
with open(file_name) as f:
config_parser.read_string("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read(), file_name)

View file

@ -32,7 +32,7 @@ class polkit():
try:
uid = self._bus.get_unix_user(sender)
except dbus.exceptions.DBusException as e:
log.error("error using falback authorization method: %s" % e)
log.error("error using fallback authorization method: %s" % e)
return -2
if uid == 0:
return 2

View file

@ -60,7 +60,7 @@ class ProfileRecommender:
try:
if not os.path.isfile(fname):
return None
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'))
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config.optionxform = str
with open(fname) as f:
config.read_file(f, fname)