Skip to content

Commit c513217

Browse files
committed
Refactor dynamic tuning functionality
- Add a new global option "dynamic_plugins" which can be used to globally enable dynamic tuning only for specific plugins. - Let plugins choose between the default (periodic) dynamic tuning and their own implementation. - Remove the "runtime" option from the scheduler plugin and integrate its functionality into dynamic tuning. Currently, the scheduler plugin is the only dynamic plugin with its own implementation of dynamic tuning (not initiated periodically by the main daemon thread). - Adjust default global configuration so that dynamic tuning is enabled by default, but only for the scheduler plugin.
1 parent fea5934 commit c513217

34 files changed

+343
-213
lines changed

profiles/openshift/tuned.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ vm.max_map_count=262144
3333
# see rhbz#1979352; exclude containers from aligning to house keeping CPUs
3434
cgroup_ps_blacklist=/kubepods\.slice/
3535
# workaround for rhbz#1921738
36-
runtime=0
36+
dynamic=0

tests/beakerlib/Traceback-caused-by-scheduler-plugin-with/runtest.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ rlJournalStart
4242
rlRun "pushd /etc/tuned/test-profile"
4343
cat << EOF > tuned.conf
4444
[scheduler]
45-
runtime=0
45+
dynamic=0
4646
EOF
4747
rlRun "popd"
4848

tuned-main.conf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
daemon = 1
88

99
# Dynamically tune devices, if disabled only static tuning will be used.
10-
dynamic_tuning = 0
10+
dynamic_tuning = 1
11+
12+
# A list of plugins to allow dynamic tuning for. The "dynamic_tuning"
13+
# option must be enabled for this to have any effect. If not set, all
14+
# plugins are allowed to perform dynamic tuning.
15+
dynamic_plugins = scheduler
1116

1217
# How long to sleep before checking for events (in seconds)
1318
# higher number means lower overhead but longer response time.

tuned/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
# (see configobj for methods, default is get for string)
104104
CFG_DAEMON = "daemon"
105105
CFG_DYNAMIC_TUNING = "dynamic_tuning"
106+
CFG_DYNAMIC_PLUGINS = "dynamic_plugins"
106107
CFG_SLEEP_INTERVAL = "sleep_interval"
107108
CFG_UPDATE_INTERVAL = "update_interval"
108109
CFG_RECOMMEND_COMMAND = "recommend_command"

tuned/daemon/application.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ def __init__(self, profile_name = None, config = None):
2828
storage_factory = storage.Factory(storage_provider)
2929

3030
self.config = GlobalConfig() if config is None else config
31-
if self.config.get_bool(consts.CFG_DYNAMIC_TUNING):
32-
log.info("dynamic tuning is enabled (can be overridden in plugins)")
33-
else:
34-
log.info("dynamic tuning is globally disabled")
3531

3632
monitors_repository = monitors.Repository()
3733
udev_buffer_size = self.config.get_size("udev_buffer_size", consts.CFG_DEF_UDEV_BUFFER_SIZE)
@@ -53,6 +49,8 @@ def __init__(self, profile_name = None, config = None):
5349
profile_locator = profiles.Locator(self.config.get_list(consts.CFG_PROFILE_DIRS, consts.CFG_DEF_PROFILE_DIRS))
5450
profile_loader = profiles.Loader(profile_locator, profile_factory, profile_merger, self.config, self.variables)
5551

52+
self._configure_dynamic_tuning()
53+
5654
self._daemon = daemon.Daemon(unit_manager, profile_loader, profile_name, self.config, self)
5755
self._controller = controller.Controller(self._daemon, self.config)
5856

@@ -71,6 +69,25 @@ def _init_signals(self):
7169
self._handle_signal(signal.SIGINT, self._controller.terminate)
7270
self._handle_signal(signal.SIGTERM, self._controller.terminate)
7371

