From 7c818982f89f4821d61af91854867348ec26a351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= Date: Thu, 13 Apr 2017 12:55:55 +0200 Subject: [PATCH 1/3] plugin_net: Refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor plugin_net to make its future extension easier. Signed-off-by: Ondřej Lysoněk --- tuned/plugins/plugin_net.py | 54 ++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/tuned/plugins/plugin_net.py b/tuned/plugins/plugin_net.py index 4399fc8..24f1edd 100644 --- a/tuned/plugins/plugin_net.py +++ b/tuned/plugins/plugin_net.py @@ -243,45 +243,55 @@ class NetTuningPlugin(base.Plugin): return None # d is dict: {parameter: value} - def _check_coalesce_parameters(self, d): - sck = set(d.keys()) - sck_supported = set(self._get_config_options_coalesce().keys()) - if not sck.issubset(sck_supported): - log.error("unknown coalesce parameter(s): %s" % str(sck - sck_supported)) + def _check_parameters(self, context, d): + if context == "features": + return True + params = set(d.keys()) + supported_getter = { "coalesce": self._get_config_options_coalesce } + supported = set(supported_getter[context]().keys()) + if not params.issubset(supported): + log.error("unknown %s parameter(s): %s" % (context, str(params - supported))) return False return True - def _get_device_parameters(self, coalesce, device): - ret, value = self._cmd.execute(["ethtool", "-c" if coalesce else "-k", device]) + def _get_device_parameters(self, context, device): + context2opt = { "coalesce": "-c", "features": "-k" } + opt = context2opt[context] + ret, value = self._cmd.execute(["ethtool", opt, device]) if ret != 0 or len(value) == 0: return None - d = self._parse_device_parameters(value) - if coalesce and not self._check_coalesce_parameters(d): + context2parser = { "coalesce": self._parse_device_parameters, \ + "features": self._parse_device_parameters } + parser = context2parser[context] + d = parser(value) + if context == "coalesce" and not self._check_parameters(context, d): return None return d - def _set_device_parameters(self, coalesce, value, device, sim): + def _set_device_parameters(self, context, value, device, sim): if value is None or len(value) == 0: return None - d = self._parse_config_parameters(value, "coalesce" if coalesce else "features") - if d is None or (coalesce and not self._check_coalesce_parameters(d)): + d = self._parse_config_parameters(value, context) + if d is None or not self._check_parameters(context, d): return None if not sim: - log.debug("setting %s: %s" % ("coalesce" if coalesce else "features", str(d))) + log.debug("setting %s: %s" % (context, str(d))) # ignore ethtool return code 80, it means parameter is already set - self._cmd.execute(["ethtool", "-C" if coalesce else "-K", device] + self._cmd.dict2list(d), no_errors = [80]) + context2opt = { "coalesce": "-C", "features": "-K" } + opt = context2opt[context] + self._cmd.execute(["ethtool", opt, device] + self._cmd.dict2list(d), no_errors = [80]) return d - def _custom_parameters(self, coalesce, start, value, device, verify): - storage_key = self._storage_key("coalesce" if coalesce else "features", device) + def _custom_parameters(self, context, start, value, device, verify): + storage_key = self._storage_key(context, device) if start: - cd = self._get_device_parameters(coalesce, device) - d = self._set_device_parameters(coalesce, value, device, verify) + cd = self._get_device_parameters(context, device) + d = self._set_device_parameters(context, value, device, verify) # backup only parameters which are changed sd = dict(filter(lambda (k, v): k in d, cd.items())) if len(d) != len(sd): log.error("unable to save previous %s, wanted to save: '%s', but read: '%s'" % \ - ("coalesce" if coalesce else "features", str(d.keys()), str(cd.items()))) + (context, str(d.keys()), str(cd.items()))) return False if verify: return self._cmd.dict2list(d) == self._cmd.dict2list(sd) @@ -289,13 +299,13 @@ class NetTuningPlugin(base.Plugin): else: if not verify: original_value = self._storage.get(storage_key) - self._set_device_parameters(coalesce, original_value, device, False) + self._set_device_parameters(context, original_value, device, False) return None @command_custom("features", per_device = True) def _features(self, start, value, device, verify, ignore_missing): - return self._custom_parameters(False, start, value, device, verify) + return self._custom_parameters("features", start, value, device, verify) @command_custom("coalesce", per_device = True) def _coalesce(self, start, value, device, verify, ignore_missing): - return self._custom_parameters(True, start, value, device, verify) + return self._custom_parameters("coalesce", start, value, device, verify) From 2d9a445d44ce9d9e154a8441c5f13ef7e6096c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= Date: Thu, 13 Apr 2017 16:07:43 +0200 Subject: [PATCH 2/3] plugin_net: Implement setting 'pause' parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plugin_net instances can now be given a new 'pause' option with the following values (see ethtool(8) for their meaning): autoneg on|off rx on|off tx on|off The syntax is the same as the syntax for 'coalesce' and 'features' parameters introduced in commit bc5d7acca8. Example: [net] pause=autoneg off rx off Resolves: https://github.com/redhat-performance/tuned/issues/2 Signed-off-by: Ondřej Lysoněk --- tuned/plugins/plugin_net.py | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tuned/plugins/plugin_net.py b/tuned/plugins/plugin_net.py index 24f1edd..3eba84a 100644 --- a/tuned/plugins/plugin_net.py +++ b/tuned/plugins/plugin_net.py @@ -104,13 +104,20 @@ class NetTuningPlugin(base.Plugin): "sample-interval": None } + @classmethod + def _get_config_options_pause(cls): + return { "autoneg": None, + "rx": None, + "tx": None } + @classmethod def _get_config_options(cls): return { "wake_on_lan": None, "nf_conntrack_hashsize": None, "features": None, - "coalesce": None + "coalesce": None, + "pause": None, } def _init_stats_and_idle(self, instance, device): @@ -247,21 +254,33 @@ class NetTuningPlugin(base.Plugin): if context == "features": return True params = set(d.keys()) - supported_getter = { "coalesce": self._get_config_options_coalesce } + supported_getter = { "coalesce": self._get_config_options_coalesce, \ + "pause": self._get_config_options_pause } supported = set(supported_getter[context]().keys()) if not params.issubset(supported): log.error("unknown %s parameter(s): %s" % (context, str(params - supported))) return False return True + # parse output of ethtool -a + def _parse_pause_parameters(self, s): + s = self._cmd.multiple_re_replace(\ + {"Autonegotiate": "autoneg", + "RX": "rx", + "TX": "tx"}, s) + l = s.split("\n")[1:] + l = filter(lambda x: x != '' and not re.search(r"\[fixed\]", x), l) + return dict(filter(lambda x: len(x) == 2, map(lambda x: re.split(r":\s*", x), l))) + def _get_device_parameters(self, context, device): - context2opt = { "coalesce": "-c", "features": "-k" } + context2opt = { "coalesce": "-c", "features": "-k", "pause": "-a" } opt = context2opt[context] ret, value = self._cmd.execute(["ethtool", opt, device]) if ret != 0 or len(value) == 0: return None context2parser = { "coalesce": self._parse_device_parameters, \ - "features": self._parse_device_parameters } + "features": self._parse_device_parameters, \ + "pause": self._parse_pause_parameters } parser = context2parser[context] d = parser(value) if context == "coalesce" and not self._check_parameters(context, d): @@ -277,7 +296,7 @@ class NetTuningPlugin(base.Plugin): if not sim: log.debug("setting %s: %s" % (context, str(d))) # ignore ethtool return code 80, it means parameter is already set - context2opt = { "coalesce": "-C", "features": "-K" } + context2opt = { "coalesce": "-C", "features": "-K", "pause": "-A" } opt = context2opt[context] self._cmd.execute(["ethtool", opt, device] + self._cmd.dict2list(d), no_errors = [80]) return d @@ -309,3 +328,7 @@ class NetTuningPlugin(base.Plugin): @command_custom("coalesce", per_device = True) def _coalesce(self, start, value, device, verify, ignore_missing): return self._custom_parameters("coalesce", start, value, device, verify) + + @command_custom("pause", per_device = True) + def _pause(self, start, value, device, verify, ignore_missing): + return self._custom_parameters("pause", start, value, device, verify) From 2d83889c7b8bf0d9002e9f49bed784939ec5ebd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= Date: Tue, 18 Apr 2017 10:24:35 +0200 Subject: [PATCH 3/3] plugin_net: Implement setting 'ring' parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plugin_net instances can now be given a new 'ring' option with the following values (see ethtool(8) for their meaning): rx N rx-mini N rx-jumbo N tx N The syntax is the same as the syntax for 'coalesce' and 'features' parameters introduced in commit bc5d7acca8. Example: [net] ring=rx 1024 tx 512 Resolves: https://github.com/redhat-performance/tuned/issues/2 Signed-off-by: Ondřej Lysoněk --- tuned/plugins/plugin_net.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/tuned/plugins/plugin_net.py b/tuned/plugins/plugin_net.py index 3eba84a..af39706 100644 --- a/tuned/plugins/plugin_net.py +++ b/tuned/plugins/plugin_net.py @@ -110,6 +110,13 @@ class NetTuningPlugin(base.Plugin): "rx": None, "tx": None } + @classmethod + def _get_config_options_ring(cls): + return { "rx": None, + "rx-mini": None, + "rx-jumbo": None, + "tx": None } + @classmethod def _get_config_options(cls): return { @@ -118,6 +125,7 @@ class NetTuningPlugin(base.Plugin): "features": None, "coalesce": None, "pause": None, + "ring": None, } def _init_stats_and_idle(self, instance, device): @@ -255,7 +263,8 @@ class NetTuningPlugin(base.Plugin): return True params = set(d.keys()) supported_getter = { "coalesce": self._get_config_options_coalesce, \ - "pause": self._get_config_options_pause } + "pause": self._get_config_options_pause, \ + "ring": self._get_config_options_ring } supported = set(supported_getter[context]().keys()) if not params.issubset(supported): log.error("unknown %s parameter(s): %s" % (context, str(params - supported))) @@ -272,15 +281,30 @@ class NetTuningPlugin(base.Plugin): l = filter(lambda x: x != '' and not re.search(r"\[fixed\]", x), l) return dict(filter(lambda x: len(x) == 2, map(lambda x: re.split(r":\s*", x), l))) + # parse output of ethtool -g + def _parse_ring_parameters(self, s): + a = re.split(r"^Current hardware settings:$", s, flags=re.MULTILINE) + s = a[1] + s = self._cmd.multiple_re_replace(\ + {"RX": "rx", + "RX Mini": "rx-mini", + "RX Jumbo": "rx-jumbo", + "TX": "tx"}, s) + l = s.split("\n") + l = filter(lambda x: x != '', l) + l = filter(lambda x: len(x) == 2, map(lambda x: re.split(r":\s*", x), l)) + return dict(l) + def _get_device_parameters(self, context, device): - context2opt = { "coalesce": "-c", "features": "-k", "pause": "-a" } + context2opt = { "coalesce": "-c", "features": "-k", "pause": "-a", "ring": "-g" } opt = context2opt[context] ret, value = self._cmd.execute(["ethtool", opt, device]) if ret != 0 or len(value) == 0: return None context2parser = { "coalesce": self._parse_device_parameters, \ "features": self._parse_device_parameters, \ - "pause": self._parse_pause_parameters } + "pause": self._parse_pause_parameters, \ + "ring": self._parse_ring_parameters } parser = context2parser[context] d = parser(value) if context == "coalesce" and not self._check_parameters(context, d): @@ -296,7 +320,7 @@ class NetTuningPlugin(base.Plugin): if not sim: log.debug("setting %s: %s" % (context, str(d))) # ignore ethtool return code 80, it means parameter is already set - context2opt = { "coalesce": "-C", "features": "-K", "pause": "-A" } + context2opt = { "coalesce": "-C", "features": "-K", "pause": "-A", "ring": "-G" } opt = context2opt[context] self._cmd.execute(["ethtool", opt, device] + self._cmd.dict2list(d), no_errors = [80]) return d @@ -332,3 +356,7 @@ class NetTuningPlugin(base.Plugin): @command_custom("pause", per_device = True) def _pause(self, start, value, device, verify, ignore_missing): return self._custom_parameters("pause", start, value, device, verify) + + @command_custom("ring", per_device = True) + def _ring(self, start, value, device, verify, ignore_missing): + return self._custom_parameters("ring", start, value, device, verify)