diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/dpdk/usertools | |
parent | Initial commit. (diff) | |
download | ceph-upstream/18.2.2.tar.xz ceph-upstream/18.2.2.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/dpdk/usertools')
-rwxr-xr-x | src/spdk/dpdk/usertools/cpu_layout.py | 73 | ||||
-rwxr-xr-x | src/spdk/dpdk/usertools/dpdk-devbind.py | 746 | ||||
-rwxr-xr-x | src/spdk/dpdk/usertools/dpdk-pmdinfo.py | 642 | ||||
-rwxr-xr-x | src/spdk/dpdk/usertools/dpdk-setup.sh | 605 | ||||
-rwxr-xr-x | src/spdk/dpdk/usertools/dpdk-telemetry-client.py | 135 | ||||
-rwxr-xr-x | src/spdk/dpdk/usertools/dpdk-telemetry.py | 83 | ||||
-rw-r--r-- | src/spdk/dpdk/usertools/meson.build | 4 |
7 files changed, 2288 insertions, 0 deletions
diff --git a/src/spdk/dpdk/usertools/cpu_layout.py b/src/spdk/dpdk/usertools/cpu_layout.py new file mode 100755 index 000000000..6f129b1db --- /dev/null +++ b/src/spdk/dpdk/usertools/cpu_layout.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# Copyright(c) 2017 Cavium, Inc. All rights reserved. + +from __future__ import print_function +import sys +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + +sockets = [] +cores = [] +core_map = {} +base_path = "/sys/devices/system/cpu" +fd = open("{}/kernel_max".format(base_path)) +max_cpus = int(fd.read()) +fd.close() +for cpu in xrange(max_cpus + 1): + try: + fd = open("{}/cpu{}/topology/core_id".format(base_path, cpu)) + except IOError: + continue + except: + break + core = int(fd.read()) + fd.close() + fd = open("{}/cpu{}/topology/physical_package_id".format(base_path, cpu)) + socket = int(fd.read()) + fd.close() + if core not in cores: + cores.append(core) + if socket not in sockets: + sockets.append(socket) + key = (socket, core) + if key not in core_map: + core_map[key] = [] + core_map[key].append(cpu) + +print(format("=" * (47 + len(base_path)))) +print("Core and Socket Information (as reported by '{}')".format(base_path)) +print("{}\n".format("=" * (47 + len(base_path)))) +print("cores = ", cores) +print("sockets = ", sockets) +print("") + +max_processor_len = len(str(len(cores) * len(sockets) * 2 - 1)) +max_thread_count = len(list(core_map.values())[0]) +max_core_map_len = (max_processor_len * max_thread_count) \ + + len(", ") * (max_thread_count - 1) \ + + len('[]') + len('Socket ') +max_core_id_len = len(str(max(cores))) + +output = " ".ljust(max_core_id_len + len('Core ')) +for s in sockets: + output += " Socket %s" % str(s).ljust(max_core_map_len - len('Socket ')) +print(output) + +output = " ".ljust(max_core_id_len + len('Core ')) +for s in sockets: + output += " --------".ljust(max_core_map_len) + output += " " +print(output) + +for c in cores: + output = "Core %s" % str(c).ljust(max_core_id_len) + for s in sockets: + if (s,c) in core_map: + output += " " + str(core_map[(s, c)]).ljust(max_core_map_len) + else: + output += " " * (max_core_map_len + 1) + print(output) diff --git a/src/spdk/dpdk/usertools/dpdk-devbind.py b/src/spdk/dpdk/usertools/dpdk-devbind.py new file mode 100755 index 000000000..dc008823f --- /dev/null +++ b/src/spdk/dpdk/usertools/dpdk-devbind.py @@ -0,0 +1,746 @@ +#! /usr/bin/env python +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# + +from __future__ import print_function +import sys +import os +import getopt +import subprocess +from os.path import exists, abspath, dirname, basename + +# The PCI base class for all devices +network_class = {'Class': '02', 'Vendor': None, 'Device': None, + 'SVendor': None, 'SDevice': None} +acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None, + 'SVendor': None, 'SDevice': None} +ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30', + 'SVendor': None, 'SDevice': None} +encryption_class = {'Class': '10', 'Vendor': None, 'Device': None, + 'SVendor': None, 'SDevice': None} +intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None, + 'SVendor': None, 'SDevice': None} +cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d', + 'SVendor': None, 'SDevice': None} +cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053', + 'SVendor': None, 'SDevice': None} +cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049', + 'SVendor': None, 'SDevice': None} +cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051', + 'SVendor': None, 'SDevice': None} +cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037', + 'SVendor': None, 'SDevice': None} +avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110', + 'SVendor': None, 'SDevice': None} + +octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa', + 'SVendor': None, 'SDevice': None} +octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc', + 'SVendor': None, 'SDevice': None} +octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081', + 'SVendor': None, 'SDevice': None} + +intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f', + 'SVendor': None, 'SDevice': None} +intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021', + 'SVendor': None, 'SDevice': None} +intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00', + 'SVendor': None, 'SDevice': None} +intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c', + 'SVendor': None, 'SDevice': None} + +network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class] +baseband_devices = [acceleration_class] +crypto_devices = [encryption_class, intel_processor_class] +eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso] +mempool_devices = [cavium_fpa, octeontx2_npa] +compress_devices = [cavium_zip] +misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ioat_icx, intel_ntb_skx, octeontx2_dma] + +# global dict ethernet devices present. Dictionary indexed by PCI address. +# Each device within this is itself a dictionary of device properties +devices = {} +# list of supported DPDK drivers +dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"] +# list of currently loaded kernel modules +loaded_modules = None + +# command-line arg flags +b_flag = None +status_flag = False +force_flag = False +args = [] + + +def usage(): + '''Print usage information for the program''' + argv0 = basename(sys.argv[0]) + print(""" +Usage: +------ + + %(argv0)s [options] DEVICE1 DEVICE2 .... + +where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax +or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may +also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc. + +Options: + --help, --usage: + Display usage information and quit + + -s, --status: + Print the current status of all known network, crypto, event + and mempool devices. + For each device, it displays the PCI domain, bus, slot and function, + along with a text description of the device. Depending upon whether the + device is being used by a kernel driver, the igb_uio driver, or no + driver, other relevant information will be displayed: + * the Linux interface name e.g. if=eth0 + * the driver being used e.g. drv=igb_uio + * any suitable drivers not currently using that device + e.g. unused=igb_uio + NOTE: if this flag is passed along with a bind/unbind option, the + status display will always occur after the other operations have taken + place. + + --status-dev: + Print the status of given device group. Supported device groups are: + "net", "baseband", "crypto", "event", "mempool" and "compress" + + -b driver, --bind=driver: + Select the driver to use or \"none\" to unbind the device + + -u, --unbind: + Unbind a device (Equivalent to \"-b none\") + + --force: + By default, network devices which are used by Linux - as indicated by + having routes in the routing table - cannot be modified. Using the + --force flag overrides this behavior, allowing active links to be + forcibly unbound. + WARNING: This can lead to loss of network connection and should be used + with caution. + +Examples: +--------- + +To display current device status: + %(argv0)s --status + +To display current network device status: + %(argv0)s --status-dev net + +To bind eth1 from the current driver and move to use igb_uio + %(argv0)s --bind=igb_uio eth1 + +To unbind 0000:01:00.0 from using any driver + %(argv0)s -u 0000:01:00.0 + +To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver + %(argv0)s -b ixgbe 02:00.0 02:00.1 + + """ % locals()) # replace items from local variables + + +# This is roughly compatible with check_output function in subprocess module +# which is only available in python 2.7. +def check_output(args, stderr=None): + '''Run a command and capture its output''' + return subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=stderr).communicate()[0] + +# check if a specific kernel module is loaded +def module_is_loaded(module): + global loaded_modules + + if module == 'vfio_pci': + module = 'vfio-pci' + + if loaded_modules: + return module in loaded_modules + + # Get list of sysfs modules (both built-in and dynamically loaded) + sysfs_path = '/sys/module/' + + # Get the list of directories in sysfs_path + sysfs_mods = [m for m in os.listdir(sysfs_path) + if os.path.isdir(os.path.join(sysfs_path, m))] + + # special case for vfio_pci (module is named vfio-pci, + # but its .ko is named vfio_pci) + sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods] + + loaded_modules = sysfs_mods + + return module in sysfs_mods + + +def check_modules(): + '''Checks that igb_uio is loaded''' + global dpdk_drivers + + # list of supported modules + mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers] + + # first check if module is loaded + for mod in mods: + if module_is_loaded(mod["Name"]): + mod["Found"] = True + + # check if we have at least one loaded module + if True not in [mod["Found"] for mod in mods] and b_flag is not None: + print("Warning: no supported DPDK kernel modules are loaded", file=sys.stderr) + + # change DPDK driver list to only contain drivers that are loaded + dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]] + + +def has_driver(dev_id): + '''return true if a device is assigned to a driver. False otherwise''' + return "Driver_str" in devices[dev_id] + + +def get_pci_device_details(dev_id, probe_lspci): + '''This function gets additional details for a PCI device''' + device = {} + + if probe_lspci: + extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines() + + # parse lspci details + for line in extra_info: + if len(line) == 0: + continue + name, value = line.decode("utf8").split("\t", 1) + name = name.strip(":") + "_str" + device[name] = value + # check for a unix interface name + device["Interface"] = "" + for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id): + if "net" in dirs: + device["Interface"] = \ + ",".join(os.listdir(os.path.join(base, "net"))) + break + # check if a port is used for ssh connection + device["Ssh_if"] = False + device["Active"] = "" + + return device + +def clear_data(): + '''This function clears any old data''' + global devices + devices = {} + +def get_device_details(devices_type): + '''This function populates the "devices" dictionary. The keys used are + the pci addresses (domain:bus:slot.func). The values are themselves + dictionaries - one for each NIC.''' + global devices + global dpdk_drivers + + # first loop through and read details for all devices + # request machine readable format, with numeric IDs and String + dev = {} + dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines() + for dev_line in dev_lines: + if len(dev_line) == 0: + if device_type_match(dev, devices_type): + # Replace "Driver" with "Driver_str" to have consistency of + # of dictionary key names + if "Driver" in dev.keys(): + dev["Driver_str"] = dev.pop("Driver") + if "Module" in dev.keys(): + dev["Module_str"] = dev.pop("Module") + # use dict to make copy of dev + devices[dev["Slot"]] = dict(dev) + # Clear previous device's data + dev = {} + else: + name, value = dev_line.decode("utf8").split("\t", 1) + value_list = value.rsplit(' ', 1) + if len(value_list) > 1: + # String stored in <name>_str + dev[name.rstrip(":") + '_str'] = value_list[0] + # Numeric IDs + dev[name.rstrip(":")] = value_list[len(value_list) - 1] \ + .rstrip("]").lstrip("[") + + if devices_type == network_devices: + # check what is the interface if any for an ssh connection if + # any to this host, so we can mark it later. + ssh_if = [] + route = check_output(["ip", "-o", "route"]) + # filter out all lines for 169.254 routes + route = "\n".join(filter(lambda ln: not ln.startswith("169.254"), + route.decode().splitlines())) + rt_info = route.split() + for i in range(len(rt_info) - 1): + if rt_info[i] == "dev": + ssh_if.append(rt_info[i+1]) + + # based on the basic info, get extended text details + for d in devices.keys(): + if not device_type_match(devices[d], devices_type): + continue + + # get additional info and add it to existing data + devices[d] = devices[d].copy() + # No need to probe lspci + devices[d].update(get_pci_device_details(d, False).items()) + + if devices_type == network_devices: + for _if in ssh_if: + if _if in devices[d]["Interface"].split(","): + devices[d]["Ssh_if"] = True + devices[d]["Active"] = "*Active*" + break + + # add igb_uio to list of supporting modules if needed + if "Module_str" in devices[d]: + for driver in dpdk_drivers: + if driver not in devices[d]["Module_str"]: + devices[d]["Module_str"] = \ + devices[d]["Module_str"] + ",%s" % driver + else: + devices[d]["Module_str"] = ",".join(dpdk_drivers) + + # make sure the driver and module strings do not have any duplicates + if has_driver(d): + modules = devices[d]["Module_str"].split(",") + if devices[d]["Driver_str"] in modules: + modules.remove(devices[d]["Driver_str"]) + devices[d]["Module_str"] = ",".join(modules) + + +def device_type_match(dev, devices_type): + for i in range(len(devices_type)): + param_count = len( + [x for x in devices_type[i].values() if x is not None]) + match_count = 0 + if dev["Class"][0:2] == devices_type[i]["Class"]: + match_count = match_count + 1 + for key in devices_type[i].keys(): + if key != 'Class' and devices_type[i][key]: + value_list = devices_type[i][key].split(',') + for value in value_list: + if value.strip(' ') == dev[key]: + match_count = match_count + 1 + # count must be the number of non None parameters to match + if match_count == param_count: + return True + return False + +def dev_id_from_dev_name(dev_name): + '''Take a device "name" - a string passed in by user to identify a NIC + device, and determine the device id - i.e. the domain:bus:slot.func - for + it, which can then be used to index into the devices array''' + + # check if it's already a suitable index + if dev_name in devices: + return dev_name + # check if it's an index just missing the domain part + elif "0000:" + dev_name in devices: + return "0000:" + dev_name + else: + # check if it's an interface name, e.g. eth1 + for d in devices.keys(): + if dev_name in devices[d]["Interface"].split(","): + return devices[d]["Slot"] + # if nothing else matches - error + raise ValueError("Unknown device: %s. " + "Please specify device in \"bus:slot.func\" format" % dev_name) + + +def unbind_one(dev_id, force): + '''Unbind the device identified by "dev_id" from its current driver''' + dev = devices[dev_id] + if not has_driver(dev_id): + print("Notice: %s %s %s is not currently managed by any driver" % + (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr) + return + + # prevent us disconnecting ourselves + if dev["Ssh_if"] and not force: + print("Warning: routing table indicates that interface %s is active. " + "Skipping unbind" % dev_id, file=sys.stderr) + return + + # write to /sys to unbind + filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"] + try: + f = open(filename, "a") + except: + sys.exit("Error: unbind failed for %s - Cannot open %s" % + (dev_id, filename)) + f.write(dev_id) + f.close() + + +def bind_one(dev_id, driver, force): + '''Bind the device given by "dev_id" to the driver "driver". If the device + is already bound to a different driver, it will be unbound first''' + dev = devices[dev_id] + saved_driver = None # used to rollback any unbind in case of failure + + # prevent disconnection of our ssh session + if dev["Ssh_if"] and not force: + print("Warning: routing table indicates that interface %s is active. " + "Not modifying" % dev_id, file=sys.stderr) + return + + # unbind any existing drivers we don't want + if has_driver(dev_id): + if dev["Driver_str"] == driver: + print("Notice: %s already bound to driver %s, skipping" % + (dev_id, driver), file=sys.stderr) + return + else: + saved_driver = dev["Driver_str"] + unbind_one(dev_id, force) + dev["Driver_str"] = "" # clear driver string + + # For kernels >= 3.15 driver_override can be used to specify the driver + # for a device rather than relying on the driver to provide a positive + # match of the device. The existing process of looking up + # the vendor and device ID, adding them to the driver new_id, + # will erroneously bind other devices too which has the additional burden + # of unbinding those devices + if driver in dpdk_drivers: + filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id + if os.path.exists(filename): + try: + f = open(filename, "w") + except: + print("Error: bind failed for %s - Cannot open %s" + % (dev_id, filename), file=sys.stderr) + return + try: + f.write("%s" % driver) + f.close() + except: + print("Error: bind failed for %s - Cannot write driver %s to " + "PCI ID " % (dev_id, driver), file=sys.stderr) + return + # For kernels < 3.15 use new_id to add PCI id's to the driver + else: + filename = "/sys/bus/pci/drivers/%s/new_id" % driver + try: + f = open(filename, "w") + except: + print("Error: bind failed for %s - Cannot open %s" + % (dev_id, filename), file=sys.stderr) + return + try: + # Convert Device and Vendor Id to int to write to new_id + f.write("%04x %04x" % (int(dev["Vendor"],16), + int(dev["Device"], 16))) + f.close() + except: + print("Error: bind failed for %s - Cannot write new PCI ID to " + "driver %s" % (dev_id, driver), file=sys.stderr) + return + + # do the bind by writing to /sys + filename = "/sys/bus/pci/drivers/%s/bind" % driver + try: + f = open(filename, "a") + except: + print("Error: bind failed for %s - Cannot open %s" + % (dev_id, filename), file=sys.stderr) + if saved_driver is not None: # restore any previous driver + bind_one(dev_id, saved_driver, force) + return + try: + f.write(dev_id) + f.close() + except: + # for some reason, closing dev_id after adding a new PCI ID to new_id + # results in IOError. however, if the device was successfully bound, + # we don't care for any errors and can safely ignore IOError + tmp = get_pci_device_details(dev_id, True) + if "Driver_str" in tmp and tmp["Driver_str"] == driver: + return + print("Error: bind failed for %s - Cannot bind to driver %s" + % (dev_id, driver), file=sys.stderr) + if saved_driver is not None: # restore any previous driver + bind_one(dev_id, saved_driver, force) + return + + # For kernels > 3.15 driver_override is used to bind a device to a driver. + # Before unbinding it, overwrite driver_override with empty string so that + # the device can be bound to any other driver + filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id + if os.path.exists(filename): + try: + f = open(filename, "w") + except: + sys.exit("Error: unbind failed for %s - Cannot open %s" + % (dev_id, filename)) + try: + f.write("\00") + f.close() + except: + sys.exit("Error: unbind failed for %s - Cannot open %s" + % (dev_id, filename)) + + +def unbind_all(dev_list, force=False): + """Unbind method, takes a list of device locations""" + + if dev_list[0] == "dpdk": + for d in devices.keys(): + if "Driver_str" in devices[d]: + if devices[d]["Driver_str"] in dpdk_drivers: + unbind_one(devices[d]["Slot"], force) + return + + try: + dev_list = map(dev_id_from_dev_name, dev_list) + except ValueError as ex: + print(ex) + sys.exit(1) + + for d in dev_list: + unbind_one(d, force) + + +def bind_all(dev_list, driver, force=False): + """Bind method, takes a list of device locations""" + global devices + + # a common user error is to forget to specify the driver the devices need to + # be bound to. check if the driver is a valid device, and if it is, show + # a meaningful error. + try: + dev_id_from_dev_name(driver) + # if we've made it this far, this means that the "driver" was a valid + # device string, so it's probably not a valid driver name. + sys.exit("Error: Driver '%s' does not look like a valid driver. " \ + "Did you forget to specify the driver to bind devices to?" % driver) + except ValueError: + # driver generated error - it's not a valid device ID, so all is well + pass + + # check if we're attempting to bind to a driver that isn't loaded + if not module_is_loaded(driver.replace('-','_')): + sys.exit("Error: Driver '%s' is not loaded." % driver) + + try: + dev_list = map(dev_id_from_dev_name, dev_list) + except ValueError as ex: + sys.exit(ex) + + for d in dev_list: + bind_one(d, driver, force) + + # For kernels < 3.15 when binding devices to a generic driver + # (i.e. one that doesn't have a PCI ID table) using new_id, some devices + # that are not bound to any other driver could be bound even if no one has + # asked them to. hence, we check the list of drivers again, and see if + # some of the previously-unbound devices were erroneously bound. + if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d): + for d in devices.keys(): + # skip devices that were already bound or that we know should be bound + if "Driver_str" in devices[d] or d in dev_list: + continue + + # update information about this device + devices[d] = dict(devices[d].items() + + get_pci_device_details(d, True).items()) + + # check if updated information indicates that the device was bound + if "Driver_str" in devices[d]: + unbind_one(d, force) + + +def display_devices(title, dev_list, extra_params=None): + '''Displays to the user the details of a list of devices given in + "dev_list". The "extra_params" parameter, if given, should contain a string + with %()s fields in it for replacement by the named fields in each + device's dictionary.''' + strings = [] # this holds the strings to print. We sort before printing + print("\n%s" % title) + print("="*len(title)) + if len(dev_list) == 0: + strings.append("<none>") + else: + for dev in dev_list: + if extra_params is not None: + strings.append("%s '%s %s' %s" % (dev["Slot"], + dev["Device_str"], + dev["Device"], + extra_params % dev)) + else: + strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"])) + # sort before printing, so that the entries appear in PCI order + strings.sort() + print("\n".join(strings)) # print one per line + +def show_device_status(devices_type, device_name): + global dpdk_drivers + kernel_drv = [] + dpdk_drv = [] + no_drv = [] + + # split our list of network devices into the three categories above + for d in devices.keys(): + if device_type_match(devices[d], devices_type): + if not has_driver(d): + no_drv.append(devices[d]) + continue + if devices[d]["Driver_str"] in dpdk_drivers: + dpdk_drv.append(devices[d]) + else: + kernel_drv.append(devices[d]) + + n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv) + + # don't bother displaying anything if there are no devices + if n_devs == 0: + msg = "No '%s' devices detected" % device_name + print("") + print(msg) + print("".join('=' * len(msg))) + return + + # print each category separately, so we can clearly see what's used by DPDK + if len(dpdk_drv) != 0: + display_devices("%s devices using DPDK-compatible driver" % device_name, + dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s") + if len(kernel_drv) != 0: + display_devices("%s devices using kernel driver" % device_name, kernel_drv, + "if=%(Interface)s drv=%(Driver_str)s " + "unused=%(Module_str)s %(Active)s") + if len(no_drv) != 0: + display_devices("Other %s devices" % device_name, no_drv, + "unused=%(Module_str)s") + +def show_status(): + '''Function called when the script is passed the "--status" option. + Displays to the user what devices are bound to the igb_uio driver, the + kernel driver or to no driver''' + + if status_dev == "net" or status_dev == "all": + show_device_status(network_devices, "Network") + + if status_dev == "baseband" or status_dev == "all": + show_device_status(baseband_devices, "Baseband") + + if status_dev == "crypto" or status_dev == "all": + show_device_status(crypto_devices, "Crypto") + + if status_dev == "event" or status_dev == "all": + show_device_status(eventdev_devices, "Eventdev") + + if status_dev == "mempool" or status_dev == "all": + show_device_status(mempool_devices, "Mempool") + + if status_dev == "compress" or status_dev == "all": + show_device_status(compress_devices , "Compress") + + if status_dev == "misc" or status_dev == "all": + show_device_status(misc_devices, "Misc (rawdev)") + +def parse_args(): + '''Parses the command-line arguments given by the user and takes the + appropriate action for each''' + global b_flag + global status_flag + global status_dev + global force_flag + global args + if len(sys.argv) <= 1: + usage() + sys.exit(0) + + try: + opts, args = getopt.getopt(sys.argv[1:], "b:us", + ["help", "usage", "status", "status-dev=", + "force", "bind=", "unbind", ]) + except getopt.GetoptError as error: + print(str(error)) + print("Run '%s --usage' for further information" % sys.argv[0]) + sys.exit(1) + + for opt, arg in opts: + if opt == "--help" or opt == "--usage": + usage() + sys.exit(0) + if opt == "--status-dev": + status_flag = True + status_dev = arg + if opt == "--status" or opt == "-s": + status_flag = True + status_dev = "all" + if opt == "--force": + force_flag = True + if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind": + if b_flag is not None: + sys.exit("Error: binding and unbinding are mutually exclusive") + if opt == "-u" or opt == "--unbind": + b_flag = "none" + else: + b_flag = arg + + +def do_arg_actions(): + '''do the actual action requested by the user''' + global b_flag + global status_flag + global force_flag + global args + + if b_flag is None and not status_flag: + print("Error: No action specified for devices. " + "Please give a -b or -u option", file=sys.stderr) + usage() + sys.exit(1) + + if b_flag is not None and len(args) == 0: + print("Error: No devices specified.", file=sys.stderr) + usage() + sys.exit(1) + + if b_flag == "none" or b_flag == "None": + unbind_all(args, force_flag) + elif b_flag is not None: + bind_all(args, b_flag, force_flag) + if status_flag: + if b_flag is not None: + clear_data() + # refresh if we have changed anything + get_device_details(network_devices) + get_device_details(baseband_devices) + get_device_details(crypto_devices) + get_device_details(eventdev_devices) + get_device_details(mempool_devices) + get_device_details(compress_devices) + get_device_details(misc_devices) + show_status() + + +def main(): + '''program main function''' + # check if lspci is installed, suppress any output + with open(os.devnull, 'w') as devnull: + ret = subprocess.call(['which', 'lspci'], + stdout=devnull, stderr=devnull) + if ret != 0: + sys.exit("'lspci' not found - please install 'pciutils'") + parse_args() + check_modules() + clear_data() + get_device_details(network_devices) + get_device_details(baseband_devices) + get_device_details(crypto_devices) + get_device_details(eventdev_devices) + get_device_details(mempool_devices) + get_device_details(compress_devices) + get_device_details(misc_devices) + do_arg_actions() + +if __name__ == "__main__": + main() diff --git a/src/spdk/dpdk/usertools/dpdk-pmdinfo.py b/src/spdk/dpdk/usertools/dpdk-pmdinfo.py new file mode 100755 index 000000000..12f20735e --- /dev/null +++ b/src/spdk/dpdk/usertools/dpdk-pmdinfo.py @@ -0,0 +1,642 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2016 Neil Horman <nhorman@tuxdriver.com> + +# ------------------------------------------------------------------------- +# +# Utility to dump PMD_INFO_STRING support from an object file +# +# ------------------------------------------------------------------------- +from __future__ import print_function +from __future__ import unicode_literals +import json +import io +import os +import platform +import string +import sys +from elftools.common.exceptions import ELFError +from elftools.common.py3compat import byte2int +from elftools.elf.elffile import ELFFile +from optparse import OptionParser + +# For running from development directory. It should take precedence over the +# installed pyelftools. +sys.path.insert(0, '.') + +raw_output = False +pcidb = None + +# =========================================== + + +class Vendor: + """ + Class for vendors. This is the top level class + for the devices belong to a specific vendor. + self.devices is the device dictionary + subdevices are in each device. + """ + + def __init__(self, vendorStr): + """ + Class initializes with the raw line from pci.ids + Parsing takes place inside __init__ + """ + self.ID = vendorStr.split()[0] + self.name = vendorStr.replace("%s " % self.ID, "").rstrip() + self.devices = {} + + def addDevice(self, deviceStr): + """ + Adds a device to self.devices + takes the raw line from pci.ids + """ + s = deviceStr.strip() + devID = s.split()[0] + if devID in self.devices: + pass + else: + self.devices[devID] = Device(deviceStr) + + def report(self): + print(self.ID, self.name) + for id, dev in self.devices.items(): + dev.report() + + def find_device(self, devid): + # convert to a hex string and remove 0x + devid = hex(devid)[2:] + try: + return self.devices[devid] + except: + return Device("%s Unknown Device" % devid) + + +class Device: + + def __init__(self, deviceStr): + """ + Class for each device. + Each vendor has its own devices dictionary. + """ + s = deviceStr.strip() + self.ID = s.split()[0] + self.name = s.replace("%s " % self.ID, "") + self.subdevices = {} + + def report(self): + print("\t%s\t%s" % (self.ID, self.name)) + for subID, subdev in self.subdevices.items(): + subdev.report() + + def addSubDevice(self, subDeviceStr): + """ + Adds a subvendor, subdevice to device. + Uses raw line from pci.ids + """ + s = subDeviceStr.strip() + spl = s.split() + subVendorID = spl[0] + subDeviceID = spl[1] + subDeviceName = s.split(" ")[-1] + devID = "%s:%s" % (subVendorID, subDeviceID) + self.subdevices[devID] = SubDevice( + subVendorID, subDeviceID, subDeviceName) + + def find_subid(self, subven, subdev): + subven = hex(subven)[2:] + subdev = hex(subdev)[2:] + devid = "%s:%s" % (subven, subdev) + + try: + return self.subdevices[devid] + except: + if (subven == "ffff" and subdev == "ffff"): + return SubDevice("ffff", "ffff", "(All Subdevices)") + else: + return SubDevice(subven, subdev, "(Unknown Subdevice)") + + +class SubDevice: + """ + Class for subdevices. + """ + + def __init__(self, vendor, device, name): + """ + Class initializes with vendorid, deviceid and name + """ + self.vendorID = vendor + self.deviceID = device + self.name = name + + def report(self): + print("\t\t%s\t%s\t%s" % (self.vendorID, self.deviceID, self.name)) + + +class PCIIds: + """ + Top class for all pci.ids entries. + All queries will be asked to this class. + PCIIds.vendors["0e11"].devices["0046"].\ + subdevices["0e11:4091"].name = "Smart Array 6i" + """ + + def __init__(self, filename): + """ + Prepares the directories. + Checks local data file. + Tries to load from local, if not found, downloads from web + """ + self.version = "" + self.date = "" + self.vendors = {} + self.contents = None + self.readLocal(filename) + self.parse() + + def reportVendors(self): + """Reports the vendors + """ + for vid, v in self.vendors.items(): + print(v.ID, v.name) + + def report(self, vendor=None): + """ + Reports everything for all vendors or a specific vendor + PCIIds.report() reports everything + PCIIDs.report("0e11") reports only "Compaq Computer Corporation" + """ + if vendor is not None: + self.vendors[vendor].report() + else: + for vID, v in self.vendors.items(): + v.report() + + def find_vendor(self, vid): + # convert vid to a hex string and remove the 0x + vid = hex(vid)[2:] + + try: + return self.vendors[vid] + except: + return Vendor("%s Unknown Vendor" % (vid)) + + def findDate(self, content): + for l in content: + if l.find("Date:") > -1: + return l.split()[-2].replace("-", "") + return None + + def parse(self): + if len(self.contents) < 1: + print("data/%s-pci.ids not found" % self.date) + else: + vendorID = "" + deviceID = "" + for l in self.contents: + if l[0] == "#": + continue + elif len(l.strip()) == 0: + continue + else: + if l.find("\t\t") == 0: + self.vendors[vendorID].devices[ + deviceID].addSubDevice(l) + elif l.find("\t") == 0: + deviceID = l.strip().split()[0] + self.vendors[vendorID].addDevice(l) + else: + vendorID = l.split()[0] + self.vendors[vendorID] = Vendor(l) + + def readLocal(self, filename): + """ + Reads the local file + """ + with io.open(filename, 'r', encoding='utf-8') as f: + self.contents = f.readlines() + self.date = self.findDate(self.contents) + + def loadLocal(self): + """ + Loads database from local. If there is no file, + it creates a new one from web + """ + self.date = idsfile[0].split("/")[1].split("-")[0] + self.readLocal() + + +# ======================================= + +def search_file(filename, search_path): + """ Given a search path, find file with requested name """ + for path in string.split(search_path, ":"): + candidate = os.path.join(path, filename) + if os.path.exists(candidate): + return os.path.abspath(candidate) + return None + + +class ReadElf(object): + """ display_* methods are used to emit output into the output stream + """ + + def __init__(self, file, output): + """ file: + stream object with the ELF file to read + + output: + output stream to write to + """ + self.elffile = ELFFile(file) + self.output = output + + # Lazily initialized if a debug dump is requested + self._dwarfinfo = None + + self._versioninfo = None + + def _section_from_spec(self, spec): + """ Retrieve a section given a "spec" (either number or name). + Return None if no such section exists in the file. + """ + try: + num = int(spec) + if num < self.elffile.num_sections(): + return self.elffile.get_section(num) + else: + return None + except ValueError: + # Not a number. Must be a name then + section = self.elffile.get_section_by_name(force_unicode(spec)) + if section is None: + # No match with a unicode name. + # Some versions of pyelftools (<= 0.23) store internal strings + # as bytes. Try again with the name encoded as bytes. + section = self.elffile.get_section_by_name(force_bytes(spec)) + return section + + def pretty_print_pmdinfo(self, pmdinfo): + global pcidb + + for i in pmdinfo["pci_ids"]: + vendor = pcidb.find_vendor(i[0]) + device = vendor.find_device(i[1]) + subdev = device.find_subid(i[2], i[3]) + print("%s (%s) : %s (%s) %s" % + (vendor.name, vendor.ID, device.name, + device.ID, subdev.name)) + + def parse_pmd_info_string(self, mystring): + global raw_output + global pcidb + + optional_pmd_info = [ + {'id': 'params', 'tag': 'PMD PARAMETERS'}, + {'id': 'kmod', 'tag': 'PMD KMOD DEPENDENCIES'} + ] + + i = mystring.index("=") + mystring = mystring[i + 2:] + pmdinfo = json.loads(mystring) + + if raw_output: + print(json.dumps(pmdinfo)) + return + + print("PMD NAME: " + pmdinfo["name"]) + for i in optional_pmd_info: + try: + print("%s: %s" % (i['tag'], pmdinfo[i['id']])) + except KeyError: + continue + + if (len(pmdinfo["pci_ids"]) != 0): + print("PMD HW SUPPORT:") + if pcidb is not None: + self.pretty_print_pmdinfo(pmdinfo) + else: + print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE") + for i in pmdinfo["pci_ids"]: + print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" % + (i[0], i[1], i[2], i[3])) + + print("") + + def display_pmd_info_strings(self, section_spec): + """ Display a strings dump of a section. section_spec is either a + section number or a name. + """ + section = self._section_from_spec(section_spec) + if section is None: + return + + data = section.data() + dataptr = 0 + + while dataptr < len(data): + while (dataptr < len(data) and + not (32 <= byte2int(data[dataptr]) <= 127)): + dataptr += 1 + + if dataptr >= len(data): + break + + endptr = dataptr + while endptr < len(data) and byte2int(data[endptr]) != 0: + endptr += 1 + + # pyelftools may return byte-strings, force decode them + mystring = force_unicode(data[dataptr:endptr]) + rc = mystring.find("PMD_INFO_STRING") + if (rc != -1): + self.parse_pmd_info_string(mystring) + + dataptr = endptr + + def find_librte_eal(self, section): + for tag in section.iter_tags(): + # pyelftools may return byte-strings, force decode them + if force_unicode(tag.entry.d_tag) == 'DT_NEEDED': + if "librte_eal" in force_unicode(tag.needed): + return force_unicode(tag.needed) + return None + + def search_for_autoload_path(self): + scanelf = self + scanfile = None + library = None + + section = self._section_from_spec(".dynamic") + try: + eallib = self.find_librte_eal(section) + if eallib is not None: + ldlibpath = os.environ.get('LD_LIBRARY_PATH') + if ldlibpath is None: + ldlibpath = "" + dtr = self.get_dt_runpath(section) + library = search_file(eallib, + dtr + ":" + ldlibpath + + ":/usr/lib64:/lib64:/usr/lib:/lib") + if library is None: + return (None, None) + if raw_output is False: + print("Scanning for autoload path in %s" % library) + scanfile = io.open(library, 'rb') + scanelf = ReadElf(scanfile, sys.stdout) + except AttributeError: + # Not a dynamic binary + pass + except ELFError: + scanfile.close() + return (None, None) + + section = scanelf._section_from_spec(".rodata") + if section is None: + if scanfile is not None: + scanfile.close() + return (None, None) + + data = section.data() + dataptr = 0 + + while dataptr < len(data): + while (dataptr < len(data) and + not (32 <= byte2int(data[dataptr]) <= 127)): + dataptr += 1 + + if dataptr >= len(data): + break + + endptr = dataptr + while endptr < len(data) and byte2int(data[endptr]) != 0: + endptr += 1 + + # pyelftools may return byte-strings, force decode them + mystring = force_unicode(data[dataptr:endptr]) + rc = mystring.find("DPDK_PLUGIN_PATH") + if (rc != -1): + rc = mystring.find("=") + return (mystring[rc + 1:], library) + + dataptr = endptr + if scanfile is not None: + scanfile.close() + return (None, None) + + def get_dt_runpath(self, dynsec): + for tag in dynsec.iter_tags(): + # pyelftools may return byte-strings, force decode them + if force_unicode(tag.entry.d_tag) == 'DT_RUNPATH': + return force_unicode(tag.runpath) + return "" + + def process_dt_needed_entries(self): + """ Look to see if there are any DT_NEEDED entries in the binary + And process those if there are + """ + global raw_output + runpath = "" + ldlibpath = os.environ.get('LD_LIBRARY_PATH') + if ldlibpath is None: + ldlibpath = "" + + dynsec = self._section_from_spec(".dynamic") + try: + runpath = self.get_dt_runpath(dynsec) + except AttributeError: + # dynsec is None, just return + return + + for tag in dynsec.iter_tags(): + # pyelftools may return byte-strings, force decode them + if force_unicode(tag.entry.d_tag) == 'DT_NEEDED': + if 'librte_pmd' in force_unicode(tag.needed): + library = search_file(force_unicode(tag.needed), + runpath + ":" + ldlibpath + + ":/usr/lib64:/lib64:/usr/lib:/lib") + if library is not None: + if raw_output is False: + print("Scanning %s for pmd information" % library) + with io.open(library, 'rb') as file: + try: + libelf = ReadElf(file, sys.stdout) + except ELFError: + print("%s is no an ELF file" % library) + continue + libelf.process_dt_needed_entries() + libelf.display_pmd_info_strings(".rodata") + file.close() + + +# compat: remove force_unicode & force_bytes when pyelftools<=0.23 support is +# dropped. +def force_unicode(s): + if hasattr(s, 'decode') and callable(s.decode): + s = s.decode('latin-1') # same encoding used in pyelftools py3compat + return s + + +def force_bytes(s): + if hasattr(s, 'encode') and callable(s.encode): + s = s.encode('latin-1') # same encoding used in pyelftools py3compat + return s + + +def scan_autoload_path(autoload_path): + global raw_output + + if os.path.exists(autoload_path) is False: + return + + try: + dirs = os.listdir(autoload_path) + except OSError: + # Couldn't read the directory, give up + return + + for d in dirs: + dpath = os.path.join(autoload_path, d) + if os.path.isdir(dpath): + scan_autoload_path(dpath) + if os.path.isfile(dpath): + try: + file = io.open(dpath, 'rb') + readelf = ReadElf(file, sys.stdout) + except ELFError: + # this is likely not an elf file, skip it + continue + except IOError: + # No permission to read the file, skip it + continue + + if raw_output is False: + print("Hw Support for library %s" % d) + readelf.display_pmd_info_strings(".rodata") + file.close() + + +def scan_for_autoload_pmds(dpdk_path): + """ + search the specified application or path for a pmd autoload path + then scan said path for pmds and report hw support + """ + global raw_output + + if (os.path.isfile(dpdk_path) is False): + if raw_output is False: + print("Must specify a file name") + return + + file = io.open(dpdk_path, 'rb') + try: + readelf = ReadElf(file, sys.stdout) + except ElfError: + if raw_output is False: + print("Unable to parse %s" % file) + return + + (autoload_path, scannedfile) = readelf.search_for_autoload_path() + if not autoload_path: + if (raw_output is False): + print("No autoload path configured in %s" % dpdk_path) + return + if (raw_output is False): + if (scannedfile is None): + scannedfile = dpdk_path + print("Found autoload path %s in %s" % (autoload_path, scannedfile)) + + file.close() + if (raw_output is False): + print("Discovered Autoload HW Support:") + scan_autoload_path(autoload_path) + return + + +def main(stream=None): + global raw_output + global pcidb + + pcifile_default = "./pci.ids" # For unknown OS's assume local file + if platform.system() == 'Linux': + # hwdata is the legacy location, misc is supported going forward + pcifile_default = "/usr/share/misc/pci.ids" + if not os.path.exists(pcifile_default): + pcifile_default = "/usr/share/hwdata/pci.ids" + elif platform.system() == 'FreeBSD': + pcifile_default = "/usr/local/share/pciids/pci.ids" + if not os.path.exists(pcifile_default): + pcifile_default = "/usr/share/misc/pci_vendors" + + optparser = OptionParser( + usage='usage: %prog [-hrtp] [-d <pci id file] <elf-file>', + description="Dump pmd hardware support info", + add_help_option=True) + optparser.add_option('-r', '--raw', + action='store_true', dest='raw_output', + help='Dump raw json strings') + optparser.add_option("-d", "--pcidb", dest="pcifile", + help="specify a pci database " + "to get vendor names from", + default=pcifile_default, metavar="FILE") + optparser.add_option("-t", "--table", dest="tblout", + help="output information on hw support as a " + "hex table", + action='store_true') + optparser.add_option("-p", "--plugindir", dest="pdir", + help="scan dpdk for autoload plugins", + action='store_true') + + options, args = optparser.parse_args() + + if options.raw_output: + raw_output = True + + if options.pcifile: + pcidb = PCIIds(options.pcifile) + if pcidb is None: + print("Pci DB file not found") + exit(1) + + if options.tblout: + options.pcifile = None + pcidb = None + + if (len(args) == 0): + optparser.print_usage() + exit(1) + + if options.pdir is True: + exit(scan_for_autoload_pmds(args[0])) + + ldlibpath = os.environ.get('LD_LIBRARY_PATH') + if (ldlibpath is None): + ldlibpath = "" + + if (os.path.exists(args[0]) is True): + myelffile = args[0] + else: + myelffile = search_file( + args[0], ldlibpath + ":/usr/lib64:/lib64:/usr/lib:/lib") + + if (myelffile is None): + print("File not found") + sys.exit(1) + + with io.open(myelffile, 'rb') as file: + try: + readelf = ReadElf(file, sys.stdout) + readelf.process_dt_needed_entries() + readelf.display_pmd_info_strings(".rodata") + sys.exit(0) + + except ELFError as ex: + sys.stderr.write('ELF error: %s\n' % ex) + sys.exit(1) + + +# ------------------------------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/src/spdk/dpdk/usertools/dpdk-setup.sh b/src/spdk/dpdk/usertools/dpdk-setup.sh new file mode 100755 index 000000000..e5bbe9fee --- /dev/null +++ b/src/spdk/dpdk/usertools/dpdk-setup.sh @@ -0,0 +1,605 @@ +#! /bin/bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation + +# +# Run with "source /path/to/dpdk-setup.sh" +# + +# +# Change to DPDK directory ( <this-script's-dir>/.. ), and export it as RTE_SDK +# +cd $(dirname ${BASH_SOURCE[0]})/.. +export RTE_SDK=$PWD +echo "------------------------------------------------------------------------------" +echo " RTE_SDK exported as $RTE_SDK" +echo "------------------------------------------------------------------------------" + +HUGEPGSZ=`cat /proc/meminfo | grep Hugepagesize | cut -d : -f 2 | tr -d ' '` + +# +# Application EAL parameters for setting memory options (amount/channels/ranks). +# +EAL_PARAMS='-n 4' + +# +# Sets QUIT variable so script will finish. +# +quit() +{ + QUIT=$1 +} + +# Shortcut for quit. +q() +{ + quit +} + +# +# Sets up environmental variables for ICC. +# +setup_icc() +{ + DEFAULT_PATH=/opt/intel/bin/iccvars.sh + param=$1 + shpath=`which iccvars.sh 2> /dev/null` + if [ $? -eq 0 ] ; then + echo "Loading iccvars.sh from $shpath for $param" + source $shpath $param + elif [ -f $DEFAULT_PATH ] ; then + echo "Loading iccvars.sh from $DEFAULT_PATH for $param" + source $DEFAULT_PATH $param + else + echo "## ERROR: cannot find 'iccvars.sh' script to set up ICC." + echo "## To fix, please add the directory that contains" + echo "## iccvars.sh to your 'PATH' environment variable." + quit + fi +} + +# +# Sets RTE_TARGET and does a "make install". +# +setup_target() +{ + option=$1 + export RTE_TARGET=${TARGETS[option]} + + compiler=${RTE_TARGET##*-} + if [ "$compiler" == "icc" ] ; then + platform=${RTE_TARGET%%-*} + if [ "$platform" == "x86_64" ] ; then + setup_icc intel64 + else + setup_icc ia32 + fi + fi + if [ "$QUIT" == "0" ] ; then + make install T=${RTE_TARGET} + fi + echo "------------------------------------------------------------------------------" + echo " RTE_TARGET exported as $RTE_TARGET" + echo "------------------------------------------------------------------------------" +} + +# +# Creates hugepage filesystem. +# +create_mnt_huge() +{ + echo "Creating /mnt/huge and mounting as hugetlbfs" + sudo mkdir -p /mnt/huge + + grep -s '/mnt/huge' /proc/mounts > /dev/null + if [ $? -ne 0 ] ; then + sudo mount -t hugetlbfs nodev /mnt/huge + fi +} + +# +# Removes hugepage filesystem. +# +remove_mnt_huge() +{ + echo "Unmounting /mnt/huge and removing directory" + grep -s '/mnt/huge' /proc/mounts > /dev/null + if [ $? -eq 0 ] ; then + sudo umount /mnt/huge + fi + + if [ -d /mnt/huge ] ; then + sudo rm -R /mnt/huge + fi +} + +# +# Unloads igb_uio.ko. +# +remove_igb_uio_module() +{ + echo "Unloading any existing DPDK UIO module" + /sbin/lsmod | grep -s igb_uio > /dev/null + if [ $? -eq 0 ] ; then + sudo /sbin/rmmod igb_uio + fi +} + +# +# Loads new igb_uio.ko (and uio module if needed). +# +load_igb_uio_module() +{ + if [ ! -f $RTE_SDK/$RTE_TARGET/kmod/igb_uio.ko ];then + echo "## ERROR: Target does not have the DPDK UIO Kernel Module." + echo " To fix, please try to rebuild target." + return + fi + + remove_igb_uio_module + + /sbin/lsmod | grep -s uio > /dev/null + if [ $? -ne 0 ] ; then + modinfo uio > /dev/null + if [ $? -eq 0 ]; then + echo "Loading uio module" + sudo /sbin/modprobe uio + fi + fi + + # UIO may be compiled into kernel, so it may not be an error if it can't + # be loaded. + + echo "Loading DPDK UIO module" + sudo /sbin/insmod $RTE_SDK/$RTE_TARGET/kmod/igb_uio.ko + if [ $? -ne 0 ] ; then + echo "## ERROR: Could not load kmod/igb_uio.ko." + quit + fi +} + +# +# Unloads VFIO modules. +# +remove_vfio_module() +{ + echo "Unloading any existing VFIO module" + /sbin/lsmod | grep -s vfio > /dev/null + if [ $? -eq 0 ] ; then + sudo /sbin/rmmod vfio-pci + sudo /sbin/rmmod vfio_iommu_type1 + sudo /sbin/rmmod vfio + fi +} + +# +# Loads new vfio-pci (and vfio module if needed). +# +load_vfio_module() +{ + remove_vfio_module + + VFIO_PATH="kernel/drivers/vfio/pci/vfio-pci.ko" + + echo "Loading VFIO module" + /sbin/lsmod | grep -s vfio_pci > /dev/null + if [ $? -ne 0 ] ; then + if [ -f /lib/modules/$(uname -r)/$VFIO_PATH ] ; then + sudo /sbin/modprobe vfio-pci + fi + fi + + # make sure regular users can read /dev/vfio + echo "chmod /dev/vfio" + sudo chmod a+x /dev/vfio + if [ $? -ne 0 ] ; then + echo "FAIL" + quit + fi + echo "OK" + + # check if /dev/vfio/vfio exists - that way we + # know we either loaded the module, or it was + # compiled into the kernel + if [ ! -e /dev/vfio/vfio ] ; then + echo "## ERROR: VFIO not found!" + fi +} + +# +# Unloads the rte_kni.ko module. +# +remove_kni_module() +{ + echo "Unloading any existing DPDK KNI module" + /sbin/lsmod | grep -s rte_kni > /dev/null + if [ $? -eq 0 ] ; then + sudo /sbin/rmmod rte_kni + fi +} + +# +# Loads the rte_kni.ko module. +# +load_kni_module() +{ + # Check that the KNI module is already built. + if [ ! -f $RTE_SDK/$RTE_TARGET/kmod/rte_kni.ko ];then + echo "## ERROR: Target does not have the DPDK KNI Module." + echo " To fix, please try to rebuild target." + return + fi + + # Unload existing version if present. + remove_kni_module + + # Now try load the KNI module. + echo "Loading DPDK KNI module" + sudo /sbin/insmod $RTE_SDK/$RTE_TARGET/kmod/rte_kni.ko + if [ $? -ne 0 ] ; then + echo "## ERROR: Could not load kmod/rte_kni.ko." + quit + fi +} + +# +# Sets appropriate permissions on /dev/vfio/* files +# +set_vfio_permissions() +{ + # make sure regular users can read /dev/vfio + echo "chmod /dev/vfio" + sudo chmod a+x /dev/vfio + if [ $? -ne 0 ] ; then + echo "FAIL" + quit + fi + echo "OK" + + # make sure regular user can access everything inside /dev/vfio + echo "chmod /dev/vfio/*" + sudo chmod 0666 /dev/vfio/* + if [ $? -ne 0 ] ; then + echo "FAIL" + quit + fi + echo "OK" + + # since permissions are only to be set when running as + # regular user, we only check ulimit here + # + # warn if regular user is only allowed + # to memlock <64M of memory + MEMLOCK_AMNT=`ulimit -l` + + if [ "$MEMLOCK_AMNT" != "unlimited" ] ; then + MEMLOCK_MB=`expr $MEMLOCK_AMNT / 1024` + echo "" + echo "Current user memlock limit: ${MEMLOCK_MB} MB" + echo "" + echo "This is the maximum amount of memory you will be" + echo "able to use with DPDK and VFIO if run as current user." + echo -n "To change this, please adjust limits.conf memlock " + echo "limit for current user." + + if [ $MEMLOCK_AMNT -lt 65536 ] ; then + echo "" + echo "## WARNING: memlock limit is less than 64MB" + echo -n "## DPDK with VFIO may not be able to initialize " + echo "if run as current user." + fi + fi +} + +# +# Removes all reserved hugepages. +# +clear_huge_pages() +{ + echo > .echo_tmp + for d in /sys/devices/system/node/node? ; do + echo "echo 0 > $d/hugepages/hugepages-${HUGEPGSZ}/nr_hugepages" >> .echo_tmp + done + echo "Removing currently reserved hugepages" + sudo sh .echo_tmp + rm -f .echo_tmp + + remove_mnt_huge +} + +# +# Creates hugepages. +# +set_non_numa_pages() +{ + clear_huge_pages + + echo "" + echo " Input the number of ${HUGEPGSZ} hugepages" + echo " Example: to have 128MB of hugepages available in a 2MB huge page system," + echo " enter '64' to reserve 64 * 2MB pages" + echo -n "Number of pages: " + read Pages + + echo "echo $Pages > /sys/kernel/mm/hugepages/hugepages-${HUGEPGSZ}/nr_hugepages" > .echo_tmp + + echo "Reserving hugepages" + sudo sh .echo_tmp + rm -f .echo_tmp + + create_mnt_huge +} + +# +# Creates hugepages on specific NUMA nodes. +# +set_numa_pages() +{ + clear_huge_pages + + echo "" + echo " Input the number of ${HUGEPGSZ} hugepages for each node" + echo " Example: to have 128MB of hugepages available per node in a 2MB huge page system," + echo " enter '64' to reserve 64 * 2MB pages on each node" + + echo > .echo_tmp + for d in /sys/devices/system/node/node? ; do + node=$(basename $d) + echo -n "Number of pages for $node: " + read Pages + echo "echo $Pages > $d/hugepages/hugepages-${HUGEPGSZ}/nr_hugepages" >> .echo_tmp + done + echo "Reserving hugepages" + sudo sh .echo_tmp + rm -f .echo_tmp + + create_mnt_huge +} + +# +# Run unit test application. +# +run_test_app() +{ + echo "" + echo " Enter hex bitmask of cores to execute test app on" + echo " Example: to execute app on cores 0 to 7, enter 0xff" + echo -n "bitmask: " + read Bitmask + echo "Launching app" + sudo ${RTE_TARGET}/app/test -c $Bitmask $EAL_PARAMS +} + +# +# Run unit testpmd application. +# +run_testpmd_app() +{ + echo "" + echo " Enter hex bitmask of cores to execute testpmd app on" + echo " Example: to execute app on cores 0 to 7, enter 0xff" + echo -n "bitmask: " + read Bitmask + echo "Launching app" + sudo ${RTE_TARGET}/app/testpmd -c $Bitmask $EAL_PARAMS -- -i +} + +# +# Print hugepage information. +# +grep_meminfo() +{ + grep -i huge /proc/meminfo +} + +# +# Calls dpdk-devbind.py --status to show the devices and what they +# are all bound to, in terms of drivers. +# +show_devices() +{ + if [ -d /sys/module/vfio_pci -o -d /sys/module/igb_uio ]; then + ${RTE_SDK}/usertools/dpdk-devbind.py --status + else + echo "# Please load the 'igb_uio' or 'vfio-pci' kernel module before " + echo "# querying or adjusting device bindings" + fi +} + +# +# Uses dpdk-devbind.py to move devices to work with vfio-pci +# +bind_devices_to_vfio() +{ + if [ -d /sys/module/vfio_pci ]; then + ${RTE_SDK}/usertools/dpdk-devbind.py --status + echo "" + echo -n "Enter PCI address of device to bind to VFIO driver: " + read PCI_PATH + sudo ${RTE_SDK}/usertools/dpdk-devbind.py -b vfio-pci $PCI_PATH && + echo "OK" + else + echo "# Please load the 'vfio-pci' kernel module before querying or " + echo "# adjusting device bindings" + fi +} + +# +# Uses dpdk-devbind.py to move devices to work with igb_uio +# +bind_devices_to_igb_uio() +{ + if [ -d /sys/module/igb_uio ]; then + ${RTE_SDK}/usertools/dpdk-devbind.py --status + echo "" + echo -n "Enter PCI address of device to bind to IGB UIO driver: " + read PCI_PATH + sudo ${RTE_SDK}/usertools/dpdk-devbind.py -b igb_uio $PCI_PATH && echo "OK" + else + echo "# Please load the 'igb_uio' kernel module before querying or " + echo "# adjusting device bindings" + fi +} + +# +# Uses dpdk-devbind.py to move devices to work with kernel drivers again +# +unbind_devices() +{ + ${RTE_SDK}/usertools/dpdk-devbind.py --status + echo "" + echo -n "Enter PCI address of device to unbind: " + read PCI_PATH + echo "" + echo -n "Enter name of kernel driver to bind the device to: " + read DRV + sudo ${RTE_SDK}/usertools/dpdk-devbind.py -b $DRV $PCI_PATH && echo "OK" +} + +# +# Options for building a target. Note that this step MUST be first as it sets +# up TARGETS[] starting from 1, and this is accessed in setup_target using the +# user entered option. +# +step1_func() +{ + TITLE="Select the DPDK environment to build" + CONFIG_NUM=1 + for cfg in config/defconfig_* ; do + cfg=${cfg/config\/defconfig_/} + TEXT[$CONFIG_NUM]="$cfg" + TARGETS[$CONFIG_NUM]=$cfg + FUNC[$CONFIG_NUM]="setup_target" + let "CONFIG_NUM+=1" + done +} + +# +# Options for setting up environment. +# +step2_func() +{ + TITLE="Setup linux environment" + + TEXT[1]="Insert IGB UIO module" + FUNC[1]="load_igb_uio_module" + + TEXT[2]="Insert VFIO module" + FUNC[2]="load_vfio_module" + + TEXT[3]="Insert KNI module" + FUNC[3]="load_kni_module" + + TEXT[4]="Setup hugepage mappings for non-NUMA systems" + FUNC[4]="set_non_numa_pages" + + TEXT[5]="Setup hugepage mappings for NUMA systems" + FUNC[5]="set_numa_pages" + + TEXT[6]="Display current Ethernet/Baseband/Crypto device settings" + FUNC[6]="show_devices" + + TEXT[7]="Bind Ethernet/Baseband/Crypto device to IGB UIO module" + FUNC[7]="bind_devices_to_igb_uio" + + TEXT[8]="Bind Ethernet/Baseband/Crypto device to VFIO module" + FUNC[8]="bind_devices_to_vfio" + + TEXT[9]="Setup VFIO permissions" + FUNC[9]="set_vfio_permissions" +} + +# +# Options for running applications. +# +step3_func() +{ + TITLE="Run test application for linux environment" + + TEXT[1]="Run test application (\$RTE_TARGET/app/test)" + FUNC[1]="run_test_app" + + TEXT[2]="Run testpmd application in interactive mode (\$RTE_TARGET/app/testpmd)" + FUNC[2]="run_testpmd_app" +} + +# +# Other options +# +step4_func() +{ + TITLE="Other tools" + + TEXT[1]="List hugepage info from /proc/meminfo" + FUNC[1]="grep_meminfo" + +} + +# +# Options for cleaning up the system +# +step5_func() +{ + TITLE="Uninstall and system cleanup" + + TEXT[1]="Unbind devices from IGB UIO or VFIO driver" + FUNC[1]="unbind_devices" + + TEXT[2]="Remove IGB UIO module" + FUNC[2]="remove_igb_uio_module" + + TEXT[3]="Remove VFIO module" + FUNC[3]="remove_vfio_module" + + TEXT[4]="Remove KNI module" + FUNC[4]="remove_kni_module" + + TEXT[5]="Remove hugepage mappings" + FUNC[5]="clear_huge_pages" +} + +STEPS[1]="step1_func" +STEPS[2]="step2_func" +STEPS[3]="step3_func" +STEPS[4]="step4_func" +STEPS[5]="step5_func" + +QUIT=0 + +while [ "$QUIT" == "0" ]; do + OPTION_NUM=1 + + for s in $(seq ${#STEPS[@]}) ; do + ${STEPS[s]} + + echo "----------------------------------------------------------" + echo " Step $s: ${TITLE}" + echo "----------------------------------------------------------" + + for i in $(seq ${#TEXT[@]}) ; do + echo "[$OPTION_NUM] ${TEXT[i]}" + OPTIONS[$OPTION_NUM]=${FUNC[i]} + let "OPTION_NUM+=1" + done + + # Clear TEXT and FUNC arrays before next step + unset TEXT + unset FUNC + + echo "" + done + + echo "[$OPTION_NUM] Exit Script" + OPTIONS[$OPTION_NUM]="quit" + echo "" + echo -n "Option: " + read our_entry + echo "" + ${OPTIONS[our_entry]} ${our_entry} + + if [ "$QUIT" == "0" ] ; then + echo + echo -n "Press enter to continue ..."; read + fi + +done diff --git a/src/spdk/dpdk/usertools/dpdk-telemetry-client.py b/src/spdk/dpdk/usertools/dpdk-telemetry-client.py new file mode 100755 index 000000000..35edb7cd2 --- /dev/null +++ b/src/spdk/dpdk/usertools/dpdk-telemetry-client.py @@ -0,0 +1,135 @@ +#! /usr/bin/env python +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +from __future__ import print_function +from __future__ import unicode_literals + +import socket +import os +import sys +import time + +BUFFER_SIZE = 200000 + +METRICS_REQ = "{\"action\":0,\"command\":\"ports_all_stat_values\",\"data\":null}" +API_REG = "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\":\"" +API_UNREG = "{\"action\":2,\"command\":\"clients\",\"data\":{\"client_path\":\"" +GLOBAL_METRICS_REQ = "{\"action\":0,\"command\":\"global_stat_values\",\"data\":null}" +DEFAULT_FP = "/var/run/dpdk/default_client" + +try: + raw_input # Python 2 +except NameError: + raw_input = input # Python 3 + +class Socket: + + def __init__(self): + self.send_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.client_fd = None + + def __del__(self): + try: + self.send_fd.close() + self.recv_fd.close() + self.client_fd.close() + except: + print("Error - Sockets could not be closed") + +class Client: + + def __init__(self): # Creates a client instance + self.socket = Socket() + self.file_path = None + self.choice = None + self.unregistered = 0 + + def __del__(self): + try: + if self.unregistered == 0: + self.unregister(); + except: + print("Error - Client could not be destroyed") + + def getFilepath(self, file_path): # Gets arguments from Command-Line and assigns to instance of client + self.file_path = file_path + + def register(self): # Connects a client to DPDK-instance + if os.path.exists(self.file_path): + os.unlink(self.file_path) + try: + self.socket.recv_fd.bind(self.file_path) + except socket.error as msg: + print ("Error - Socket binding error: " + str(msg) + "\n") + self.socket.recv_fd.settimeout(2) + self.socket.send_fd.connect("/var/run/dpdk/rte/telemetry") + JSON = (API_REG + self.file_path + "\"}}") + self.socket.send_fd.sendall(JSON.encode()) + + self.socket.recv_fd.listen(1) + self.socket.client_fd = self.socket.recv_fd.accept()[0] + + def unregister(self): # Unregister a given client + self.socket.client_fd.send((API_UNREG + self.file_path + "\"}}").encode()) + self.socket.client_fd.close() + + def requestMetrics(self): # Requests metrics for given client + self.socket.client_fd.send(METRICS_REQ.encode()) + data = self.socket.client_fd.recv(BUFFER_SIZE).decode() + print("\nResponse: \n", data) + + def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client + print("\nPlease enter the number of times you'd like to continuously request Metrics:") + n_requests = int(raw_input("\n:")) + print("\033[F") #Removes the user input from screen, cleans it up + print("\033[K") + for i in range(n_requests): + self.requestMetrics() + time.sleep(sleep_time) + + def requestGlobalMetrics(self): #Requests global metrics for given client + self.socket.client_fd.send(GLOBAL_METRICS_REQ.encode()) + data = self.socket.client_fd.recv(BUFFER_SIZE).decode() + print("\nResponse: \n", data) + + def interactiveMenu(self, sleep_time): # Creates Interactive menu within the script + while self.choice != 4: + print("\nOptions Menu") + print("[1] Send for Metrics for all ports") + print("[2] Send for Metrics for all ports recursively") + print("[3] Send for global Metrics") + print("[4] Unregister client") + + try: + self.choice = int(raw_input("\n:")) + print("\033[F") #Removes the user input for screen, cleans it up + print("\033[K") + if self.choice == 1: + self.requestMetrics() + elif self.choice == 2: + self.repeatedlyRequestMetrics(sleep_time) + elif self.choice == 3: + self.requestGlobalMetrics() + elif self.choice == 4: + self.unregister() + self.unregistered = 1 + else: + print("Error - Invalid request choice") + except: + pass + +if __name__ == "__main__": + + sleep_time = 1 + file_path = "" + if (len(sys.argv) == 2): + file_path = sys.argv[1] + else: + print("Warning - No filepath passed, using default (" + DEFAULT_FP + ").") + file_path = DEFAULT_FP + client = Client() + client.getFilepath(file_path) + client.register() + client.interactiveMenu(sleep_time) diff --git a/src/spdk/dpdk/usertools/dpdk-telemetry.py b/src/spdk/dpdk/usertools/dpdk-telemetry.py new file mode 100755 index 000000000..afbf01b19 --- /dev/null +++ b/src/spdk/dpdk/usertools/dpdk-telemetry.py @@ -0,0 +1,83 @@ +#! /usr/bin/python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2020 Intel Corporation + +""" +Script to be used with V2 Telemetry. +Allows the user input commands and read the Telemetry response. +""" + +import socket +import os +import glob +import json +import readline + +# global vars +TELEMETRY_VERSION = "v2" +CMDS = [] + + +def read_socket(sock, buf_len, echo=True): + """ Read data from socket and return it in JSON format """ + reply = sock.recv(buf_len).decode() + try: + ret = json.loads(reply) + except json.JSONDecodeError: + print("Error in reply: ", reply) + sock.close() + raise + if echo: + print(json.dumps(ret)) + return ret + + +def handle_socket(path): + """ Connect to socket and handle user input """ + sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + global CMDS + print("Connecting to " + path) + try: + sock.connect(path) + except OSError: + print("Error connecting to " + path) + sock.close() + return + json_reply = read_socket(sock, 1024) + output_buf_len = json_reply["max_output_len"] + + # get list of commands for readline completion + sock.send("/".encode()) + CMDS = read_socket(sock, output_buf_len, False)["/"] + + # interactive prompt + text = input('--> ').strip() + while text != "quit": + if text.startswith('/'): + sock.send(text.encode()) + read_socket(sock, output_buf_len) + text = input('--> ').strip() + sock.close() + + +def readline_complete(text, state): + """ Find any matching commands from the list based on user input """ + all_cmds = ['quit'] + CMDS + if text: + matches = [c for c in all_cmds if c.startswith(text)] + else: + matches = all_cmds + return matches[state] + + +readline.parse_and_bind('tab: complete') +readline.set_completer(readline_complete) +readline.set_completer_delims(readline.get_completer_delims().replace('/', '')) + +# Path to sockets for processes run as a root user +for f in glob.glob('/var/run/dpdk/*/dpdk_telemetry.%s' % TELEMETRY_VERSION): + handle_socket(f) +# Path to sockets for processes run as a regular user +for f in glob.glob('/run/user/%d/dpdk/*/dpdk_telemetry.%s' % + (os.getuid(), TELEMETRY_VERSION)): + handle_socket(f) diff --git a/src/spdk/dpdk/usertools/meson.build b/src/spdk/dpdk/usertools/meson.build new file mode 100644 index 000000000..64e27238f --- /dev/null +++ b/src/spdk/dpdk/usertools/meson.build @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +install_data(['dpdk-devbind.py', 'dpdk-pmdinfo.py', 'dpdk-telemetry.py'], install_dir: 'bin') |