72+
def _configure_dynamic_tuning(self, plugins_repository):
73+
if not self.config.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
74+
log.info("dynamic tuning is globally disabled")
75+
self.config.set(consts.CFG_DYNAMIC_PLUGINS, [])
76+
return
77+
all_plugins = {p.name(): p for p in plugins_repository.load_all_plugins()}
78+
all_dynamic_plugins = {name: p for name, p in all_plugins.items() if p.supports_dynamic_tuning()}
79+
enabled_dynamic_plugins = {}
80+
for name in self.config.get_list(consts.CFG_DYNAMIC_PLUGINS, all_dynamic_plugins.keys()):
81+
if name not in all_plugins:
82+
log.warn("Configuring dynamic tuning: Plugin '%s' does not exist" % name)
83+
continue
84+
if name not in all_dynamic_plugins:
85+
log.warn("Configuring dynamic tuning: Plugin '%s' does not support dynamic tuning" % name)
86+
continue
87+
enabled_dynamic_plugins[name] = all_plugins[name]
88+
self.config.set(consts.CFG_DYNAMIC_PLUGINS, enabled_dynamic_plugins.values())
89+
log.info("dynamic tuning is enabled for plugins: %s" % ", ".join(enabled_dynamic_plugins.keys()))
90+
7491
def attach_to_dbus(self, bus_name, object_name, interface_name, namespace):
7592
if self._dbus_exporter is not None:
7693
raise TunedException("DBus interface is already initialized.")

tuned/daemon/controller.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def get_all_plugins(self, caller = None):
290290
return False
291291
plugins = {}
292292
for plugin_class in self._daemon.get_all_plugins():
293-
plugin_name = plugin_class.__module__.split(".")[-1].split("_", 1)[1]
293+
plugin_name = plugin_class.name()
294294
conf_options = plugin_class._get_config_options()
295295
plugins[plugin_name] = {}
296296
for key, val in conf_options.items():
@@ -360,10 +360,10 @@ def instance_acquire_devices(self, devices, instance_name, caller = None):
360360
devs -= devs_moving
361361
log.info("Moving devices '%s' from instance '%s' to instance '%s'." % (str(devs_moving),
362362
instance.name, instance_target.name))
363-
if (instance.plugin.name != instance_target.plugin.name):
363+
if (instance.plugin.name() != instance_target.plugin.name()):
364364
rets = "Target instance '%s' is of type '%s', but devices '%s' are currently handled by " \
365365
"instance '%s' which is of type '%s'." % (instance_target.name,
366-
instance_target.plugin.name, str(devs_moving), instance.name, instance.plugin.name)
366+
instance_target.plugin.name(), str(devs_moving), instance.name, instance.plugin.name())
367367
log.error(rets)
368368
return (False, rets)
369369
instance.plugin._remove_devices_nocheck(instance, devs_moving)
@@ -394,8 +394,8 @@ def get_instances(self, plugin_name, caller = None):
394394
return (False, rets, [])
395395
instances = filter(lambda instance: instance.active, self._daemon._unit_manager.instances)
396396
if plugin_name != "":
397-
instances = filter(lambda instance: instance.plugin.name == plugin_name, instances)
398-
return (True, "OK", list(map(lambda instance: (instance.name, instance.plugin.name), instances)))
397+
instances = filter(lambda instance: instance.plugin.name() == plugin_name, instances)
398+
return (True, "OK", list(map(lambda instance: (instance.name, instance.plugin.name()), instances)))
399399

400400
@exports.export("s", "(bsas)")
401401
def instance_get_devices(self, instance_name, caller = None):

