From: Ondrej Lichtner <olichtne(a)redhat.com>
The BaseHWConfigMixin class hierarchy implements various device
configuration scripts related to hw devices that we almost always do in
the same order and on the same set of devices. It includes:
* mtu configuration
* device interrupt cpu pinning
* coalescing configuration
* QDisc configuration in case parallel stream tests are requested
Each configuration is implemented in its own mixin class inheriting from
the BaseHWConfigMixin class that defines the interface. Additionally the
CommonHWConfigMixin class inherits from all 4 config mixins and
implements a collaborative inheritance version of the
test_wide_configuration, deconfiguration and description methods to be
added to the BaseEnrtRecipe derived classes.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
v2:
* renamed parent to desc in description generation methods
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Recipes/ENRT/BaseEnrtRecipe.py | 5 -
.../ENRT/ConfigMixins/BaseHWConfigMixin.py | 42 ++++++++
.../ConfigMixins/CoalescingHWConfigMixin.py | 32 ++++++
.../ENRT/ConfigMixins/CommonHWConfigMixin.py | 31 ++++++
.../ConfigMixins/DevInterruptHWConfigMixin.py | 102 ++++++++++++++++++
.../ENRT/ConfigMixins/MTUHWConfigMixin.py | 18 ++++
.../ParallelStreamQDiscHWConfigMixin.py | 32 ++++++
7 files changed, 257 insertions(+), 5 deletions(-)
create mode 100644 lnst/Recipes/ENRT/ConfigMixins/BaseHWConfigMixin.py
create mode 100644 lnst/Recipes/ENRT/ConfigMixins/CoalescingHWConfigMixin.py
create mode 100644 lnst/Recipes/ENRT/ConfigMixins/CommonHWConfigMixin.py
create mode 100644 lnst/Recipes/ENRT/ConfigMixins/DevInterruptHWConfigMixin.py
create mode 100644 lnst/Recipes/ENRT/ConfigMixins/MTUHWConfigMixin.py
create mode 100644 lnst/Recipes/ENRT/ConfigMixins/ParallelStreamQDiscHWConfigMixin.py
diff --git a/lnst/Recipes/ENRT/BaseEnrtRecipe.py b/lnst/Recipes/ENRT/BaseEnrtRecipe.py
index d89ad03..4c941c9 100644
--- a/lnst/Recipes/ENRT/BaseEnrtRecipe.py
+++ b/lnst/Recipes/ENRT/BaseEnrtRecipe.py
@@ -22,11 +22,6 @@ class BaseEnrtRecipe(BaseSubConfigMixin, PingTestAndEvaluate,
PerfRecipe):
#common requirements parameters
driver = StrParam(default="ixgbe")
- #common configuration parameters
- mtu = IntParam(mandatory=False)
- adaptive_rx_coalescing = BoolParam(mandatory=False)
- adaptive_tx_coalescing = BoolParam(mandatory=False)
-
#common test parameters
ip_versions = Param(default=("ipv4", "ipv6"))
diff --git a/lnst/Recipes/ENRT/ConfigMixins/BaseHWConfigMixin.py
b/lnst/Recipes/ENRT/ConfigMixins/BaseHWConfigMixin.py
new file mode 100644
index 0000000..1550691
--- /dev/null
+++ b/lnst/Recipes/ENRT/ConfigMixins/BaseHWConfigMixin.py
@@ -0,0 +1,42 @@
+class BaseHWConfigMixin(object):
+ @property
+ def hw_config_dev_list(self):
+ return []
+
+ def hw_config(self, config):
+ config.hw_config = {}
+
+ def hw_deconfig(self, config):
+ del config.hw_config
+
+ def describe_hw_config(self, config):
+ return []
+
+ def _configure_dev_attribute(self, config, attr_name, value):
+ hw_config = config.hw_config
+ if value:
+ attr_cfg = hw_config[attr_name + "_configuration"] = {}
+ for dev in self.hw_config_dev_list:
+ attr_cfg[dev] = {}
+ attr_cfg[dev]["original"] = getattr(dev, attr_name)
+ setattr(dev, attr_name, value)
+ attr_cfg[dev]["configured"] = getattr(dev, attr_name)
+
+ def _describe_dev_attribute(self, config, attr_name):
+ hw_config = config.hw_config
+ res = []
+ attr = hw_config.get(attr_name + "_configuration", None)
+ if attr:
+ for dev, info in attr.items():
+ res.append(
+ "{}.{}.{} configured to {}, original value {}".format(
+ dev.host.hostid,
+ dev.name,
+ attr_name,
+ info["configured"],
+ info["original"],
+ )
+ )
+ else:
+ res.append("{} configuration skipped.".format(attr_name))
+ return res
diff --git a/lnst/Recipes/ENRT/ConfigMixins/CoalescingHWConfigMixin.py
b/lnst/Recipes/ENRT/ConfigMixins/CoalescingHWConfigMixin.py
new file mode 100644
index 0000000..d2e0eae
--- /dev/null
+++ b/lnst/Recipes/ENRT/ConfigMixins/CoalescingHWConfigMixin.py
@@ -0,0 +1,32 @@
+from lnst.Common.Parameters import BoolParam
+
+from lnst.Recipes.ENRT.ConfigMixins.BaseHWConfigMixin import BaseHWConfigMixin
+
+
+class CoalescingHWConfigMixin(BaseHWConfigMixin):
+ adaptive_rx_coalescing = BoolParam(mandatory=False)
+ adaptive_tx_coalescing = BoolParam(mandatory=False)
+
+ def hw_config(self, config):
+ super().hw_config(config)
+
+ self._configure_dev_attribute(
+ config,
+ "adaptive_rx_coalescing",
+ getattr(self.params, "adaptive_rx_coalescing", None),
+ )
+ self._configure_dev_attribute(
+ config,
+ "adaptive_tx_coalescing",
+ getattr(self.params, "adaptive_tx_coalescing", None),
+ )
+
+ def describe_hw_config(self, config):
+ desc = super().describe_hw_config(config)
+ desc.extend(
+ self._describe_dev_attribute(config, "adaptive_rx_coalescing")
+ )
+ desc.extend(
+ self._describe_dev_attribute(config, "adaptive_tx_coalescing")
+ )
+ return desc
diff --git a/lnst/Recipes/ENRT/ConfigMixins/CommonHWConfigMixin.py
b/lnst/Recipes/ENRT/ConfigMixins/CommonHWConfigMixin.py
new file mode 100644
index 0000000..cdac770
--- /dev/null
+++ b/lnst/Recipes/ENRT/ConfigMixins/CommonHWConfigMixin.py
@@ -0,0 +1,31 @@
+from lnst.Recipes.ENRT.ConfigMixins.ParallelStreamQDiscHWConfigMixin import (
+ ParallelStreamQDiscHWConfigMixin,
+)
+from lnst.Recipes.ENRT.ConfigMixins.DevInterruptHWConfigMixin import (
+ DevInterruptHWConfigMixin,
+)
+from lnst.Recipes.ENRT.ConfigMixins.CoalescingHWConfigMixin import (
+ CoalescingHWConfigMixin,
+)
+from lnst.Recipes.ENRT.ConfigMixins.MTUHWConfigMixin import MTUHWConfigMixin
+
+
+class CommonHWConfigMixin(
+ ParallelStreamQDiscHWConfigMixin,
+ DevInterruptHWConfigMixin,
+ CoalescingHWConfigMixin,
+ MTUHWConfigMixin,
+):
+ def test_wide_configuration(self):
+ configuration = super().test_wide_configuration()
+ self.hw_config(configuration)
+ return configuration
+
+ def test_wide_deconfiguration(self, configuration):
+ self.hw_deconfig(configuration)
+ return super().test_wide_deconfiguration(configuration)
+
+ def generate_test_wide_description(self, config):
+ desc = super().generate_test_wide_description(config)
+ desc.extend(self.describe_hw_config(config))
+ return desc
diff --git a/lnst/Recipes/ENRT/ConfigMixins/DevInterruptHWConfigMixin.py
b/lnst/Recipes/ENRT/ConfigMixins/DevInterruptHWConfigMixin.py
new file mode 100644
index 0000000..0a47255
--- /dev/null
+++ b/lnst/Recipes/ENRT/ConfigMixins/DevInterruptHWConfigMixin.py
@@ -0,0 +1,102 @@
+import re
+
+from lnst.Common.Parameters import IntParam
+from lnst.Controller.Recipe import RecipeError
+from lnst.Controller.RecipeResults import ResultLevel
+from lnst.Recipes.ENRT.ConfigMixins.BaseHWConfigMixin import BaseHWConfigMixin
+
+
+class DevInterruptHWConfigMixin(BaseHWConfigMixin):
+ dev_intr_cpu = IntParam(mandatory=False)
+
+ def hw_config(self, config):
+ super().hw_config(config)
+
+ hw_config = config.hw_config
+
+ if "dev_intr_cpu" in self.params:
+ intr_cfg = hw_config["dev_intr_cpu_configuration"] = {}
+ intr_cfg["irq_devs"] = {}
+ intr_cfg["irqbalance_hosts"] = []
+
+ hosts = []
+ for dev in self.hw_config_dev_list:
+ if dev.host not in hosts:
+ hosts.append(dev.host)
+ for host in hosts:
+ host.run("service irqbalance stop")
+ intr_cfg["irqbalance_hosts"].append(host)
+
+ for dev in self.hw_config_dev_list:
+ # TODO better service handling through HostAPI
+ self._pin_dev_interrupts(dev, self.params.dev_intr_cpu)
+ intr_cfg["irq_devs"][dev] = self.params.dev_intr_cpu
+
+ def hw_deconfig(self, config):
+ intr_config = config.hw_config["dev_intr_cpu_configuration"]
+ for host in intr_config.get("irqbalance_hosts", []):
+ host.run("service irqbalance start")
+
+ super().hw_deconfig(config)
+
+ def describe_hw_config(self, config):
+ desc = super().describe_hw_config(config)
+
+ hw_config = config.hw_config
+
+ intr_cfg = hw_config.get("dev_intr_cpu_configuration", None)
+ if intr_cfg:
+ desc += [
+ "{} irqbalance stopped".format(host.hostid)
+ for host in intr_cfg["irqbalance_hosts"]
+ ]
+ desc += [
+ "{}.{} irqs bound to cpu {}".format(
+ dev.host.hostid, dev.name, cpu
+ )
+ for dev, cpu in intr_cfg["irq_devs"].items()
+ ]
+ else:
+ desc.append("Device irq configuration skipped.")
+ return desc
+
+ def _pin_dev_interrupts(self, dev, cpu):
+ netns = dev.netns
+ cpu_info = netns.run("lscpu", job_level=ResultLevel.DEBUG).stdout
+ regex = "CPU\(s\): *([0-9]*)"
+ num_cpus = int(re.search(regex, cpu_info).groups()[0])
+ if cpu < 0 or cpu > num_cpus - 1:
+ raise RecipeError(
+ "Invalid CPU value given: %d. Accepted value %s."
+ % (
+ cpu,
+ "is: 0" if num_cpus == 1 else "are: 0..%d" %
(num_cpus - 1),
+ )
+ )
+
+ res = netns.run(
+ "grep {} /proc/interrupts | cut -f1 -d: | sed 's/
//'".format(
+ dev.name
+ ),
+ job_level=ResultLevel.DEBUG,
+ )
+ intrs = res.stdout
+ split = res.stdout.split("\n")
+ if len(split) == 1 and split[0] == "":
+ res = netns.run(
+ "dev_irqs=/sys/class/net/{}/device/msi_irqs; "
+ "[ -d $dev_irqs ] && ls -1
$dev_irqs".format(dev.name),
+ job_level=ResultLevel.DEBUG,
+ )
+ intrs = res.stdout
+
+ for intr in intrs.split("\n"):
+ try:
+ int(intr)
+ netns.run(
+ "echo -n {} > /proc/irq/{}/smp_affinity_list".format(
+ cpu, intr.strip()
+ )
+ )
+ except ValueError:
+ pass
diff --git a/lnst/Recipes/ENRT/ConfigMixins/MTUHWConfigMixin.py
b/lnst/Recipes/ENRT/ConfigMixins/MTUHWConfigMixin.py
new file mode 100644
index 0000000..6bcf0c5
--- /dev/null
+++ b/lnst/Recipes/ENRT/ConfigMixins/MTUHWConfigMixin.py
@@ -0,0 +1,18 @@
+from lnst.Common.Parameters import IntParam
+
+from lnst.Recipes.ENRT.ConfigMixins.BaseHWConfigMixin import BaseHWConfigMixin
+
+
+class MTUHWConfigMixin(BaseHWConfigMixin):
+ mtu = IntParam(mandatory=False)
+
+ def hw_config(self, config):
+ super().hw_config(config)
+
+ self._configure_dev_attribute(
+ config, "mtu", getattr(self.params, "mtu", None)
+ )
+
+ def describe_hw_config(self, config):
+ desc = super().describe_hw_config(config)
+ return desc + self._describe_dev_attribute(config, "mtu")
diff --git a/lnst/Recipes/ENRT/ConfigMixins/ParallelStreamQDiscHWConfigMixin.py
b/lnst/Recipes/ENRT/ConfigMixins/ParallelStreamQDiscHWConfigMixin.py
new file mode 100644
index 0000000..7ca5741
--- /dev/null
+++ b/lnst/Recipes/ENRT/ConfigMixins/ParallelStreamQDiscHWConfigMixin.py
@@ -0,0 +1,32 @@
+from lnst.Recipes.ENRT.ConfigMixins.BaseHWConfigMixin import BaseHWConfigMixin
+
+
+class ParallelStreamQDiscHWConfigMixin(BaseHWConfigMixin):
+ def hw_config(self, config):
+ super().hw_config(config)
+
+ hw_config = config.hw_config
+
+ parallel_streams = getattr(self.params, "perf_parallel_streams", None)
+ if parallel_streams is not None and parallel_streams > 1:
+ hw_config["parallel_stream_devs"] = []
+ for dev in self.hw_config_dev_list:
+ dev.host.run("tc qdisc replace dev %s root mq" % dev.name)
+ hw_config["parallel_stream_devs"].append(dev)
+
+ def describe_hw_config(self, config):
+ desc = super().describe_hw_config(config)
+
+ hw_config = config.hw_config
+
+ parallel_devs = hw_config.get("parallel_stream_devs", None)
+ if parallel_devs:
+ for dev in parallel_devs:
+ desc.append(
+ "{}.{} configured to use mq qdisc".format(
+ dev.host.hostid, dev.name
+ )
+ )
+ else:
+ desc.append("Parallel streams qdisc configuration skipped.")
+ return desc
--
2.22.0