This will not completely resolve RHEL-60906, because there will be always race condition if the rename event happens before the instance is fully initialized, but it should significantly improve the situation. Drawback is that it can report in the logs device names that shouldn't be matched by the plugin_net instance, i.e. it matches devices according to their original names but accesses them with their new names. This can be confusing. Also in the runtime API (e.g. for removal of the device from the instance), the original device names have to be used. For insertion of the device to the instance, new names have to be used. This can be even more confusing. Unfortunately, there probably isn't better alternative now, because for correct operation we would have to handle rename by removal and addition events. This would require rollback and retune steps which would lead to very negative performance consequences. Also we would have to cope with partially tuned devices if the rename event happens during profile application. It's doable, but it would require big architectural changes. The PR also extends internal API, now the plugin instances receive 'instance' as a method parameter. Also fixed some existing CodeQL warnings. Resolves: RHEL-60906 Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
181 lines
5.4 KiB
Python
181 lines
5.4 KiB
Python
import tuned.consts as consts
|
|
from . import base
|
|
from .decorators import *
|
|
from subprocess import Popen,PIPE
|
|
import tuned.logs
|
|
from tuned.utils.commands import commands
|
|
import glob
|
|
|
|
log = tuned.logs.get()
|
|
cmd = commands()
|
|
|
|
class MountsPlugin(base.Plugin):
|
|
"""
|
|
Enables or disables barriers for mounts according to the value of the
|
|
boolean option [option]`disable_barriers`. The option additionally allows
|
|
the special value `force`, which disables barriers even on mountpoints with
|
|
write back caches.
|
|
|
|
NOTE: Only extended file systems (ext) are supported by this plug-in.
|
|
"""
|
|
|
|
@classmethod
|
|
def _generate_mountpoint_topology(cls):
|
|
"""
|
|
Gets the information about disks, partitions and mountpoints. Stores information about used filesystem and
|
|
creates a list of all underlying devices (in case of LVM) for each mountpoint.
|
|
"""
|
|
mountpoint_topology = {}
|
|
current_disk = None
|
|
|
|
stdout, stderr = Popen(["lsblk", "-rno", \
|
|
"TYPE,RM,KNAME,FSTYPE,MOUNTPOINT"], \
|
|
stdout=PIPE, stderr=PIPE, close_fds=True, \
|
|
universal_newlines = True).communicate()
|
|
for columns in [line.split() for line in stdout.splitlines()]:
|
|
if len(columns) < 3:
|
|
continue
|
|
device_type, device_removable, device_name = columns[:3]
|
|
filesystem = columns[3] if len(columns) > 3 else None
|
|
mountpoint = columns[4] if len(columns) > 4 else None
|
|
|
|
if device_type == "disk":
|
|
current_disk = device_name
|
|
continue
|
|
|
|
# skip removable, skip nonpartitions
|
|
if device_removable == "1" or device_type not in ["part", "lvm"]:
|
|
continue
|
|
|
|
if mountpoint is None or mountpoint == "[SWAP]":
|
|
continue
|
|
|
|
mountpoint_topology.setdefault(mountpoint, {"disks": set(), "device_name": device_name, "filesystem": filesystem})
|
|
mountpoint_topology[mountpoint]["disks"].add(current_disk)
|
|
|
|
cls._mountpoint_topology = mountpoint_topology
|
|
|
|
def _init_devices(self):
|
|
self._generate_mountpoint_topology()
|
|
self._devices_supported = True
|
|
self._free_devices = set(self._mountpoint_topology.keys())
|
|
self._assigned_devices = set()
|
|
|
|
@classmethod
|
|
def _get_config_options(self):
|
|
return {
|
|
"disable_barriers": None,
|
|
}
|
|
|
|
def _instance_init(self, instance):
|
|
instance._has_dynamic_tuning = False
|
|
instance._has_static_tuning = True
|
|
|
|
def _instance_cleanup(self, instance):
|
|
pass
|
|
|
|
def _get_device_cache_type(self, device):
|
|
"""
|
|
Get device cache type. This will work only for devices on SCSI kernel subsystem.
|
|
"""
|
|
source_filenames = glob.glob("/sys/block/%s/device/scsi_disk/*/cache_type" % device)
|
|
for source_filename in source_filenames:
|
|
return cmd.read_file(source_filename).strip()
|
|
return None
|
|
|
|
def _mountpoint_has_writeback_cache(self, mountpoint):
|
|
"""
|
|
Checks if the device has 'write back' cache. If the cache type cannot be determined, asume some other cache.
|
|
"""
|
|
for device in self._mountpoint_topology[mountpoint]["disks"]:
|
|
if self._get_device_cache_type(device) == "write back":
|
|
return True
|
|
return False
|
|
|
|
def _mountpoint_has_barriers(self, mountpoint):
|
|
"""
|
|
Checks if a given mountpoint is mounted with barriers enabled or disabled.
|
|
"""
|
|
with open("/proc/mounts") as mounts_file:
|
|
for line in mounts_file:
|
|
# device mountpoint filesystem options dump check
|
|
columns = line.split()
|
|
if columns[0][0] != "/":
|
|
continue
|
|
if columns[1] == mountpoint:
|
|
option_list = columns[3]
|
|
break
|
|
else:
|
|
return None
|
|
|
|
options = option_list.split(",")
|
|
for option in options:
|
|
(name, sep, value) = option.partition("=")
|
|
# nobarrier barrier=0
|
|
if name == "nobarrier" or (name == "barrier" and value == "0"):
|
|
return False
|
|
# barrier barrier=1
|
|
elif name == "barrier":
|
|
return True
|
|
else:
|
|
# default
|
|
return True
|
|
|
|
def _remount_partition(self, partition, options):
|
|
"""
|
|
Remounts partition.
|
|
"""
|
|
remount_command = ["/usr/bin/mount", partition, "-o", "remount,%s" % options]
|
|
cmd.execute(remount_command)
|
|
|
|
@command_custom("disable_barriers", per_device=True)
|
|
def _disable_barriers(self, start, value, mountpoint, verify, ignore_missing, instance):
|
|
storage_key = self._storage_key(
|
|
command_name = "disable_barriers",
|
|
device_name = mountpoint)
|
|
force = str(value).lower() == "force"
|
|
value = force or self._option_bool(value)
|
|
|
|
if start:
|
|
if not value:
|
|
return None
|
|
|
|
reject_reason = None
|
|
|
|
if not self._mountpoint_topology[mountpoint]["filesystem"].startswith("ext"):
|
|
reject_reason = "filesystem not supported"
|
|
elif not force and self._mountpoint_has_writeback_cache(mountpoint):
|
|
reject_reason = "device uses write back cache"
|
|
else:
|
|
original_value = self._mountpoint_has_barriers(mountpoint)
|
|
if original_value is None:
|
|
reject_reason = "unknown current setting"
|
|
elif original_value == False:
|
|
if verify:
|
|
log.info(consts.STR_VERIFY_PROFILE_OK % mountpoint)
|
|
return True
|
|
else:
|
|
reject_reason = "barriers already disabled"
|
|
elif verify:
|
|
log.error(consts.STR_VERIFY_PROFILE_FAIL % mountpoint)
|
|
return False
|
|
|
|
if reject_reason is not None:
|
|
log.info("not disabling barriers on '%s' (%s)" % (mountpoint, reject_reason))
|
|
return None
|
|
|
|
self._storage.set(storage_key, original_value)
|
|
log.info("disabling barriers on '%s'" % mountpoint)
|
|
self._remount_partition(mountpoint, "barrier=0")
|
|
|
|
else:
|
|
if verify:
|
|
return None
|
|
original_value = self._storage.get(storage_key)
|
|
if original_value is None:
|
|
return None
|
|
|
|
log.info("enabling barriers on '%s'" % mountpoint)
|
|
self._remount_partition(mountpoint, "barrier=1")
|
|
self._storage.unset(storage_key)
|
|
return None
|