Skip to content

Commit 4d78ebf

Browse files
committed
Refactor per-plugin dynamic tuning configuration
Add a new global option "dynamic_plugins" which can be used to globally enable dynamic tuning only for specific plugins. Also unify the behavior of the "dynamic" profile option for plugins with dynamic capabilities. All such plugins now have this option and it is set to True by default. Dynamic tuning is then enabled for a specific plugin if it is allowed both globally and locally.
1 parent 5512f1d commit 4d78ebf

31 files changed

+287
-166
lines changed

tests/unit/plugins/test_base.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ def __init__(self,*args,**kwargs):
169169
super(DummyPlugin,self).__init__(*args,**kwargs)
170170
self.cleaned_instances = []
171171

172+
@classmethod
173+
def supports_static_tuning(cls):
174+
return True
175+
176+
@classmethod
177+
def supports_dynamic_tuning(cls):
178+
return False
179+
172180
@classmethod
173181
def _get_config_options(self):
174182
return {'default_option1':'default_value1',\
@@ -205,6 +213,14 @@ def __init__(self,*args,**kwargs):
205213
super(CommandsPlugin,self).__init__(*args,**kwargs)
206214
self._size = 'S'
207215

216+
@classmethod
217+
def supports_static_tuning(cls):
218+
return True
219+
220+
@classmethod
221+
def supports_dynamic_tuning(cls):
222+
return False
223+
208224
@classmethod
209225
def _get_config_options(self):
210226
"""Default configuration options for the plugin."""

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: 31 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(plugins_repository)
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,35 @@ 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+
# First check if the global `dynamic_tuning` option is set to False,
74+
# disabling dynamic tuning for all plugins
75+
if not self.config.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
76+
log.info("dynamic tuning is globally disabled")
77+
self.config.set(consts.CFG_DYNAMIC_PLUGINS, [])
78+
return
79+
# If the global `dynamic_tuning` is True, check the `dynamic_plugins` option, which
80+
# restricts the dynamic tuning to selected plugins only (if present)
81+
all_plugins = {p.name(): p for p in plugins_repository.load_all_plugins()}
82+
all_dynamic_plugins = {name: p for name, p in all_plugins.items() if p.supports_dynamic_tuning()}
83+
# If it's not present, we enable dynamic tuning where possible
84+
if self.config.get(consts.CFG_DYNAMIC_PLUGINS) is None:
85+
self.config.set(consts.CFG_DYNAMIC_PLUGINS, all_dynamic_plugins.values())
86+
log.info("dynamic tuning is enabled for all plugins which support it")
87+
return
88+
# Otherwise only to the specified plugins
89+
enabled_dynamic_plugins = {}
90+
for name in self.config.get_list(consts.CFG_DYNAMIC_PLUGINS):
91+
if name not in all_plugins:
92+
log.warn("Configuring dynamic tuning: Plugin '%s' does not exist" % name)
93+
continue
94+
if name not in all_dynamic_plugins:
95+
log.warn("Configuring dynamic tuning: Plugin '%s' does not support dynamic tuning" % name)
96+
continue
97+
enabled_dynamic_plugins[name] = all_plugins[name]
98+
self.config.set(consts.CFG_DYNAMIC_PLUGINS, enabled_dynamic_plugins.values())
99+
log.info("dynamic tuning is enabled for plugins: %s" % ", ".join(enabled_dynamic_plugins.keys()))
100+
74101
def attach_to_dbus(self, bus_name, object_name, interface_name, namespace):
75102
if self._dbus_exporter is not None:
76103
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
@@ -297,7 +297,7 @@ def get_all_plugins(self, caller = None):
297297
return False
298298
plugins = {}
299299
for plugin_class in self._daemon.get_all_plugins():
300-
plugin_name = plugin_class.__module__.split(".")[-1].split("_", 1)[1]
300+
plugin_name = plugin_class.name()
301301
conf_options = plugin_class._get_config_options()
302302
plugins[plugin_name] = {}
303303
for key, val in conf_options.items():
@@ -371,10 +371,10 @@ def instance_acquire_devices(self, devices, instance_name, caller = None):
371371
devs -= devs_moving
372372
log.info("Moving devices '%s' from instance '%s' to instance '%s'." % (str(devs_moving),
373373
instance.name, instance_target.name))
374-
if (instance.plugin.name != instance_target.plugin.name):
374+
if (instance.plugin.name() != instance_target.plugin.name()):
375375
rets = "Target instance '%s' is of type '%s', but devices '%s' are currently handled by " \
376376
"instance '%s' which is of type '%s'." % (instance_target.name,
377-
instance_target.plugin.name, str(devs_moving), instance.name, instance.plugin.name)
377+
instance_target.plugin.name(), str(devs_moving), instance.name, instance.plugin.name())
378378
log.error(rets)
379379
return (False, rets)
380380
instance.plugin._remove_devices_nocheck(instance, devs_moving)
@@ -405,8 +405,8 @@ def get_instances(self, plugin_name, caller = None):
405405
return (False, rets, [])
406406
instances = filter(lambda instance: instance.active, self._daemon._unit_manager.instances)
407407
if plugin_name != "":
408-
instances = filter(lambda instance: instance.plugin.name == plugin_name, instances)
409-
return (True, "OK", list(map(lambda instance: (instance.name, instance.plugin.name), instances)))
408+
instances = filter(lambda instance: instance.plugin.name() == plugin_name, instances)
409+
return (True, "OK", list(map(lambda instance: (instance.name, instance.plugin.name()), instances)))
410410

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

