From fbde16f16ad5db3fb060f75dc5e1762115b87c96 Mon Sep 17 00:00:00 2001 From: Jan Vcelak Date: Fri, 30 Nov 2012 16:53:04 +0100 Subject: [PATCH] mounts: introduce new plugin for mountpoints tuning - currently supports only barrier handling --- tuned/plugins/base.py | 4 +- tuned/plugins/plugin_mounts.py | 143 +++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 tuned/plugins/plugin_mounts.py diff --git a/tuned/plugins/base.py b/tuned/plugins/base.py index 5710663..570dca0 100644 --- a/tuned/plugins/base.py +++ b/tuned/plugins/base.py @@ -189,9 +189,9 @@ class Plugin(object): return value == "true" or value == "1" def _config_bool(self, value, true_value="1", false_value="0"): - if value == True or value == "1" or value.lower() == "true": + if value == True or value == "1" or str(value).lower() == "true": return true_value - elif value == False or value == "0" or value.lower() == "false": + elif value == False or value == "0" or str(value).lower() == "false": return false_value else: return None diff --git a/tuned/plugins/plugin_mounts.py b/tuned/plugins/plugin_mounts.py new file mode 100644 index 0000000..a7baa77 --- /dev/null +++ b/tuned/plugins/plugin_mounts.py @@ -0,0 +1,143 @@ +import base +from decorators import * +from subprocess import Popen,PIPE +import tuned.logs +import tuned.utils.commands +import glob + +log = tuned.logs.get() + +class MountsPlugin(base.Plugin): + """ + Plugin for tuning options of mount-points. + """ + + _mountpoint_topology = None + + def _post_init(self): + self._dynamic_tuning = False + + @classmethod + def _generate_mountpoint_topology(cls): + mountpoint_topology = {} + current_disk = None + + stdout, stderr = Popen(["/usr/bin/lsblk", "-rno", "TYPE,RM,KNAME,FSTYPE,MOUNTPOINT"], stdout=PIPE, stderr=PIPE).communicate() + for columns in map(lambda line: line.split(), stdout.splitlines()): + 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 + + @classmethod + def tunable_devices(cls): + if cls._mountpoint_topology is None: + cls._generate_mountpoint_topology() + return set(cls._mountpoint_topology.keys()) + + @classmethod + def _get_default_options(cls): + return { + "disable_barriers": None, + } + + def _get_device_cache_type(self, device): + source_filenames = glob.glob("/sys/block/%s/device/scsi_disk/*/cache_type" % device) + for source_filename in source_filenames: + return tuned.utils.commands.read_file(source_filename).strip() + return None + + def _mountpoint_has_writeback_cache(self, mountpoint): + 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): + 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): + remount_command = ["/usr/bin/mount", partition, "-o", "remount,%s" % options] + tuned.utils.commands.execute(remount_command) + + @command_custom("disable_barriers", per_device=True) + def _disable_barriers(self, start, value, mountpoint): + storage_key = self._storage_key("disable_barriers", mountpoint) + force = str(value).lower() == "force" + value = force or self._config_bool(value) + + if start: + if not value: + return + + reject_reason = None + + if not force: + if mountpoint in ["/", "/boot"]: + reject_reason = "system boot protection" + elif not self._mountpoint_topology[mountpoint]["filesystem"].startswith("ext"): + reject_reason = "filesystem not supported" + elif self._mountpoint_has_writeback_cache(mountpoint): + reject_reason = "device uses write back cache" + + if reject_reason is None: + original_value = self._mountpoint_has_barriers(mountpoint) + if original_value is None: + reject_reason = "unknown current setting" + elif original_value == False: + reject_reason = "barriers already disabled" + + if reject_reason is not None: + log.info("not disabling barriers on '%s' (%s)" % (mountpoint, reject_reason)) + return + + self._storage.set(storage_key, original_value) + log.info("disabling barriers on '%s'" % mountpoint) + self._remount_partition(mountpoint, "barrier=0") + + else: + original_value = self._storage.get(storage_key) + if original_value is None: + return + + log.info("enabling barriers on '%s'" % mountpoint) + self._remount_partition(mountpoint, "barrier=1") + self._storage.unset(storage_key)