diff --git a/tuned/daemon/application.py b/tuned/daemon/application.py index 2877709..61cdcf5 100644 --- a/tuned/daemon/application.py +++ b/tuned/daemon/application.py @@ -24,6 +24,7 @@ class Application(object): monitors_repository = monitors.Repository() hardware_inventory = hardware.Inventory() device_matcher = hardware.DeviceMatcher() + device_matcher_udev = hardware.DeviceMatcherUdev() plugin_instance_factory = plugins.instance.Factory() self.variables = profiles.variables.Variables() @@ -33,7 +34,8 @@ class Application(object): else: 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) + plugins_repository = plugins.Repository(monitors_repository, storage_factory, hardware_inventory,\ + device_matcher, device_matcher_udev, plugin_instance_factory, self.config, self.variables) 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) diff --git a/tuned/gtk/gui_plugin_loader.py b/tuned/gtk/gui_plugin_loader.py index 67fa195..8966fe8 100644 --- a/tuned/gtk/gui_plugin_loader.py +++ b/tuned/gtk/gui_plugin_loader.py @@ -71,6 +71,7 @@ class GuiPluginLoader(PluginLoader): monitors_repository = monitors.Repository() hardware_inventory = hardware.Inventory() device_matcher = hardware.DeviceMatcher() + device_matcher_udev = hardware.DeviceMatcherUdev() plugin_instance_factory = plugins.instance.Factory() self.repo = repository.Repository( @@ -78,6 +79,7 @@ class GuiPluginLoader(PluginLoader): storage_factory, hardware_inventory, device_matcher, + device_matcher_udev, plugin_instance_factory, None, None diff --git a/tuned/hardware/__init__.py b/tuned/hardware/__init__.py index b3c9af9..afe36b9 100644 --- a/tuned/hardware/__init__.py +++ b/tuned/hardware/__init__.py @@ -1,2 +1,3 @@ from inventory import * from device_matcher import * +from device_matcher_udev import * diff --git a/tuned/hardware/device_matcher_udev.py b/tuned/hardware/device_matcher_udev.py new file mode 100644 index 0000000..098ad1c --- /dev/null +++ b/tuned/hardware/device_matcher_udev.py @@ -0,0 +1,18 @@ +import device_matcher +import re + +__all__ = ["DeviceMatcherUdev"] + +class DeviceMatcherUdev(device_matcher.DeviceMatcher): + def match(self, regex, device): + """ + Match a device against the udev regex in tuning profiles. + + device is a pyudev.Device object + """ + + properties = '' + for key, val in device.items(): + properties += key + '=' + val + '\n' + + return re.search(regex, properties, re.MULTILINE) is not None diff --git a/tuned/hardware/inventory.py b/tuned/hardware/inventory.py index 665c00c..7a24348 100644 --- a/tuned/hardware/inventory.py +++ b/tuned/hardware/inventory.py @@ -28,6 +28,10 @@ class Inventory(object): self._subscriptions = {} + def get_device(self, subsystem, sys_name): + """Get a pyudev.Device object for the sys_name (e.g. 'sda').""" + return pyudev.Devices.from_name(self._udev_context, subsystem, sys_name) + def get_devices(self, subsystem): """Get list of devices on a given subsystem.""" return self._udev_context.list_devices(subsystem=subsystem) diff --git a/tuned/plugins/base.py b/tuned/plugins/base.py index 6bf9893..445d97a 100644 --- a/tuned/plugins/base.py +++ b/tuned/plugins/base.py @@ -17,13 +17,14 @@ class Plugin(object): Intentionally a lot of logic is included in the plugin to increase plugin flexibility. """ - def __init__(self, monitors_repository, storage_factory, hardware_inventory, device_matcher, instance_factory, global_cfg, variables): + def __init__(self, monitors_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, instance_factory, global_cfg, variables): """Plugin constructor.""" self._storage = storage_factory.create(self.__class__.__name__) self._monitors_repository = monitors_repository self._hardware_inventory = hardware_inventory self._device_matcher = device_matcher + self._device_matcher_udev = device_matcher_udev self._instance_factory = instance_factory self._instances = collections.OrderedDict() @@ -80,13 +81,13 @@ class Plugin(object): # Interface for manipulation with instances of the plugin. # - def create_instance(self, name, devices_expression, options): + def create_instance(self, name, devices_expression, devices_udev_regex, options): """Create new instance of the plugin and seize the devices.""" if name in self._instances: raise Exception("Plugin instance with name '%s' already exists." % name) effective_options = self._get_effective_options(options) - instance = self._instance_factory.create(self, name, devices_expression, effective_options) + instance = self._instance_factory.create(self, name, devices_expression, devices_udev_regex, effective_options) self._instances[name] = instance return instance @@ -133,8 +134,21 @@ class Plugin(object): self._assigned_devices = set() self._free_devices = set() + def _get_device_objects(self, devices): + """Override this in a subclass to transform a list of device names (e.g. ['sda']) + to a list of pyudev.Device objects, if your plugin supports it""" + return None + def _get_matching_devices(self, instance, devices): - return set(self._device_matcher.match_list(instance.devices_expression, devices)) + if instance.devices_udev_regex is None: + return set(self._device_matcher.match_list(instance.devices_expression, devices)) + else: + udev_devices = self._get_device_objects(devices) + if udev_devices is None: + log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name) + return set() + udev_devices = self._device_matcher_udev.match_list(instance.devices_udev_regex, udev_devices) + return set(map(lambda x: x.sys_name, udev_devices)) def assign_free_devices(self, instance): if not self._devices_supported: diff --git a/tuned/plugins/instance/instance.py b/tuned/plugins/instance/instance.py index be0d6ba..8006ab9 100644 --- a/tuned/plugins/instance/instance.py +++ b/tuned/plugins/instance/instance.py @@ -2,10 +2,11 @@ class Instance(object): """ """ - def __init__(self, plugin, name, devices_expression, options): + def __init__(self, plugin, name, devices_expression, devices_udev_regex, options): self._plugin = plugin self._name = name self._devices_expression = devices_expression + self._devices_udev_regex = devices_udev_regex self._options = options self._active = True @@ -40,6 +41,10 @@ class Instance(object): def devices(self): return self._devices + @property + def devices_udev_regex(self): + return self._devices_udev_regex + @property def options(self): return self._options diff --git a/tuned/plugins/plugin_cpu.py b/tuned/plugins/plugin_cpu.py index efea02f..59a5371 100644 --- a/tuned/plugins/plugin_cpu.py +++ b/tuned/plugins/plugin_cpu.py @@ -40,6 +40,9 @@ class CPULatencyPlugin(base.Plugin): self._assigned_devices = set() + def _get_device_objects(self, devices): + return map(lambda x: self._hardware_inventory.get_device("cpu", x), devices) + @classmethod def _get_config_options(self): return { diff --git a/tuned/plugins/plugin_disk.py b/tuned/plugins/plugin_disk.py index f98cca4..4192323 100644 --- a/tuned/plugins/plugin_disk.py +++ b/tuned/plugins/plugin_disk.py @@ -33,6 +33,9 @@ class DiskPlugin(hotplug.Plugin): self._assigned_devices = set() + def _get_device_objects(self, devices): + return map(lambda x: self._hardware_inventory.get_device("block", x), devices) + @classmethod def _device_is_supported(cls, device): return device.device_type == "disk" and \ diff --git a/tuned/plugins/plugin_net.py b/tuned/plugins/plugin_net.py index c1bf3ea..cc5bca1 100644 --- a/tuned/plugins/plugin_net.py +++ b/tuned/plugins/plugin_net.py @@ -33,6 +33,9 @@ class NetTuningPlugin(base.Plugin): log.debug("devices: %s" % str(self._free_devices)); + def _get_device_objects(self, devices): + return map(lambda x: self._hardware_inventory.get_device("net", x), devices) + def _instance_init(self, instance): instance._has_static_tuning = True instance._has_dynamic_tuning = True diff --git a/tuned/plugins/plugin_scsi_host.py b/tuned/plugins/plugin_scsi_host.py index a2014cb..328b633 100644 --- a/tuned/plugins/plugin_scsi_host.py +++ b/tuned/plugins/plugin_scsi_host.py @@ -28,6 +28,9 @@ class DiskPlugin(hotplug.Plugin): self._assigned_devices = set() + def _get_device_objects(self, devices): + return map(lambda x: self._hardware_inventory.get_device("scsi", x), devices) + @classmethod def _device_is_supported(cls, device): return device.device_type == "scsi_host" diff --git a/tuned/plugins/plugin_usb.py b/tuned/plugins/plugin_usb.py index 021220f..504ee71 100644 --- a/tuned/plugins/plugin_usb.py +++ b/tuned/plugins/plugin_usb.py @@ -21,6 +21,9 @@ class USBPlugin(base.Plugin): self._cmd = commands() + def _get_device_objects(self, devices): + return map(lambda x: self._hardware_inventory.get_device("usb", x), devices) + @classmethod def _get_config_options(self): return { diff --git a/tuned/plugins/plugin_video.py b/tuned/plugins/plugin_video.py index 640c876..cbf3c17 100644 --- a/tuned/plugins/plugin_video.py +++ b/tuned/plugins/plugin_video.py @@ -22,6 +22,9 @@ class VideoPlugin(base.Plugin): self._cmd = commands() + def _get_device_objects(self, devices): + return map(lambda x: self._hardware_inventory.get_device("drm", x), devices) + @classmethod def _get_config_options(self): return { diff --git a/tuned/plugins/repository.py b/tuned/plugins/repository.py index 610eea0..e1ef9e6 100644 --- a/tuned/plugins/repository.py +++ b/tuned/plugins/repository.py @@ -8,13 +8,14 @@ __all__ = ["Repository"] class Repository(PluginLoader): - def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, plugin_instance_factory, global_cfg, variables): + def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, plugin_instance_factory, global_cfg, variables): super(self.__class__, self).__init__() self._plugins = set() self._monitor_repository = monitor_repository self._storage_factory = storage_factory self._hardware_inventory = hardware_inventory self._device_matcher = device_matcher + self._device_matcher_udev = device_matcher_udev self._plugin_instance_factory = plugin_instance_factory self._global_cfg = global_cfg self._variables = variables @@ -32,7 +33,7 @@ class Repository(PluginLoader): log.debug("creating plugin %s" % plugin_name) plugin_cls = self.load_plugin(plugin_name) plugin_instance = plugin_cls(self._monitor_repository, self._storage_factory, self._hardware_inventory, self._device_matcher,\ - self._plugin_instance_factory, self._global_cfg, self._variables) + self._device_matcher_udev, self._plugin_instance_factory, self._global_cfg, self._variables) self._plugins.add(plugin_instance) return plugin_instance diff --git a/tuned/profiles/merger.py b/tuned/profiles/merger.py index 0a4660d..0d10107 100644 --- a/tuned/profiles/merger.py +++ b/tuned/profiles/merger.py @@ -32,6 +32,8 @@ class Merger(object): profile_a.units[unit_name].type = unit.type profile_a.units[unit_name].enabled = unit.enabled profile_a.units[unit_name].devices = unit.devices + if unit.devices_udev_regex != None: + profile_a.units[unit_name].devices_udev_regex = unit.devices_udev_regex if unit_name == "script" and profile_a.units[unit_name].options.get("script", None) is not None: script = profile_a.units[unit_name].options.get("script", None) profile_a.units[unit_name].options.update(unit.options) diff --git a/tuned/profiles/unit.py b/tuned/profiles/unit.py index a7ed237..e966031 100644 --- a/tuned/profiles/unit.py +++ b/tuned/profiles/unit.py @@ -3,7 +3,7 @@ class Unit(object): Unit description. """ - __slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_options" ] + __slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_devices_udev_regex", "_options" ] def __init__(self, name, config): self._name = name @@ -11,6 +11,7 @@ class Unit(object): self._enabled = config.pop("enabled", True) in [True, "true", 1] self._replace = config.pop("replace", False) in [True, "true", 1] self._devices = config.pop("devices", "*") + self._devices_udev_regex = config.pop("devices_udev_regex", None) self._options = dict(config) @property @@ -45,6 +46,14 @@ class Unit(object): def devices(self, value): self._devices = value + @property + def devices_udev_regex(self): + return self._devices_udev_regex + + @devices_udev_regex.setter + def devices_udev_regex(self, value): + self._devices_udev_regex = value + @property def options(self): return self._options diff --git a/tuned/units/manager.py b/tuned/units/manager.py index ed83ba1..b718ec0 100644 --- a/tuned/units/manager.py +++ b/tuned/units/manager.py @@ -62,7 +62,7 @@ class Manager(object): 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) + new_instance = plugin.create_instance(instance_info.name, instance_info.devices, instance_info.devices_udev_regex, instance_info.options) plugin.assign_free_devices(new_instance) plugin.initialize_instance(new_instance) self._instances.append(new_instance)