Skip to content

Commit

Permalink
Merge pull request #669 from zacikpa/ppd-adjustments
Browse files Browse the repository at this point in the history
More tuned-ppd adjustments
  • Loading branch information
yarda authored Dec 11, 2024
2 parents 90c24ee + 23eb673 commit d9293d7
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 71 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ install: install-dirs
echo -n > $(DESTDIR)$(TUNED_CFG_DIR)/active_profile
echo -n > $(DESTDIR)$(TUNED_CFG_DIR)/profile_mode
echo -n > $(DESTDIR)$(TUNED_CFG_DIR)/post_loaded_profile
echo -n > $(DESTDIR)$(TUNED_CFG_DIR)/ppd_base_profile
install -Dpm 0644 bootcmdline $(DESTDIR)$(TUNED_CFG_DIR)/bootcmdline
install -Dpm 0644 modules.conf $(DESTDIR)$(SYSCONFDIR)/modprobe.d/tuned.conf

Expand Down
2 changes: 2 additions & 0 deletions tuned.spec
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ BuildRequires: %{_py}-mock
BuildRequires: %{_py}-pyudev
Requires: %{_py}-pyudev
Requires: %{_py}-linux-procfs, %{_py}-perf
Requires: %{_py}-inotify
%if %{without python3}
Requires: %{_py}-schedutils
%endif
Expand Down Expand Up @@ -504,6 +505,7 @@ fi
%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/active_profile
%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/profile_mode
%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/post_loaded_profile
%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/ppd_base_profile
%config(noreplace) %{_sysconfdir}/tuned/tuned-main.conf
%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/bootcmdline
%verify(not size mtime md5) %{_sysconfdir}/modprobe.d/tuned.conf
Expand Down
2 changes: 2 additions & 0 deletions tuned/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
PPD_DBUS_OBJECT = "/net/hadess/PowerProfiles"
PPD_DBUS_INTERFACE = PPD_DBUS_BUS
PPD_CONFIG_FILE = "/etc/tuned/ppd.conf"
PPD_BASE_PROFILE_FILE = "/etc/tuned/ppd_base_profile"
PPD_API_COMPATIBILITY = "0.23"

# After adding new option to tuned-main.conf add here its name with CFG_ prefix
# and eventually default value with CFG_DEF_ prefix (default is None)
Expand Down
123 changes: 91 additions & 32 deletions tuned/ppd/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,90 @@
import os

PPD_POWER_SAVER = "power-saver"
PPD_BALANCED = "balanced"
PPD_PERFORMANCE = "performance"

MAIN_SECTION = "main"
PROFILES_SECTION = "profiles"
BATTERY_SECTION = "battery"
DEFAULT_PROFILE_OPTION = "default"
BATTERY_DETECTION_OPTION = "battery_detection"
THINKPAD_FUNCTION_KEYS_OPTION = "thinkpad_function_keys"


class ProfileMap:
"""
Mapping of PPD profiles to TuneD profiles or vice versa.
"""
def __init__(self, ac_map, dc_map):
self._ac_map = ac_map
self._dc_map = dc_map

def get(self, profile, on_battery):
"""
Returns a TuneD profile corresponding to the given
PPD profile and power supply status (or vice versa).
"""
profile_map = self._dc_map if on_battery else self._ac_map
return profile_map[profile]

def keys(self, on_battery):
"""
Returns the supported PPD or TuneD profiles.
"""
profile_map = self._dc_map if on_battery else self._ac_map
return profile_map.keys()


class PPDConfig:
def __init__(self, config_file):
"""
Configuration for the tuned-ppd daemon.
"""
def __init__(self, config_file, tuned_interface):
self._tuned_interface = tuned_interface
self.load_from_file(config_file)

@property
def battery_detection(self):
"""
Whether battery detection is enabled.
"""
return self._battery_detection

@property
def default_profile(self):
"""
Default PPD profile to set during initialization.
"""
return self._default_profile

@property
def ppd_to_tuned(self):
"""
Mapping of PPD profiles to TuneD profiles.
"""
return self._ppd_to_tuned

@property
def tuned_to_ppd(self):
"""
Mapping of TuneD profiles to PPD profiles.
"""
return self._tuned_to_ppd

@property
def ppd_to_tuned_battery(self):
return self._ppd_to_tuned_battery
def thinkpad_function_keys(self):
"""
Whether to react to changes of ACPI platform profile
done via function keys (e.g., Fn-L) on newer Thinkpad
machines. Experimental feature.
"""
return self._thinkpad_function_keys

def load_from_file(self, config_file):
"""
Loads the configuration from the provided file.
"""
cfg = ConfigParser()

if not os.path.isfile(config_file):
Expand All @@ -48,38 +98,47 @@ def load_from_file(self, config_file):