tuned/daemon/daemon.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,28 @@ def __init__(self, unit_manager, profile_loader, profile_names=None, config=None
1919
self._daemon = consts.CFG_DEF_DAEMON
2020
self._sleep_interval = int(consts.CFG_DEF_SLEEP_INTERVAL)
2121
self._update_interval = int(consts.CFG_DEF_UPDATE_INTERVAL)
22-
self._dynamic_tuning = consts.CFG_DEF_DYNAMIC_TUNING
2322
self._recommend_command = True
2423
self._rollback = consts.CFG_DEF_ROLLBACK
24+
dynamic_plugins = []
2525
if config is not None:
2626
self._daemon = config.get_bool(consts.CFG_DAEMON, consts.CFG_DEF_DAEMON)
2727
self._sleep_interval = int(config.get(consts.CFG_SLEEP_INTERVAL, consts.CFG_DEF_SLEEP_INTERVAL))
2828
self._update_interval = int(config.get(consts.CFG_UPDATE_INTERVAL, consts.CFG_DEF_UPDATE_INTERVAL))
29-
self._dynamic_tuning = config.get_bool(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING)
3029
self._recommend_command = config.get_bool(consts.CFG_RECOMMEND_COMMAND, consts.CFG_DEF_RECOMMEND_COMMAND)
3130
self._rollback = config.get(consts.CFG_ROLLBACK, consts.CFG_DEF_ROLLBACK)
31+
dynamic_plugins = config.get(consts.CFG_DYNAMIC_PLUGINS, [])
3232
self._application = application
33+
self._periodic_tuning = any(plugin.uses_periodic_tuning() for plugin in dynamic_plugins)
3334
if self._sleep_interval <= 0:
3435
self._sleep_interval = int(consts.CFG_DEF_SLEEP_INTERVAL)
3536
if self._update_interval == 0:
36-
self._dynamic_tuning = False
37+
self._periodic_tuning = False
3738
elif self._update_interval < self._sleep_interval:
3839
self._update_interval = self._sleep_interval
3940
self._sleep_cycles = self._update_interval // self._sleep_interval
40-
log.info("using sleep interval of %d second(s)" % self._sleep_interval)
41-
if self._dynamic_tuning:
42-
log.info("dynamic tuning is enabled (can be overridden by plugins)")
41+
if self._periodic_tuning:
42+
log.info("using sleep interval of %d second(s)" % self._sleep_interval)
43+
log.info("periodic tuning is enabled")
4344
log.info("using update interval of %d second(s) (%d times of the sleep interval)" % (self._sleep_cycles * self._sleep_interval, self._sleep_cycles))
4445

4546
self._profile_recommender = ProfileRecommender(is_hardcoded = not self._recommend_command)
@@ -215,7 +216,7 @@ def _thread_code(self):
215216
# For more details see TuneD rhbz#917587.
216217
_sleep_cnt = self._sleep_cycles
217218
while not self._cmd.wait(self._terminate, self._sleep_interval):
218-
if self._dynamic_tuning:
219+
if self._periodic_tuning:
219220
_sleep_cnt -= 1
220221
if _sleep_cnt <= 0:
221222
_sleep_cnt = self._sleep_cycles

tuned/plugins/base.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,21 @@ def init_devices(self):
4949
self._init_devices()
5050
self._devices_inited = True
5151

52-
@property
53-
def name(self):
54-
return self.__class__.__module__.split(".")[-1].split("_", 1)[1]
52+
@classmethod
53+
def name(cls):
54+
return cls.__module__.split(".")[-1].split("_", 1)[1]
55+
56+
@classmethod
57+
def supports_static_tuning(cls):
58+
raise NotImplementedError()
59+
60+
@classmethod
61+
def supports_dynamic_tuning(cls):
62+
raise NotImplementedError()
63+
64+
@classmethod
65+
def uses_periodic_tuning(cls):
66+
raise NotImplementedError()
5567

5668
#
5769
# Plugin configuration manipulation and helpers.
@@ -74,8 +86,10 @@ def _get_config_options_used_by_dynamic(self):
7486

7587
def _get_effective_options(self, options):
7688
"""Merge provided options with plugin default options."""
77-
# TODO: _has_dynamic_options is a hack
7889
effective = self._get_config_options().copy()
90+
if self.supports_dynamic_tuning():
91+
effective["dynamic"] = True
92+
# TODO: _has_dynamic_options is a hack
7993
for key in options:
8094
if key in effective or self._has_dynamic_options:
8195
effective[key] = options[key]
@@ -118,13 +132,13 @@ def destroy_instance(self, instance):
118132

119133
def initialize_instance(self, instance):
120134
"""Initialize an instance."""
121-
log.debug("initializing instance %s (%s)" % (instance.name, self.name))
135+
log.debug("initializing instance %s (%s)" % (instance.name, self.name()))
122136
self._instance_init(instance)
123137

124138
def destroy_instances(self):
125139
"""Destroy all instances."""
126140
for instance in list(self._instances.values()):
127-
log.debug("destroying instance %s (%s)" % (instance.name, self.name))
141+
log.debug("destroying instance %s (%s)" % (instance.name, self.name()))
128142
self._destroy_instance(instance)
129143
self._instances.clear()
130144

@@ -133,10 +147,13 @@ def _destroy_instance(self, instance):
133147
self._instance_cleanup(instance)
134148

135149
def _instance_init(self, instance):
136-
raise NotImplementedError()
150+
instance._static_tuning_enabled = self.supports_static_tuning()
151+
instance._dynamic_tuning_enabled = self.supports_dynamic_tuning() \
152+
and self.__class__ in self._global_cfg.get(consts.CFG_DYNAMIC_PLUGINS) \
153+
and instance.options["dynamic"]
137154

138155
def _instance_cleanup(self, instance):
139-
raise NotImplementedError()
156+
pass
140157

141158
#
142159
# Devices handling
@@ -158,7 +175,7 @@ def _get_matching_devices(self, instance, devices):
158175
else:
159176
udev_devices = self._get_device_objects(devices)
160177
if udev_devices is None:
161-
log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name)
178+
log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name())
162179
return set()
163180
udev_devices = self._device_matcher_udev.match_list(instance.devices_udev_regex, udev_devices)
164181
return set([x.sys_name for x in udev_devices])
@@ -174,8 +191,8 @@ def assign_free_devices(self, instance):
174191
log.warn("instance %s: no matching devices available" % instance.name)
175192
else:
176193
name = instance.name
177-
if instance.name != self.name:
178-
name += " (%s)" % self.name
194+
if instance.name != self.name():
195+
name += " (%s)" % self.name()
179196
log.info("instance %s: assigning devices %s" % (name, ", ".join(to_assign)))
180197
instance.assigned_devices.update(to_assign) # cannot use |=
181198
self._assigned_devices |= to_assign
@@ -254,15 +271,15 @@ def instance_apply_tuning(self, instance):
254271
if not instance.active:
255272
return
256273