tuned/plugins/base.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,17 @@ 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()
5563

5664
#
5765
# Plugin configuration manipulation and helpers.
@@ -74,8 +82,10 @@ def _get_config_options_used_by_dynamic(self):
7482

7583
def _get_effective_options(self, options):
7684
"""Merge provided options with plugin default options."""
77-
# TODO: _has_dynamic_options is a hack
7885
effective = self._get_config_options().copy()
86+
if self.supports_dynamic_tuning():
87+
effective["dynamic"] = True
88+
# TODO: _has_dynamic_options is a hack
7989
for key in options:
8090
if key in effective or self._has_dynamic_options:
8191
effective[key] = options[key]
@@ -119,13 +129,13 @@ def destroy_instance(self, instance):
119129

120130
def initialize_instance(self, instance):
121131
"""Initialize an instance."""
122-
log.debug("initializing instance %s (%s)" % (instance.name, self.name))
132+
log.debug("initializing instance %s (%s)" % (instance.name, self.name()))
123133
self._instance_init(instance)
124134

125135
def destroy_instances(self):
126136
"""Destroy all instances."""
127137
for instance in list(self._instances.values()):
128-
log.debug("destroying instance %s (%s)" % (instance.name, self.name))
138+
log.debug("destroying instance %s (%s)" % (instance.name, self.name()))
129139
self._destroy_instance(instance)
130140
self._instances.clear()
131141

@@ -134,10 +144,13 @@ def _destroy_instance(self, instance):
134144
self._instance_cleanup(instance)
135145

136146
def _instance_init(self, instance):
137-
raise NotImplementedError()
147+
instance._static_tuning_enabled = self.supports_static_tuning()
148+
instance._dynamic_tuning_enabled = self.supports_dynamic_tuning() \
149+
and self.__class__ in self._global_cfg.get(consts.CFG_DYNAMIC_PLUGINS) \
150+
and self._option_bool(instance.options["dynamic"])
138151

139152
def _instance_cleanup(self, instance):
140-
raise NotImplementedError()
153+
pass
141154

142155
#
143156
# Devices handling
@@ -159,7 +172,7 @@ def _get_matching_devices(self, instance, devices):
159172
else:
160173
udev_devices = self._get_device_objects(devices)
161174
if udev_devices is None:
162-
log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name)
175+
log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name())
163176
return set()
164177
udev_devices = self._device_matcher_udev.match_list(instance.devices_udev_regex, udev_devices)
165178
return set([x.sys_name for x in udev_devices])
@@ -175,8 +188,8 @@ def assign_free_devices(self, instance):
175188
log.warn("instance %s: no matching devices available" % instance.name)
176189
else:
177190
name = instance.name
178-
if instance.name != self.name:
179-
name += " (%s)" % self.name
191+
if instance.name != self.name():
192+
name += " (%s)" % self.name()
180193
log.info("instance %s: assigning devices %s" % (name, ", ".join(to_assign)))
181194
instance.assigned_devices.update(to_assign) # cannot use |=
182195
self._assigned_devices |= to_assign
@@ -255,15 +268,15 @@ def instance_apply_tuning(self, instance):
255268
if not instance.active:
256269
return
257270

