Commit cdab715b authored by Karol Latecki's avatar Karol Latecki Committed by Tomasz Zawadzki
Browse files

scripts/nvmf_perf: add irq_settings option



Allow user to specify the way IRQ alignment for
network interfaces used in test should be done.

Change-Id: Ib835d2ac2bc0c7b79474e617de32b96a483e436b
Signed-off-by: default avatarKarol Latecki <karol.latecki@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15737


Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent 7be3200f
Loading
Loading
Loading
Loading
+31 −2
Original line number Diff line number Diff line
@@ -95,7 +95,12 @@ Optional:
  "zcopy_settings": false,
  "dif_insert_strip": true,
  "null_block_dif_type": 3,
  "pm_settings": [true, 30, 1, 60]
  "pm_settings": [true, 30, 1, 60],
  "irq_settings": {
    "mode": "cpulist",
    "cpulist": "[0-10]",
    "exclude_cpulist": false
  }
}
```

@@ -131,6 +136,18 @@ Optional, common:
- enable_pm - bool;
  if bool is set to true, power measurement is enabled via collect-bmc-pm on
  the target side. Default: true.
- irq_settings - dict;
  Choose how to adjust network interface IRQ settings.
  mode: default - run IRQ alignment script with no additional options.
  mode: bynode - align IRQs to be processed only on CPU cores matching NIC
    NUMA node.
  mode: cpulist - align IRQs to be processed only on CPU cores provided
    in the cpulist parameter.
  cpulist: list of CPU cores to use for cpulist mode. Can be provided as
    list of individual cores ("[0,1,10]"), core ranges ("[0-10]"), or mix
    of both ("[0-1,10,20-22]")
  exclude_cpulist: reverse the effect of cpulist mode. Allow IRQ processing
    only on CPU cores which are not provided in cpulist parameter.

Optional, Kernel Target only:

@@ -163,6 +180,15 @@ Optional, SPDK Target only:
  Accelerator (DSA) engine.
- scheduler_core_limit - int, 0-100. Dynamic scheduler option to load limit on
  the core to be considered full.
- irq_settings - dict;
  Choose how to adjust network interface IRQ settings.
  Same as in common options section, but SPDK Target allows more modes:
  mode: shared - align IRQs to be processed only on the same CPU cores which
    are already used by SPDK Target process.
  mode: split - align IRQs to be processed only on CPU cores which are not
    used by SPDK Target process.
  mode: split-bynode - same as "split", but reduce the number of CPU cores
    to use for IRQ processing to only these matching NIC NUMA node.

### Initiator system settings section

@@ -181,7 +207,8 @@ There can be one or more `initiatorX` setting sections, depending on the test se
  "num_cores": 4,
  "cpu_frequency": 2100000,
  "adq_enable": false,
  "kernel_engine": "io_uring"
  "kernel_engine": "io_uring",
  "irq_settings": { "mode": "bynode" }
}
```

@@ -231,6 +258,8 @@ Optional, common:
  Available options:
  - libaio (default)
  - io_uring
- irq_settings - dict;
  Same as "irq_settings" in Target common options section.

Optional, SPDK Initiator only:

+100 −26
Original line number Diff line number Diff line
@@ -54,10 +54,13 @@ class Server:

        self.enable_adq = False
        self.adq_priority = None
        self.irq_settings = {"mode": "default"}

        if "adq_enable" in server_config and server_config["adq_enable"]:
            self.enable_adq = server_config["adq_enable"]
            self.adq_priority = 1

        if "irq_settings" in server_config:
            self.irq_settings.update(server_config["irq_settings"])
        if "tuned_profile" in server_config:
            self.tuned_profile = server_config["tuned_profile"]

@@ -121,7 +124,7 @@ class Server:
        self.configure_sysctl()
        self.configure_tuned()
        self.configure_cpu_governor()
        self.configure_irq_affinity()
        self.configure_irq_affinity(**self.irq_settings)

    def load_drivers(self):
        self.log.info("Loading drivers")
