diff --git a/profiles/balanced-battery/tuned.conf b/profiles/balanced-battery/tuned.conf new file mode 100644 index 0000000..05f60e8 --- /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 bf39077..1e33016 100644 --- a/tuned/ppd/config.py +++ b/tuned/ppd/config.py @@ -7,13 +7,19 @@ PPD_PERFORMANCE = "performance" 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 @@ class PPDConfig: 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 @@ class PPDConfig: 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 f74d218..1016c28 100644 --- a/tuned/ppd/controller.py +++ b/tuned/ppd/controller.py @@ -13,6 +13,9 @@ DRIVER = "tuned" 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 @@ class Controller(exports.interfaces.ExportableInterface): 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 @@ class Controller(exports.interfaces.ExportableInterface): 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 9b2b9c6..7296b3c 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