From 363d74815ac554b3c0ff85a28466545510994dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= Date: Fri, 17 Feb 2017 14:24:53 +0100 Subject: [PATCH] Implement instance priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: rhbz#1246172 Allow specifying order in which individual plugin instances are to be executed. Each instance can be given the 'instance_priority' option in the profile configuration. The value must be an integer (it can be negative). If no priority is specified, it is inherited from the parent profile, or set to the default instance priority, if no parent profile specifies the priority for that instance. The default instance priority is by default zero, but it can be set to a different value using the 'default_instance_priority' option in the global configuration file. Creating plugin instances, assigning devices to instances, initializing instances, applying, updating and verifying tuning are all done in order from lowest numerical instance priority to highest. Unapplying tuning and destroying instances is done in reversed order. Signed-off-by: Ondřej Lysoněk --- tuned-main.conf | 3 ++ tuned/consts.py | 3 ++ tuned/daemon/application.py | 3 +- tuned/plugins/base.py | 32 +++++++++---------- tuned/plugins/instance/instance.py | 4 +++ tuned/units/manager.py | 50 ++++++++++++++++-------------- 6 files changed, 54 insertions(+), 41 deletions(-) diff --git a/tuned-main.conf b/tuned-main.conf index ceaf3e5..22cd64b 100644 --- a/tuned-main.conf +++ b/tuned-main.conf @@ -26,3 +26,6 @@ recommend_command = 1 # If enabled these sysctls will be re-appliead after Tuned sysctls are # applied, i.e. Tuned sysctls will not override system sysctls. reapply_sysctl = 1 + +# Default priority assigned to instances +default_instance_priority = 0 diff --git a/tuned/consts.py b/tuned/consts.py index 8beeb9a..2d06765 100644 --- a/tuned/consts.py +++ b/tuned/consts.py @@ -56,6 +56,7 @@ CFG_SLEEP_INTERVAL = "sleep_interval" CFG_UPDATE_INTERVAL = "update_interval" CFG_RECOMMEND_COMMAND = "recommend_command" CFG_REAPPLY_SYSCTL = "reapply_sysctl" +CFG_DEFAULT_INSTANCE_PRIORITY = "default_instance_priority" # no_daemon mode CFG_DEF_DAEMON = True @@ -69,6 +70,8 @@ CFG_DEF_UPDATE_INTERVAL = 10 CFG_DEF_RECOMMEND_COMMAND = True # reapply system sysctl CFG_DEF_REAPPLY_SYSCTL = True +# default instance priority +CFG_DEF_DEFAULT_INSTANCE_PRIORITY = 0 PATH_CPU_DMA_LATENCY = "/dev/cpu_dma_latency" diff --git a/tuned/daemon/application.py b/tuned/daemon/application.py index a7708ae..2877709 100644 --- a/tuned/daemon/application.py +++ b/tuned/daemon/application.py @@ -34,7 +34,8 @@ class Application(object): log.info("dynamic tuning is globally disabled") plugins_repository = plugins.Repository(monitors_repository, storage_factory, hardware_inventory, device_matcher, plugin_instance_factory, self.config, self.variables) - unit_manager = units.Manager(plugins_repository, monitors_repository) + def_instance_priority = int(self.config.get(consts.CFG_DEFAULT_INSTANCE_PRIORITY, consts.CFG_DEF_DEFAULT_INSTANCE_PRIORITY)) + unit_manager = units.Manager(plugins_repository, monitors_repository, def_instance_priority) profile_factory = profiles.Factory() profile_merger = profiles.Merger() diff --git a/tuned/plugins/base.py b/tuned/plugins/base.py index b07f0a4..7079d93 100644 --- a/tuned/plugins/base.py +++ b/tuned/plugins/base.py @@ -102,11 +102,10 @@ class Plugin(object): self._destroy_instance(instance) del self._instances[instance.name] - def initialize_instances(self): - """Initialize all created instances.""" - for (instance_name, instance) in self._instances.items(): - log.debug("initializing instance %s (%s)" % (instance_name, self.name)) - self._instance_init(instance) + def initialize_instance(self, instance): + """Initialize an instance.""" + log.debug("initializing instance %s (%s)" % (instance.name, self.name)) + self._instance_init(instance) def destroy_instances(self): """Destroy all instances.""" @@ -140,21 +139,20 @@ class Plugin(object): def _get_matching_devices(self, instance, devices): return set(self._device_matcher.match_list(instance.devices_expression, devices)) - def assign_free_devices(self): + def assign_free_devices(self, instance): if not self._devices_supported(): return - log.debug("assigning devices to all instances") - for instance_name, instance in reversed(self._instances.items()): - to_assign = self._get_matching_devices(instance, self._free_devices) - instance.active = len(to_assign) > 0 - if not instance.active: - log.warn("instance %s: no matching devices available" % instance_name) - else: - log.info("instance %s: assigning devices %s" % (instance_name, ", ".join(to_assign))) - instance.devices.update(to_assign) # cannot use |= - self._assigned_devices |= to_assign - self._free_devices -= to_assign + log.debug("assigning devices to instance %s" % instance.name) + to_assign = self._get_matching_devices(instance, self._free_devices) + instance.active = len(to_assign) > 0 + if not instance.active: + log.warn("instance %s: no matching devices available" % instance.name) + else: + log.info("instance %s: assigning devices %s" % (instance.name, ", ".join(to_assign))) + instance.devices.update(to_assign) # cannot use |= + self._assigned_devices |= to_assign + self._free_devices -= to_assign def release_devices(self, instance): if not self._devices_supported(): diff --git a/tuned/plugins/instance/instance.py b/tuned/plugins/instance/instance.py index d012cb5..be0d6ba 100644 --- a/tuned/plugins/instance/instance.py +++ b/tuned/plugins/instance/instance.py @@ -15,6 +15,10 @@ class Instance(object): # properties + @property + def plugin(self): + return self._plugin + @property def name(self): return self._name diff --git a/tuned/units/manager.py b/tuned/units/manager.py index 1097063..2dd5600 100644 --- a/tuned/units/manager.py +++ b/tuned/units/manager.py @@ -1,3 +1,4 @@ +import collections import tuned.exceptions import tuned.logs import tuned.plugins.exceptions @@ -11,10 +12,11 @@ class Manager(object): Manager creates plugin instances and keeps a track of them. """ - def __init__(self, plugins_repository, monitors_repository): + def __init__(self, plugins_repository, monitors_repository, def_instance_priority): super(self.__class__, self).__init__() self._plugins_repository = plugins_repository self._monitors_repository = monitors_repository + self._def_instance_priority = def_instance_priority self._instances = [] self._plugins = [] @@ -27,22 +29,25 @@ class Manager(object): return self._instances def create(self, instances_config): - - # group instances by plugin - - instances_by_plugin = {} + instance_info_list = [] for instance_name, instance_info in instances_config.items(): if not instance_info.enabled: log.debug("skipping disabled instance '%s'" % instance_name) continue - instances_by_plugin.setdefault(instance_info.type, []) - instances_by_plugin[instance_info.type].append(instance_info) + instance_info.options.setdefault("instance_priority", self._def_instance_priority) + instance_info.options["instance_priority"] = int(instance_info.options["instance_priority"]) + instance_info_list.append(instance_info) - # create all plugin instances at once + instance_info_list.sort(key=lambda x: x.options["instance_priority"]) + plugins_by_name = collections.OrderedDict() + for instance_info in instance_info_list: + instance_info.options.pop("instance_priority") + plugins_by_name[instance_info.type] = None - for plugin_name, instances_info in instances_by_plugin.items(): + for plugin_name, none in plugins_by_name.items(): try: plugin = self._plugins_repository.create(plugin_name) + plugins_by_name[plugin_name] = plugin self._plugins.append(plugin) except tuned.plugins.exceptions.NotSupportedPluginException: log.info("skipping plugin '%s', not supported on your system" % plugin_name) @@ -52,21 +57,20 @@ class Manager(object): log.exception(e) continue - created_instances = [] - for instance_info in instances_info: - log.debug("creating '%s' (%s)" % (instance_info.name, instance_info.type)) - new_instance = plugin.create_instance(instance_info.name, instance_info.devices, instance_info.options) - created_instances.append(new_instance) - - plugin.assign_free_devices() - plugin.initialize_instances() - - self._instances.extend(created_instances) + for instance_info in instance_info_list: + plugin = plugins_by_name[instance_info.type] + if plugin is None: + continue + log.debug("creating '%s' (%s)" % (instance_info.name, instance_info.type)) + new_instance = plugin.create_instance(instance_info.name, instance_info.devices, instance_info.options) + plugin.assign_free_devices(new_instance) + plugin.initialize_instance(new_instance) + self._instances.append(new_instance) def destroy_all(self): - for plugin in self._plugins: - log.debug("cleaning plugin '%s'" % plugin.name) - plugin.cleanup() + for instance in self._instances: + log.debug("destroying instance %s" % instance.name) + instance.plugin.destroy_instance(instance) del self._plugins[:] del self._instances[:] @@ -93,5 +97,5 @@ class Manager(object): # profile_switch is helper telling plugins whether the stop is due to profile switch def stop_tuning(self, profile_switch = False): - for instance in self._instances: + for instance in reversed(self._instances): instance.unapply_tuning(profile_switch)