@@ -313,13 +316,79 @@ class Server:
        self.governor_restore = self.exec_cmd(["cat", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"]).strip()
        self.exec_cmd(["sudo", "cpupower", "frequency-set", "-g", "performance"])

    def configure_irq_affinity(self):
        self.log.info("Setting NIC irq affinity for NICs...")
    def get_core_list_from_mask(self, core_mask):
        # Generate list of individual cores from hex core mask
        # (e.g. '0xffff') or list containing:
        # - individual cores (e.g. '1, 2, 3')
        # - core ranges (e.g. '0-3')
        # - mix of both (e.g. '0, 1-4, 9, 11-13')

        core_list = []
        if "0x" in core_mask:
            core_mask_int = int(core_mask, 16)
            for i in range(core_mask_int.bit_length()):
                if (1 << i) & core_mask_int:
                    core_list.append(i)
            return core_list
        else:
            # Core list can be provided in .json config with square brackets
            # remove them first
            core_mask = core_mask.replace("[", "")
            core_mask = core_mask.replace("]", "")

            for i in core_mask.split(","):
                if "-" in i:
                    start, end = i.split("-")
                    core_range = range(int(start), int(end) + 1)
                    core_list.extend(core_range)
                else:
                    core_list.append(int(i))
            return core_list

    def configure_irq_affinity(self, mode="default", cpulist=None, exclude_cpulist=False):
        self.log.info("Setting NIC irq affinity for NICs. Using %s mode" % mode)

        if mode not in ["default", "bynode", "cpulist"]:
            raise ValueError("%s irq affinity setting not supported" % mode)

        if mode == "cpulist" and not cpulist:
            raise ValueError("%s irq affinity setting set, but no cpulist provided" % mode)

        affinity_script = "set_irq_affinity.sh"
        if "default" not in mode:
            affinity_script = "set_irq_affinity_cpulist.sh"
            system_cpu_map = self.get_numa_cpu_map()
        irq_script_path = os.path.join(self.irq_scripts_dir, affinity_script)

        def cpu_list_to_string(cpulist):
            return ",".join(map(lambda x: str(x), cpulist))

        irq_script_path = os.path.join(self.irq_scripts_dir, "set_irq_affinity.sh")
        nic_names = [self.get_nic_name_by_ip(n) for n in self.nic_ips]
        for nic in nic_names:
            irq_cmd = ["sudo", irq_script_path, nic]
        for nic_name in nic_names:
            irq_cmd = ["sudo", irq_script_path]

            # Use only CPU cores matching NIC NUMA node.
            # Remove any CPU cores if they're on exclusion list.
            if mode == "bynode":
                irq_cpus = system_cpu_map[self.get_nic_numa_node(nic_name)]
                if cpulist and exclude_cpulist:
                    disallowed_cpus = self.get_core_list_from_mask(cpulist)
                    irq_cpus = list(set(irq_cpus) - set(disallowed_cpus))
                    if not irq_cpus:
                        raise Exception("No CPUs left to process IRQs after excluding CPUs!")
                irq_cmd.append(cpu_list_to_string(irq_cpus))

            if mode == "cpulist":
                irq_cpus = self.get_core_list_from_mask(cpulist)
                if exclude_cpulist:
                    # Flatten system CPU list, we don't need NUMA awareness here
                    system_cpu_list = sorted({x for v in system_cpu_map.values() for x in v})
                    irq_cpus = list(set(system_cpu_list) - set(irq_cpus))
                    if not irq_cpus:
                        raise Exception("No CPUs left to process IRQs after excluding CPUs!")
                irq_cmd.append(cpu_list_to_string(irq_cpus))

            irq_cmd.append(nic_name)
            self.log.info(irq_cmd)
            self.exec_cmd(irq_cmd, change_dir=self.irq_scripts_dir)

@@ -977,11 +1046,11 @@ class KernelTarget(Target):

class SPDKTarget(Target):
    def __init__(self, name, general_config, target_config):
        super().__init__(name, general_config, target_config)

        # Required fields
        self.core_mask = target_config["core_mask"]
        self.num_cores = self.get_num_cores(self.core_mask)
        self.num_cores = len(self.get_core_list_from_mask(self.core_mask))

        super().__init__(name, general_config, target_config)

        # Defaults
        self.dif_insert_strip = False
@@ -1011,6 +1080,27 @@ class SPDKTarget(Target):
        self.log.info("====DSA settings:====")
        self.log.info("DSA enabled: %s" % (self.enable_dsa))

    def configure_irq_affinity(self, mode="default", cpulist=None, exclude_cpulist=False):
        if mode not in ["default", "bynode", "cpulist",
                        "shared", "split", "split-bynode"]:
            self.log.error("%s irq affinity setting not supported" % mode)
            raise Exception

        # Create core list from SPDK's mask and change it to string.
        # This is the type configure_irq_affinity expects for cpulist parameter.
        spdk_tgt_core_list = self.get_core_list_from_mask(self.core_mask)
        spdk_tgt_core_list = ",".join(map(lambda x: str(x), spdk_tgt_core_list))
        spdk_tgt_core_list = "[" + spdk_tgt_core_list + "]"

        if mode == "shared":
            super().configure_irq_affinity(mode="cpulist", cpulist=spdk_tgt_core_list)
        elif mode == "split":
            super().configure_irq_affinity(mode="cpulist", cpulist=spdk_tgt_core_list, exclude_cpulist=True)
        elif mode == "split-bynode":
            super().configure_irq_affinity(mode="bynode", cpulist=spdk_tgt_core_list, exclude_cpulist=True)
        else:
            super().configure_irq_affinity(mode=mode, cpulist=cpulist, exclude_cpulist=exclude_cpulist)

    def adq_set_busy_read(self, busy_read_val):
        return {"net.core.busy_read": busy_read_val}

@@ -1030,22 +1120,6 @@ class SPDKTarget(Target):
                bdev_bdfs.append(bdev_traddr)
        return bdev_bdfs

    @staticmethod
    def get_num_cores(core_mask):
        if "0x" in core_mask:
            return bin(int(core_mask, 16)).count("1")
        else:
            num_cores = 0
            core_mask = core_mask.replace("[", "")
            core_mask = core_mask.replace("]", "")
            for i in core_mask.split(","):
                if "-" in i:
                    x, y = i.split("-")
                    num_cores += len(range(int(x), int(y))) + 1
                else:
                    num_cores += 1
            return num_cores

    def spdk_tgt_configure(self):
        self.log.info("Configuring SPDK NVMeOF target via RPC")