Blob Blame History Raw
diff --git a/man/tuned-main.conf.5 b/man/tuned-main.conf.5
index 61a6a4b..6f324be 100644
--- a/man/tuned-main.conf.5
+++ b/man/tuned-main.conf.5
@@ -16,17 +16,31 @@ will be used. Please note if it is enabled here, it is still possible
 to individually disable it in plugins.
 
 .TP
+.BI sleep_interval= INT
+Tuned daemon is periodically waken after \fIINT\fR seconds and checks
+for events. By default this is set to 1 second. If you have Python 2
+interpreter with applied patch from Red Hat Bugzilla #917709 this
+controls responsiveness time of Tuned to commands (i.e. if you
+request profile switch, it may take up to 1 second until Tuned reacts).
+Increase this number for higher responsiveness times and more power
+savings (due to lower number of wakeups). In case you have unpatched
+Python 2 interpreter, this settings will have no visible effect,
+because the intepreter will poll 20 times per second.
+
+.TP
 .BI update_interval= INT
 Update interval for dynamic tuning (in seconds). Tuned daemon is periodically
 waken after \fIINT\fR seconds, updates its monitors, calculates new tuning
 parameters for enabled plugins and applies the changes. Plugins that have
 disabled dynamic tuning are not processed. By default the \fIINT\fR is set
 to 10 seconds. Tuned daemon doesn't periodically wake if dynamic tuning is
-globally disabled (see \fBdynamic_tuning\fR).
+globally disabled (see \fBdynamic_tuning\fR) or this setting set to 0.
+This must be multiple of \fBsleep_interval\fR.
 
 .SH EXAMPLE
 .nf
 dynamic_tuning = 1
+sleep_interval = 1
 update_interval = 10
 .fi
 
diff --git a/tuned/consts.py b/tuned/consts.py
index 1dd53ab..6c8dc4b 100644
--- a/tuned/consts.py
+++ b/tuned/consts.py
@@ -18,5 +18,7 @@ SYSTEM_RELEASE_FILE = "/etc/system-release-cpe"
 
 # default configuration
 CFG_DEF_DYNAMIC_TUNING = True
+# how long to sleep before checking for events (in seconds)
+CFG_DEF_SLEEP_INTERVAL = 1
 # update interval for dynamic tuning (in seconds)
 CFG_DEF_UPDATE_INTERVAL = 10
diff --git a/tuned/daemon/application.py b/tuned/daemon/application.py
index f1b5208..e4c9da3 100644
--- a/tuned/daemon/application.py
+++ b/tuned/daemon/application.py
@@ -16,6 +16,7 @@ log = tuned.logs.get()
 __all__ = ["Application"]
 
 global_config_spec = 	["dynamic_tuning = boolean(default=%s)" % consts.CFG_DEF_DYNAMIC_TUNING,
+			"sleep_interval = integer(default=%s)" % consts.CFG_DEF_SLEEP_INTERVAL,
 			"update_interval = integer(default=%s)" % consts.CFG_DEF_UPDATE_INTERVAL]
 
 class Application(object):
@@ -31,7 +32,6 @@ class Application(object):
 		self.config = self._load_global_config()
 		if self.config.get("dynamic_tuning"):
 			log.info("dynamic tuning is enabled (can be overriden in plugins)")
-			log.info("update interval is %d seconds" % self.config.get("update_interval"))
 		else:
 			log.info("dynamic tuning is globally disabled")
 
@@ -44,7 +44,7 @@ class Application(object):
 		profile_loader = profiles.Loader(profile_locator, profile_factory, profile_merger)
 
 
-		self._daemon = daemon.Daemon(unit_manager, profile_loader, profile_name, int(self.config.get("update_interval", consts.CFG_DEF_UPDATE_INTERVAL)))
+		self._daemon = daemon.Daemon(unit_manager, profile_loader, profile_name, self.config)
 		self._controller = controller.Controller(self._daemon)
 
 		self._dbus_exporter = None
diff --git a/tuned/daemon/daemon.py b/tuned/daemon/daemon.py
index 804ac0b..35f60c2 100644
--- a/tuned/daemon/daemon.py
+++ b/tuned/daemon/daemon.py
@@ -9,9 +9,27 @@ log = tuned.logs.get()
 
 
 class Daemon(object):
-	def __init__(self, unit_manager, profile_loader, profile_name=None, update_interval = int(consts.CFG_DEF_UPDATE_INTERVAL)):
+	def __init__(self, unit_manager, profile_loader, profile_name=None, config=None):
 		log.debug("initializing daemon")
-		self._update_interval = update_interval
+		self._sleep_interval = int(consts.CFG_DEF_SLEEP_INTERVAL)
+		self._update_interval = int(consts.CFG_DEF_UPDATE_INTERVAL)
+		self._dynamic_tuning = consts.CFG_DEF_DYNAMIC_TUNING
+		if config is not None:
+			self._sleep_interval = int(config.get("sleep_interval", consts.CFG_DEF_SLEEP_INTERVAL))
+			self._update_interval = int(config.get("update_interval", consts.CFG_DEF_UPDATE_INTERVAL))
+			self._dynamic_tuning = config.get("dynamic_tuning", consts.CFG_DEF_DYNAMIC_TUNING)
+		if self._sleep_interval <= 0:
+			self._sleep_interval = int(consts.CFG_DEF_SLEEP_INTERVAL)
+		if self._update_interval == 0:
+			self._dynamic_tuning = False
+		elif self._update_interval < self._sleep_interval:
+			self._update_interval = self._sleep_interval
+		self._sleep_cycles = self._update_interval // self._sleep_interval
+		log.info("using sleep interval of %d second(s)" % self._sleep_interval)
+		if self._dynamic_tuning:
+			log.info("dynamic tuning is enabled (can be overriden by plugins)")
+			log.info("using update interval of %d second(s) (%d times of the sleep interval)" % (self._sleep_cycles * self._sleep_interval, self._sleep_cycles))
+
 		self._unit_manager = unit_manager
 		self._profile_loader = profile_loader
 		self._init_threads()
@@ -65,11 +83,21 @@ class Daemon(object):
 		self._save_active_profile(self._profile.name)
 		self._unit_manager.start_tuning()
 
-		while not tuned.utils.commands.wait(self._terminate, self._update_interval):
-			log.debug("updating monitors")
-			self._unit_manager.update_monitors()
-			log.debug("performing tunings")
-			self._unit_manager.update_tuning()
+		# In python 2 interpreter with applied patch for rhbz#917709 we need to periodically
+		# poll, otherwise the python will not have chance to update events / locks (due to GIL)
+		# and e.g. DBus control will not work. The polling interval of 1 seconds (which is
+		# the default) is still much better than 50 ms polling with unpatched interpreter.
+		# For more details see tuned rhbz#917587.
+		_sleep_cnt = self._sleep_cycles
+		while not tuned.utils.commands.wait(self._terminate, self._sleep_interval):
+			if self._dynamic_tuning:
+				_sleep_cnt -= 1
+				if _sleep_cnt <= 0:
+					_sleep_cnt = self._sleep_cycles
+					log.debug("updating monitors")
+					self._unit_manager.update_monitors()
+					log.debug("performing tunings")
+					self._unit_manager.update_tuning()
 
 		self._unit_manager.stop_tuning()
 		self._unit_manager.destroy_all()