Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions tuned/daemon/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,18 +373,18 @@ def instance_acquire_devices(self, devices, instance_name, caller = None):
return (False, "Invalid devices")
if not self._cmd.is_valid_name(instance_name):
return (False, "Invalid instance_name")
found = False
plugin = None
for instance_target in self._daemon._unit_manager.instances:
if instance_target.name == instance_name:
log.debug("Found instance '%s'." % instance_target.name)
found = True
plugin = instance_target.plugin
break
if not found:
if plugin is None:
rets = "Instance '%s' not found" % instance_name
log.error(rets)
return (False, rets)
if not isinstance(instance_target.plugin, hotplug.Plugin):
rets = "Plugin '%s' does not support hotplugging or dynamic instances." % instance_target.plugin.name
if not isinstance(plugin, hotplug.Plugin):
rets = "Plugin '%s' does not support hotplugging or dynamic instances." % plugin.name
log.error(rets)
return (False, rets)
devs = set(self._cmd.devstr2devs(devices))
Expand All @@ -395,14 +395,14 @@ def instance_acquire_devices(self, devices, instance_name, caller = None):
devs -= devs_moving
log.info("Moving devices '%s' from instance '%s' to instance '%s'." % (str(devs_moving),
instance.name, instance_target.name))
if (instance.plugin.name != instance_target.plugin.name):
if (instance.plugin.name != plugin.name):
rets = "Target instance '%s' is of type '%s', but devices '%s' are currently handled by " \
"instance '%s' which is of type '%s'." % (instance_target.name,
instance_target.plugin.name, str(devs_moving), instance.name, instance.plugin.name)
plugin.name, str(devs_moving), instance.name, instance.plugin.name)
log.error(rets)
return (False, rets)
instance.plugin._remove_devices_nocheck(instance, devs_moving)
instance_target.plugin._add_devices_nocheck(instance_target, devs_moving)
for dev in devs_moving:
plugin._transfer_device(instance, instance_target, dev)
if (len(devs)):
rets = "Ignoring devices not handled by any instance '%s'." % str(devs)
log.info(rets)
Expand Down Expand Up @@ -508,17 +508,15 @@ def instance_create(self, plugin_name, instance_name, options, caller = None):
plugin.instance_apply_tuning(instance)
# transfer matching devices from other instances, if the priority of the new
# instance is equal or higher (equal or lower priority value)
for other_instance in self._daemon._unit_manager.instances:
if (other_instance == instance or
other_instance.plugin != plugin or
instance.priority > other_instance.priority):
for other_instance in plugin._instances.values():
if (other_instance == instance or instance.priority > other_instance.priority):
continue
devs_moving = plugin._get_matching_devices(instance, other_instance.processed_devices)
if len(devs_moving):
log.info("Moving devices '%s' from instance '%s' to instance '%s'." % (str(devs_moving),
other_instance.name, instance.name))
plugin._remove_devices_nocheck(other_instance, devs_moving)
plugin._add_devices_nocheck(instance, devs_moving)
for dev in devs_moving:
plugin._transfer_device(other_instance, instance, dev)
return (True, "OK")

@exports.export("s", "(bs)")
Expand Down Expand Up @@ -547,6 +545,17 @@ def instance_destroy(self, instance_name, caller = None):
rets = "Plugin '%s' does not support hotplugging or dynamic instances." % plugin.name
log.error(rets)
return (False, rets)
# transfer devices to other instances that want them
for other_instance in plugin._instances.values():
if other_instance == instance:
continue
devs_moving = plugin._get_matching_devices(other_instance, instance.processed_devices)
if len(devs_moving):
log.info("Moving devices '%s' from instance '%s' to instance '%s'." % (str(devs_moving),
instance.name, other_instance.name))
for dev in devs_moving:
plugin._transfer_device(instance, other_instance, dev)
# any devices left?
devices = instance.processed_devices.copy()
try:
plugin._remove_devices_nocheck(instance, devices)
Expand All @@ -559,6 +568,6 @@ def instance_destroy(self, instance_name, caller = None):
return (False, rets)
log.info("Deleted instance '%s'" % instance_name)
for device in devices:
# _add_device() will find a suitable plugin instance
# return the devices not claimed by instances to the plugins's free_devices
plugin._add_device(device)
return (True, "OK")
43 changes: 26 additions & 17 deletions tuned/plugins/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,13 @@ def _execute_all_non_device_commands(self, instance):
if new_value is not None:
self._execute_non_device_command(instance, command, new_value)

