summaryrefslogtreecommitdiffstats
path: root/src/spdk/dpdk/usertools
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/dpdk/usertools
parentInitial commit. (diff)
downloadceph-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-xsrc/spdk/dpdk/usertools/cpu_layout.py73
-rwxr-xr-xsrc/spdk/dpdk/usertools/dpdk-devbind.py746
-rwxr-xr-xsrc/spdk/dpdk/usertools/dpdk-pmdinfo.py642
-rwxr-xr-xsrc/spdk/dpdk/usertools/dpdk-setup.sh605
-rwxr-xr-xsrc/spdk/dpdk/usertools/dpdk-telemetry-client.py135
-rwxr-xr-xsrc/spdk/dpdk/usertools/dpdk-telemetry.py83
-rw-r--r--src/spdk/dpdk/usertools/meson.build4
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')