From 6ef5b58ea870f770bcc72ffc5d1806e40a0fabd9 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 1 Apr 2024 23:26:01 -0500 Subject: [PATCH] tuned-ppd: Detect battery change events power-profiles-daemon has the ability to detect battery change events using upower and to apply different tuned settings based upon whether plugged into power or not. In PPD this is used specifically to set the energy performance preference differently in the 'balanced' profile, but there is no reason that this concept can't actually apply to all profiles. Add support for detecting battery change events and apply a profile specified in ppd.conf for battery in the different PPD states. --- profiles/balanced-battery/tuned.conf | 10 ++++++++++ tuned/ppd/config.py | 24 ++++++++++++++++++++++++ tuned/ppd/controller.py | 24 +++++++++++++++++++++++- tuned/ppd/ppd.conf | 5 +++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 profiles/balanced-battery/tuned.conf diff --git a/profiles/balanced-battery/tuned.conf b/profiles/balanced-battery/tuned.conf new file mode 100644 index 00000000..05f60e8a --- /dev/null +++ b/profiles/balanced-battery/tuned.conf @@ -0,0 +1,10 @@ +# +# tuned configuration +# + +[main] +summary=Balanced profile biased towards power savings changes for battery +include=balanced + +[cpu] +energy_performance_preference=balance_power diff --git a/tuned/ppd/config.py b/tuned/ppd/config.py index bf39077a..1e33016e 100644 --- a/tuned/ppd/config.py +++ b/tuned/ppd/config.py @@ -7,13 +7,19 @@ MAIN_SECTION = "main" PROFILES_SECTION = "profiles" +BATTERY_SECTION = "battery" DEFAULT_PROFILE_OPTION = "default" +BATTERY_DETECTION_OPTION = "battery_detection" class PPDConfig: def __init__(self, config_file): self.load_from_file(config_file) + @property + def battery_detection(self): + return self._battery_detection + @property def default_profile(self): return self._default_profile @@ -26,6 +32,10 @@ def ppd_to_tuned(self): def tuned_to_ppd(self): return self._tuned_to_ppd + @property + def ppd_to_tuned_battery(self): + return self._ppd_to_tuned_battery + def load_from_file(self, config_file): cfg = ConfigParser() @@ -59,3 +69,17 @@ def load_from_file(self, 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} diff --git a/tuned/ppd/controller.py b/tuned/ppd/controller.py index f74d218f..1016c281 100644 --- a/tuned/ppd/controller.py +++ b/tuned/ppd/controller.py @@ -13,6 +13,9 @@ NO_TURBO_PATH = "/sys/devices/system/cpu/intel_pstate/no_turbo" LAP_MODE_PATH = "/sys/bus/platform/devices/thinkpad_acpi/dytc_lapmode" +UPOWER_DBUS_NAME = "org.freedesktop.UPower" +UPOWER_DBUS_PATH = "/org/freedesktop/UPower" +UPOWER_DBUS_INTERFACE = "org.freedesktop.UPower" class PerformanceDegraded(StrEnum): NONE = "" @@ -101,8 +104,25 @@ def __init__(self, bus, tuned_interface): self._performance_degraded = PerformanceDegraded.NONE self._cmd = commands() self._terminate = threading.Event() + self._on_battery = False self.load_config() + def upower_changed(self, interface, changed, invalidated): + properties = dbus.Interface(self.proxy, dbus.PROPERTIES_IFACE) + self._on_battery = bool(properties.Get(UPOWER_DBUS_INTERFACE, "OnBattery")) + tuned_profile = self._config.ppd_to_tuned_battery[self._base_profile] if self._on_battery else self._config.ppd_to_tuned[self._base_profile] + log.info("Switching to profile '%s' due to battery %s" % (tuned_profile, self._on_battery)) + self._tuned_interface.switch_profile(tuned_profile) + + def setup_battery_signaling(self): + try: + bus = dbus.SystemBus() + self.proxy = bus.get_object(UPOWER_DBUS_NAME, UPOWER_DBUS_PATH) + self.proxy.connect_to_signal("PropertiesChanged", self.upower_changed) + self.upower_changed(None, None, None) + except dbus.exceptions.DBusException as error: + log.debug(error) + def _check_performance_degraded(self): performance_degraded = PerformanceDegraded.NONE if os.path.exists(NO_TURBO_PATH): @@ -137,11 +157,13 @@ def load_config(self): self._config = PPDConfig(PPD_CONFIG_FILE) self._base_profile = self._config.default_profile self.switch_profile(self._config.default_profile) + if self._config.battery_detection: + self.setup_battery_signaling() def switch_profile(self, profile): if self.active_profile() == profile: return - tuned_profile = self._config.ppd_to_tuned[profile] + tuned_profile = self._config.ppd_to_tuned_battery[profile] if self._on_battery else self._config.ppd_to_tuned[profile] log.info("Switching to profile '%s'" % tuned_profile) self._tuned_interface.switch_profile(tuned_profile) exports.property_changed("ActiveProfile", profile) diff --git a/tuned/ppd/ppd.conf b/tuned/ppd/ppd.conf index 9b2b9c60..7296b3cf 100644 --- a/tuned/ppd/ppd.conf +++ b/tuned/ppd/ppd.conf @@ -1,9 +1,14 @@ [main] # The default PPD profile default=balanced +battery_detection=true [profiles] # PPD = TuneD power-saver=powersave balanced=balanced performance=throughput-performance + +[battery] +# PPD = TuneD +balanced=balanced-battery