def _execute_all_device_commands(self, instance, devices):
def _execute_all_device_commands(self, instance, devices, transfer_from_instance=None):
for command in [command for command in list(self._commands.values()) if command["per_device"]]:
new_value = self._variables.expand(instance.options.get(command["name"], None))
if new_value is None:
continue
for device in devices:
self._execute_device_command(instance, command, device, new_value)
self._execute_device_command(instance, command, device, new_value, transfer_from_instance)

def _verify_all_non_device_commands(self, instance, ignore_missing):
ret = True
Expand Down Expand Up @@ -510,24 +510,28 @@ def _get_current_value(self, instance, command, device = None, ignore_missing=Fa
else:
return command["get"](instance)

def _check_and_save_value(self, instance, command, device = None, new_value = None):
current_value = self._get_current_value(instance, command, device)
def _check_and_save_value(self, instance, command, device = None, new_value = None, transfer_from_instance=None):
if transfer_from_instance is not None and transfer_from_instance.options.get(command["name"], None) is not None:
# we're transfering: base new value calculation on the stored original
current_value = self._storage_get(transfer_from_instance, command, device)
else:
current_value = self._get_current_value(instance, command, device)
new_value = self._process_assignment_modifiers(new_value, current_value)
if new_value is not None and current_value is not None:
self._storage_set(instance, command, current_value, device)
return new_value

def _execute_device_command(self, instance, command, device, new_value):
def _execute_device_command(self, instance, command, device, new_value, transfer_from_instance=None):
if command["custom"] is not None:
command["custom"](True, new_value, device, False, False, instance)
command["custom"](True, new_value, device, False, False, instance, transfer_from_instance)
else:
new_value = self._check_and_save_value(instance, command, device, new_value)
new_value = self._check_and_save_value(instance, command, device, new_value, transfer_from_instance)
if new_value is not None:
command["set"](new_value, device, instance, sim = False, remove = False)

def _execute_non_device_command(self, instance, command, new_value):
if command["custom"] is not None:
command["custom"](True, new_value, False, False, instance)
command["custom"](True, new_value, False, False, instance, None)
else:
new_value = self._check_and_save_value(instance, command, None, new_value)
if new_value is not None:
Expand Down Expand Up @@ -588,7 +592,7 @@ def _log_verification_result(self, name, success, new_value,

def _verify_device_command(self, instance, command, device, new_value, ignore_missing):
if command["custom"] is not None:
return command["custom"](True, new_value, device, True, ignore_missing, instance)
return command["custom"](True, new_value, device, True, ignore_missing, instance, None)
current_value = self._get_current_value(instance, command, device, ignore_missing=ignore_missing)
new_value = self._process_assignment_modifiers(new_value, current_value)
if new_value is None:
Expand All @@ -598,7 +602,7 @@ def _verify_device_command(self, instance, command, device, new_value, ignore_mi

def _verify_non_device_command(self, instance, command, new_value, ignore_missing):
if command["custom"] is not None:
return command["custom"](True, new_value, True, ignore_missing, instance)
return command["custom"](True, new_value, True, ignore_missing, instance, None)
current_value = self._get_current_value(instance, command)
new_value = self._process_assignment_modifiers(new_value, current_value)
if new_value is None:
Expand All @@ -611,24 +615,29 @@ def _cleanup_all_non_device_commands(self, instance):
if (instance.options.get(command["name"], None) is not None) or (command["name"] in self._options_used_by_dynamic):
self._cleanup_non_device_command(instance, command)

def _cleanup_all_device_commands(self, instance, devices, remove = False):
def _cleanup_all_device_commands(self, instance, devices, remove = False, transfer_to_instance=None):
for command in reversed([command for command in list(self._commands.values()) if command["per_device"]]):
if transfer_to_instance is not None and transfer_to_instance.options.get(command["name"], None) is not None:
transfer_this_command = transfer_to_instance
else:
transfer_this_command = None
if (instance.options.get(command["name"], None) is not None) or (command["name"] in self._options_used_by_dynamic):
for device in devices:
self._cleanup_device_command(instance, command, device, remove)
self._cleanup_device_command(instance, command, device, remove, transfer_this_command)

def _cleanup_device_command(self, instance, command, device, remove = False):
def _cleanup_device_command(self, instance, command, device, remove = False, transfer_to_instance=None):
if command["custom"] is not None:
command["custom"](False, None, device, False, False, instance)
command["custom"](False, None, device, False, False, instance, transfer_to_instance)
else:
old_value = self._storage_get(instance, command, device)
if old_value is not None:
if old_value is not None and transfer_to_instance is None:
command["set"](old_value, device, instance, sim = False, remove = remove)
self._storage_unset(instance, command, device)
if transfer_to_instance is None:
self._storage_unset(instance, command, device)

def _cleanup_non_device_command(self, instance, command):
if command["custom"] is not None:
command["custom"](False, None, False, False, instance)
command["custom"](False, None, False, False, instance, None)
else:
old_value = self._storage_get(instance, command)
if old_value is not None:
Expand Down
37 changes: 27 additions & 10 deletions tuned/plugins/hotplug.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ def _hardware_events_callback(self, event, device):
log.info("device: '%s', rename event, reported new name" % device.sys_name)
self._move_device(device.sys_name)

def _add_device_process(self, instance, device_name):
def _add_device_process(self, instance, device_name, transfer_from_instance=None):
log.info("instance %s: adding new device %s" % (instance.name, device_name))
self._assigned_devices.add(device_name)
self._call_device_script(instance, instance.script_pre, "apply", [device_name])
self._added_device_apply_tuning(instance, device_name)
self._added_device_apply_tuning(instance, device_name, transfer_from_instance)
self._call_device_script(instance, instance.script_post, "apply", [device_name])
instance.processed_devices.add(device_name)

Expand Down Expand Up @@ -68,11 +68,14 @@ def _add_devices_nocheck(self, instance, device_names):
instance.active = len(instance.processed_devices) \
+ len(instance.assigned_devices) > 0

def _remove_device_process(self, instance, device_name):
def _remove_device_process(self, instance, device_name, transfer_to_instance=None):
if device_name in instance.processed_devices:
self._call_device_script(instance, instance.script_post, "unapply", [device_name])
self._removed_device_unapply_tuning(instance, device_name)
self._call_device_script(instance, instance.script_pre, "unapply", [device_name])
if transfer_to_instance is not None:
self._removed_device_unapply_tuning(instance, device_name, transfer_to_instance)
else:
self._call_device_script(instance, instance.script_post, "unapply", [device_name])
self._removed_device_unapply_tuning(instance, device_name, transfer_to_instance)
self._call_device_script(instance, instance.script_pre, "unapply", [device_name])
instance.processed_devices.remove(device_name)
# This can be a bit racy (we can overcount),
# but it shouldn't affect the boolean result
Expand Down Expand Up @@ -122,12 +125,26 @@ def _remove_devices_nocheck(self, instance, device_names):
for dev in device_names:
self._remove_device_process(instance, dev)

def _added_device_apply_tuning(self, instance, device_name):
self._execute_all_device_commands(instance, [device_name])
def _transfer_device(self, from_instance, to_instance, device_name):
"""Transfer a device between instances

Apply the tuning of the target instance without the intermediate step
of rolling back to the original tuning.
"""
if device_name not in (self._assigned_devices | self._free_devices):
return

if not self._remove_device_process(from_instance, device_name, to_instance):
return

self._add_device_process(to_instance, device_name, transfer_from_instance=from_instance)

def _added_device_apply_tuning(self, instance, device_name, transfer_from_instance):
self._execute_all_device_commands(instance, [device_name], transfer_from_instance)
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
self._instance_apply_dynamic(instance, device_name)

def _removed_device_unapply_tuning(self, instance, device_name):
def _removed_device_unapply_tuning(self, instance, device_name, transfer_to_instance):
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
self._instance_unapply_dynamic(instance, device_name)
self._cleanup_all_device_commands(instance, [device_name], remove = True)
self._cleanup_all_device_commands(instance, [device_name], remove = True, transfer_to_instance=transfer_to_instance)
14 changes: 7 additions & 7 deletions tuned/plugins/plugin_bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,15 +562,15 @@ def _install_initrd(self, img):
return True

@command_custom("grub2_cfg_file")
def _grub2_cfg_file(self, enabling, value, verify, ignore_missing, instance):
def _grub2_cfg_file(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
# nothing to verify
if verify:
return None
if enabling and value is not None:
self._grub2_cfg_file_names = [str(value)]

@command_custom("initrd_dst_img")
def _initrd_dst_img(self, enabling, value, verify, ignore_missing, instance):
def _initrd_dst_img(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
# nothing to verify
if verify:
return None
Expand All @@ -585,7 +585,7 @@ def _initrd_dst_img(self, enabling, value, verify, ignore_missing, instance):
return None

@command_custom("initrd_remove_dir")
def _initrd_remove_dir(self, enabling, value, verify, ignore_missing, instance):
def _initrd_remove_dir(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
# nothing to verify
if verify:
return None
Expand All @@ -595,7 +595,7 @@ def _initrd_remove_dir(self, enabling, value, verify, ignore_missing, instance):
return None

@command_custom("initrd_add_img", per_device = False, priority = 10)
def _initrd_add_img(self, enabling, value, verify, ignore_missing, instance):
def _initrd_add_img(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
# nothing to verify
if verify:
return None
Expand All @@ -610,7 +610,7 @@ def _initrd_add_img(self, enabling, value, verify, ignore_missing, instance):
return None

@command_custom("initrd_add_dir", per_device = False, priority = 10)
def _initrd_add_dir(self, enabling, value, verify, ignore_missing, instance):
def _initrd_add_dir(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
# nothing to verify
if verify:
return None
Expand Down Expand Up @@ -642,7 +642,7 @@ def _initrd_add_dir(self, enabling, value, verify, ignore_missing, instance):
return None

@command_custom("cmdline", per_device = False, priority = 10)
def _cmdline(self, enabling, value, verify, ignore_missing, instance):
def _cmdline(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
v = self._variables.expand(self._cmd.unquote(value))
if verify:
if self._rpm_ostree:
Expand Down Expand Up @@ -677,7 +677,7 @@ def _cmdline(self, enabling, value, verify, ignore_missing, instance):
return None

@command_custom("skip_grub_config", per_device = False, priority = 10)
def _skip_grub_config(self, enabling, value, verify, ignore_missing, instance):
def _skip_grub_config(self, enabling, value, verify, ignore_missing, instance, transfer_instance):
if verify:
return None
if enabling and value is not None:
Expand Down
10 changes: 5 additions & 5 deletions tuned/plugins/plugin_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,15 @@ def _hardware_events_callback(self, event, device):
if self._device_is_supported(device) or event == "remove":
super(DiskPlugin, self)._hardware_events_callback(event, device)

def _added_device_apply_tuning(self, instance, device_name):
def _added_device_apply_tuning(self, instance, device_name, transfer_from_instance):
if instance._load_monitor is not None:
instance._load_monitor.add_device(device_name)
super(DiskPlugin, self)._added_device_apply_tuning(instance, device_name)
super(DiskPlugin, self)._added_device_apply_tuning(instance, device_name, transfer_from_instance)

def _removed_device_unapply_tuning(self, instance, device_name):
def _removed_device_unapply_tuning(self, instance, device_name, transfer_to_instance):
if instance._load_monitor is not None:
instance._load_monitor.remove_device(device_name)
super(DiskPlugin, self)._removed_device_unapply_tuning(instance, device_name)
super(DiskPlugin, self)._removed_device_unapply_tuning(instance, device_name, transfer_to_instance)

@classmethod
def _get_config_options(cls):
Expand Down Expand Up @@ -449,7 +449,7 @@ def _get_readahead(self, device, instance, ignore_missing=False):
return int(value)

@command_custom("readahead_multiply", per_device=True)
def _multiply_readahead(self, enabling, multiplier, device, verify, ignore_missing, instance):
def _multiply_readahead(self, enabling, multiplier, device, verify, ignore_missing, instance, transfer_instance):
if verify:
return None
storage_key = self._storage_key(
Expand Down
Loading