257-
if instance.has_static_tuning:
274+
if instance.static_tuning_enabled:
258275
self._call_device_script(instance, instance.script_pre,
259276
"apply", instance.assigned_devices)
260277
self._instance_pre_static(instance, True)
261278
self._instance_apply_static(instance)
262279
self._instance_post_static(instance, True)
263280
self._call_device_script(instance, instance.script_post,
264281
"apply", instance.assigned_devices)
265-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
282+
if instance.dynamic_tuning_enabled:
266283
self._instance_init_dynamic(instance)
267284
self._run_for_each_device(instance, self._instance_apply_dynamic, instance.assigned_devices)
268285
instance.processed_devices.update(instance.assigned_devices)
@@ -279,7 +296,7 @@ def instance_verify_tuning(self, instance, ignore_missing):
279296
log.error("BUG: Some devices have not been tuned: %s"
280297
% ", ".join(instance.assigned_devices))
281298
devices = instance.processed_devices.copy()
282-
if instance.has_static_tuning:
299+
if instance.static_tuning_enabled:
283300
if self._call_device_script(instance, instance.script_pre, "verify", devices) == False:
284301
return False
285302
if self._instance_verify_static(instance, ignore_missing, devices) == False:
@@ -296,7 +313,7 @@ def instance_update_tuning(self, instance):
296313
"""
297314
if not instance.active:
298315
return
299-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
316+
if instance.dynamic_tuning_enabled and self.uses_periodic_tuning():
300317
self._run_for_each_device(instance, self._instance_update_dynamic, instance.processed_devices.copy())
301318

302319
def instance_unapply_tuning(self, instance, rollback = consts.ROLLBACK_SOFT):
@@ -306,9 +323,10 @@ def instance_unapply_tuning(self, instance, rollback = consts.ROLLBACK_SOFT):
306323
if rollback == consts.ROLLBACK_NONE:
307324
return
308325

309-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
326+
if instance.dynamic_tuning_enabled:
310327
self._run_for_each_device(instance, self._instance_unapply_dynamic, instance.processed_devices)
311-
if instance.has_static_tuning:
328+
self._instance_deinit_dynamic(instance)
329+
if instance.static_tuning_enabled:
312330
self._call_device_script(instance, instance.script_post,
313331
"unapply", instance.processed_devices,
314332
rollback = rollback)
@@ -337,14 +355,17 @@ def _instance_unapply_static(self, instance, rollback = consts.ROLLBACK_SOFT):
337355
def _instance_init_dynamic(self, instance):
338356
pass
339357

358+
def _instance_deinit_dynamic(self, instance):
359+
pass
360+
340361
def _instance_apply_dynamic(self, instance, device):
341362
for option in [opt for opt in self._options_used_by_dynamic if self._storage_get(instance, self._commands[opt], device) is None]:
342363
self._check_and_save_value(instance, self._commands[option], device)
343-
344-
self._instance_update_dynamic(instance, device)
364+
if self.uses_periodic_tuning():
365+
self._instance_update_dynamic(instance, device)
345366

346367
def _instance_unapply_dynamic(self, instance, device):
347-
raise NotImplementedError()
368+
pass
348369

349370
def _instance_update_dynamic(self, instance, device):
350371
raise NotImplementedError()

tuned/plugins/hotplug.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ def _remove_devices_nocheck(self, instance, device_names):
103103

104104
def _added_device_apply_tuning(self, instance, device_name):
105105
self._execute_all_device_commands(instance, [device_name])
106-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
106+
if instance.dynamic_tuning_enabled:
107107
self._instance_apply_dynamic(instance, device_name)
108108

109109
def _removed_device_unapply_tuning(self, instance, device_name):
110-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
110+
if instance.dynamic_tuning_enabled:
111111
self._instance_unapply_dynamic(instance, device_name)
112112
self._cleanup_all_device_commands(instance, [device_name], remove = True)

tuned/plugins/instance/instance.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def __init__(self, plugin, name, devices_expression, devices_udev_regex, script_
1414
self._options = options
1515

1616
self._active = True
17-
self._has_static_tuning = False
18-
self._has_dynamic_tuning = False
17+
self._static_tuning_enabled = False
18+
self._dynamic_tuning_enabled = False
1919
self._assigned_devices = set()
2020
self._processed_devices = set()
2121

@@ -67,12 +67,12 @@ def options(self):
6767
return self._options
6868

6969
@property
70-
def has_static_tuning(self):
71-
return self._has_static_tuning
70+
def static_tuning_enabled(self):
71+
return self._static_tuning_enabled
7272

7373
@property
74-
def has_dynamic_tuning(self):
75-
return self._has_dynamic_tuning
74+
def dynamic_tuning_enabled(self):
75+
return self._dynamic_tuning_enabled
7676

7777
# methods
7878

tuned/plugins/plugin_acpi.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ def __init__(self, *args, **kwargs):
3737
super(ACPIPlugin, self).__init__(*args, **kwargs)
3838

3939
@classmethod
40-
def _get_config_options(cls):
41-
return {"platform_profile": None}
40+
def supports_static_tuning(cls):
41+
return True
4242

43-
def _instance_init(self, instance):
44-
instance._has_static_tuning = True
45-
instance._has_dynamic_tuning = False
43+
@classmethod
44+
def supports_dynamic_tuning(cls):
45+
return False
4646

47-
def _instance_cleanup(self, instance):
48-
pass
47+
@classmethod
48+
def _get_config_options(cls):
49+
return {"platform_profile": None}
4950

5051
@classmethod
5152
def _platform_profile_choices_path(cls):

0 commit comments

Comments
 (0)