258-
if instance.has_static_tuning:
271+
if instance.static_tuning_enabled:
259272
self._call_device_script(instance, instance.script_pre,
260273
"apply", instance.assigned_devices)
261274
self._instance_pre_static(instance, True)
262275
self._instance_apply_static(instance)
263276
self._instance_post_static(instance, True)
264277
self._call_device_script(instance, instance.script_post,
265278
"apply", instance.assigned_devices)
266-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
279+
if instance.dynamic_tuning_enabled:
267280
self._instance_init_dynamic(instance)
268281
self._run_for_each_device(instance, self._instance_apply_dynamic, instance.assigned_devices)
269282
instance.processed_devices.update(instance.assigned_devices)
@@ -280,7 +293,7 @@ def instance_verify_tuning(self, instance, ignore_missing):
280293
log.error("BUG: Some devices have not been tuned: %s"
281294
% ", ".join(instance.assigned_devices))
282295
devices = instance.processed_devices.copy()
283-
if instance.has_static_tuning:
296+
if instance.static_tuning_enabled:
284297
if self._call_device_script(instance, instance.script_pre, "verify", devices) == False:
285298
return False
286299
if self._instance_verify_static(instance, ignore_missing, devices) == False:
@@ -297,7 +310,7 @@ def instance_update_tuning(self, instance):
297310
"""
298311
if not instance.active:
299312
return
300-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
313+
if instance.dynamic_tuning_enabled:
301314
self._run_for_each_device(instance, self._instance_update_dynamic, instance.processed_devices.copy())
302315

303316
def instance_unapply_tuning(self, instance, rollback = consts.ROLLBACK_SOFT):
@@ -307,9 +320,9 @@ def instance_unapply_tuning(self, instance, rollback = consts.ROLLBACK_SOFT):
307320
if rollback == consts.ROLLBACK_NONE:
308321
return
309322

310-
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
323+
if instance.dynamic_tuning_enabled:
311324
self._run_for_each_device(instance, self._instance_unapply_dynamic, instance.processed_devices)
312-
if instance.has_static_tuning:
325+
if instance.static_tuning_enabled:
313326
self._call_device_script(instance, instance.script_post,
314327
"unapply", instance.processed_devices,
315328
rollback = rollback)
@@ -345,7 +358,7 @@ def _instance_apply_dynamic(self, instance, device):
345358
self._instance_update_dynamic(instance, device)
346359

347360
def _instance_unapply_dynamic(self, instance, device):
348-
raise NotImplementedError()
361+
pass
349362

350363
def _instance_update_dynamic(self, instance, device):
351364
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
@@ -15,8 +15,8 @@ def __init__(self, plugin, name, priority, devices_expression, devices_udev_rege
1515

1616
self._active = True
1717
self._priority = priority
18-
self._has_static_tuning = False
19-
self._has_dynamic_tuning = False
18+
self._static_tuning_enabled = False
19+
self._dynamic_tuning_enabled = False
2020
self._assigned_devices = set()
2121
self._processed_devices = set()
2222

@@ -72,12 +72,12 @@ def options(self):
7272
return self._options
7373

7474
@property
75-
def has_static_tuning(self):
76-
return self._has_static_tuning
75+
def static_tuning_enabled(self):
76+
return self._static_tuning_enabled
7777

7878
@property
79-
def has_dynamic_tuning(self):
80-
return self._has_dynamic_tuning
79+
def dynamic_tuning_enabled(self):
80+
return self._dynamic_tuning_enabled
8181

8282
# methods
8383

tuned/plugins/plugin_acpi.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,18 @@ class ACPIPlugin(base.Plugin):
3333
====
3434
--
3535
"""
36-
def __init__(self, *args, **kwargs):
37-
super(ACPIPlugin, self).__init__(*args, **kwargs)
3836

3937
@classmethod
40-
def _get_config_options(cls):
41-
return {"platform_profile": None}
38+
def supports_static_tuning(cls):
39+
return True
4240

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

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

5049
@classmethod
5150
def _platform_profile_choices_path(cls):

tuned/plugins/plugin_audio.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ class AudioPlugin(hotplug.Plugin):
3737
====
3838
"""
3939

40+
@classmethod
41+
def supports_static_tuning(cls):
42+
return True
43+
44+
@classmethod
45+
def supports_dynamic_tuning(cls):
46+
return False
47+
4048
def _init_devices(self):
4149
self._devices_supported = True
4250
self._assigned_devices = set()
@@ -47,13 +55,6 @@ def _init_devices(self):
4755
if module_name in ["snd_hda_intel", "snd_ac97_codec"]:
4856
self._free_devices.add(module_name)
4957

50-
def _instance_init(self, instance):
51-
instance._has_static_tuning = True
52-
instance._has_dynamic_tuning = False
53-
54-
def _instance_cleanup(self, instance):
55-
pass
56-
5758
def _device_module_name(self, device):
5859
try:
5960
return device.parent.driver

tuned/plugins/plugin_bootloader.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,16 @@ def __init__(self, *args, **kwargs):
193193
super(BootloaderPlugin, self).__init__(*args, **kwargs)
194194
self._cmd = commands()
195195

196+
@classmethod
197+
def supports_static_tuning(cls):
198+
return True
199+
200+
@classmethod
201+
def supports_dynamic_tuning(cls):
202+
return False
203+
196204
def _instance_init(self, instance):
197-
instance._has_dynamic_tuning = False
198-
instance._has_static_tuning = True
205+
super(BootloaderPlugin, self)._instance_init(instance)
199206
# controls grub2_cfg rewrites in _instance_post_static
200207
self.update_grub2_cfg = False
201208
self._skip_grub_config_val = False
@@ -208,9 +215,6 @@ def _instance_init(self, instance):
208215

209216
self._rpm_ostree = self._rpm_ostree_status() is not None
210217

211-
def _instance_cleanup(self, instance):
212-
pass
213-
214218
@classmethod
215219
def _get_config_options(cls):
216220
return {

0 commit comments

Comments
 (0)