if PROFILES_SECTION not in cfg:
raise TunedException("Missing profiles section in the configuration file '%s'" % config_file)
self._ppd_to_tuned = dict(cfg[PROFILES_SECTION])

if not all(isinstance(mapped_profile, str) for mapped_profile in self._ppd_to_tuned.values()):
raise TunedException("Invalid profile mapping in the configuration file '%s'" % config_file)
profile_dict_ac = dict(cfg[PROFILES_SECTION])

if len(set(self._ppd_to_tuned.values())) != len(self._ppd_to_tuned):
raise TunedException("Duplicate profile mapping in the configuration file '%s'" % config_file)
self._tuned_to_ppd = {v: k for k, v in self._ppd_to_tuned.items()}

if PPD_POWER_SAVER not in self._ppd_to_tuned:
if PPD_POWER_SAVER not in profile_dict_ac:
raise TunedException("Missing power-saver profile in the configuration file '%s'" % config_file)

if PPD_PERFORMANCE not in self._ppd_to_tuned:
if PPD_BALANCED not in profile_dict_ac:
raise TunedException("Missing balanced profile in the configuration file '%s'" % config_file)

if PPD_PERFORMANCE not in profile_dict_ac:
raise TunedException("Missing performance profile in the configuration file '%s'" % config_file)

if MAIN_SECTION not in cfg or DEFAULT_PROFILE_OPTION not in cfg[MAIN_SECTION]:
raise TunedException("Missing default profile in the configuration file '%s'" % config_file)

self._default_profile = cfg[MAIN_SECTION][DEFAULT_PROFILE_OPTION]
if self._default_profile not in self._ppd_to_tuned:
raise TunedException("Unknown default profile '%s'" % self._default_profile)

if BATTERY_DETECTION_OPTION not in cfg[MAIN_SECTION]:
raise TunedException("Missing battery detection option in the configuration file '%s'" % config_file)
self._ppd_to_tuned_battery = self._ppd_to_tuned
self._battery_detection = cfg.getboolean(MAIN_SECTION, BATTERY_DETECTION_OPTION)
if self._battery_detection:
if BATTERY_SECTION not in cfg:
raise TunedException("Missing battery section in the configuration file '%s'" % config_file)
for k, _v in dict(cfg[PROFILES_SECTION]).items():
if k in cfg[BATTERY_SECTION].keys():
self._tuned_to_ppd = self._tuned_to_ppd | {cfg[BATTERY_SECTION][k]:k}
for k, v in dict(cfg[BATTERY_SECTION]).items():
if k in cfg[PROFILES_SECTION].keys():
self._ppd_to_tuned_battery = self._ppd_to_tuned_battery | {k:v}
self._default_profile = PPD_BALANCED
else:
self._default_profile = cfg[MAIN_SECTION][DEFAULT_PROFILE_OPTION]

if self._default_profile not in profile_dict_ac:
raise TunedException("Default profile '%s' missing in the profile mapping" % self._default_profile)

self._battery_detection = cfg.getboolean(MAIN_SECTION, BATTERY_DETECTION_OPTION, fallback=BATTERY_SECTION in cfg)

if self._battery_detection and BATTERY_SECTION not in cfg:
raise TunedException("Missing battery section in the configuration file '%s'" % config_file)

profile_dict_dc = profile_dict_ac | dict(cfg[BATTERY_SECTION]) if self._battery_detection else profile_dict_ac

# Make sure all of the TuneD profiles specified in the configuration file actually exist
unknown_tuned_profiles = (set(profile_dict_ac.values()) | set(profile_dict_dc.values())) - set(self._tuned_interface.profiles())
if unknown_tuned_profiles:
raise TunedException("Unknown TuneD profiles in the configuration file: " + ", ".join(unknown_tuned_profiles))

# Make sure there are no PPD profiles appearing in the battery section which are not defined before
unknown_battery_profiles = set(profile_dict_dc.keys()) - set(profile_dict_ac.keys())
if unknown_battery_profiles:
raise TunedException("Unknown PPD profiles in the battery section: " + ", ".join(unknown_battery_profiles))

# Make sure the profile mapping is injective so it can be reverted
if len(set(profile_dict_ac.values())) != len(profile_dict_ac) or len(set(profile_dict_dc.values())) != len(profile_dict_dc):
raise TunedException("Duplicate profile mapping in the configuration file '%s'" % config_file)

self._ppd_to_tuned = ProfileMap(profile_dict_ac, profile_dict_dc)
self._tuned_to_ppd = ProfileMap({v: k for k, v in profile_dict_ac.items()}, {v: k for k, v in profile_dict_dc.items()})

self._thinkpad_function_keys = cfg.getboolean(MAIN_SECTION, THINKPAD_FUNCTION_KEYS_OPTION, fallback=False)
Loading

0 comments on commit d9293d7

Please sign in to comment.