summaryrefslogtreecommitdiffstats
path: root/src/seastar/dpdk/usertools
diff options
context:
space:
mode:
Diffstat (limited to 'src/seastar/dpdk/usertools')
-rwxr-xr-xsrc/seastar/dpdk/usertools/cpu_layout.py103
-rwxr-xr-xsrc/seastar/dpdk/usertools/dpdk-devbind.py709
-rwxr-xr-xsrc/seastar/dpdk/usertools/dpdk-pmdinfo.py610
-rwxr-xr-xsrc/seastar/dpdk/usertools/dpdk-setup.sh634
4 files changed, 2056 insertions, 0 deletions
diff --git a/src/seastar/dpdk/usertools/cpu_layout.py b/src/seastar/dpdk/usertools/cpu_layout.py
new file mode 100755
index 00000000..93949449
--- /dev/null
+++ b/src/seastar/dpdk/usertools/cpu_layout.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+#
+# BSD LICENSE
+#
+# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+# Copyright(c) 2017 Cavium Networks Ltd. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+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/seastar/dpdk/usertools/dpdk-devbind.py b/src/seastar/dpdk/usertools/dpdk-devbind.py
new file mode 100755
index 00000000..f0225b6c
--- /dev/null
+++ b/src/seastar/dpdk/usertools/dpdk-devbind.py
@@ -0,0 +1,709 @@
+#! /usr/bin/env python
+#
+# BSD LICENSE
+#
+# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+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}
+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}
+
+network_devices = [network_class, cavium_pkx]
+crypto_devices = [encryption_class, intel_processor_class]
+eventdev_devices = [cavium_sso]
+mempool_devices = [cavium_fpa]
+
+# 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"]
+
+# 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", "crypto", "event" and "mempool"
+
+ -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]
+
+
+def find_module(mod):
+ '''find the .ko file for kernel module named mod.
+ Searches the $RTE_SDK/$RTE_TARGET directory, the kernel
+ modules directory and finally under the parent directory of
+ the script '''
+ # check $RTE_SDK/$RTE_TARGET directory
+ if 'RTE_SDK' in os.environ and 'RTE_TARGET' in os.environ:
+ path = "%s/%s/kmod/%s.ko" % (os.environ['RTE_SDK'],
+ os.environ['RTE_TARGET'], mod)
+ if exists(path):
+ return path
+
+ # check using depmod
+ try:
+ with open(os.devnull, "w") as fnull:
+ path = check_output(["modinfo", "-n", mod], stderr=fnull).strip()
+
+ if path and exists(path):
+ return path
+ except: # if modinfo can't find module, it fails, so continue
+ pass
+
+ # check for a copy based off current path
+ tools_dir = dirname(abspath(sys.argv[0]))
+ if tools_dir.endswith("tools"):
+ base_dir = dirname(tools_dir)
+ find_out = check_output(["find", base_dir, "-name", mod + ".ko"])
+ if len(find_out) > 0: # something matched
+ path = find_out.splitlines()[0]
+ if exists(path):
+ return path
+
+
+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
+ try:
+ # 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 = [os.path.join(sysfs_path, o) for o
+ in os.listdir(sysfs_path)
+ if os.path.isdir(os.path.join(sysfs_path, o))]
+
+ # Extract the last element of '/sys/module/abc' in the array
+ sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
+
+ # special case for vfio_pci (module is named vfio-pci,
+ # but its .ko is named vfio_pci)
+ sysfs_mods = map(lambda a:
+ a if a != 'vfio_pci' else 'vfio-pci', sysfs_mods)
+
+ for mod in mods:
+ if mod["Name"] in sysfs_mods:
+ mod["Found"] = True
+ except:
+ pass
+
+ # 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:
+ if b_flag in dpdk_drivers:
+ print("Error - no supported modules(DPDK driver) are loaded")
+ sys.exit(1)
+ else:
+ print("Warning - no supported modules(DPDK driver) are loaded")
+
+ # 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().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'''
+ 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")
+ # use dict to make copy of dev
+ devices[dev["Slot"]] = dict(dev)
+ # Clear previous device's data
+ dev = {}
+ else:
+ name, value = dev_line.decode().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
+ print("Unknown device: %s. "
+ "Please specify device in \"bus:slot.func\" format" % dev_name)
+ sys.exit(1)
+
+
+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("%s %s %s is not currently managed by any driver\n" %
+ (dev["Slot"], dev["Device_str"], dev["Interface"]))
+ return
+
+ # prevent us disconnecting ourselves
+ if dev["Ssh_if"] and not force:
+ print("Routing table indicates that interface %s is active. "
+ "Skipping unbind" % (dev_id))
+ return
+
+ # write to /sys to unbind
+ filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
+ try:
+ f = open(filename, "a")
+ except:
+ print("Error: unbind failed for %s - Cannot open %s"
+ % (dev_id, filename))
+ sys.exit(1)
+ 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("Routing table indicates that interface %s is active. "
+ "Not modifying" % (dev_id))
+ return
+
+ # unbind any existing drivers we don't want
+ if has_driver(dev_id):
+ if dev["Driver_str"] == driver:
+ print("%s already bound to driver %s, skipping\n"
+ % (dev_id, driver))
+ 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))
+ 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))
+ 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))
+ 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))
+ 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))
+ 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))
+ 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:
+ print("Error: unbind failed for %s - Cannot open %s"
+ % (dev_id, filename))
+ sys.exit(1)
+ try:
+ f.write("\00")
+ f.close()
+ except:
+ print("Error: unbind failed for %s - Cannot open %s"
+ % (dev_id, filename))
+ sys.exit(1)
+
+
+def unbind_all(dev_list, force=False):
+ """Unbind method, takes a list of device locations"""
+ dev_list = map(dev_id_from_dev_name, dev_list)
+ 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
+
+ dev_list = map(dev_id_from_dev_name, dev_list)
+
+ for d in dev_list:
+ bind_one(d, driver, force)
+
+ # For kenels < 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])
+
+ # print each category separately, so we can clearly see what's used by DPDK
+ display_devices("%s devices using DPDK-compatible driver" % device_name,
+ dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
+ display_devices("%s devices using kernel driver" % device_name, kernel_drv,
+ "if=%(Interface)s drv=%(Driver_str)s "
+ "unused=%(Module_str)s %(Active)s")
+ 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 == "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")
+
+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:
+ print("Error - Only one bind or unbind may be specified\n")
+ sys.exit(1)
+ 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")
+ print("Run '%s --usage' for further information" % sys.argv[0])
+ sys.exit(1)
+
+ if b_flag is not None and len(args) == 0:
+ print("Error: No devices specified.")
+ print("Run '%s --usage' for further information" % sys.argv[0])
+ 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(crypto_devices)
+ get_device_details(eventdev_devices)
+ get_device_details(mempool_devices)
+ show_status()
+
+
+def main():
+ '''program main function'''
+ parse_args()
+ check_modules()
+ clear_data()
+ get_device_details(network_devices)
+ get_device_details(crypto_devices)
+ get_device_details(eventdev_devices)
+ get_device_details(mempool_devices)
+ do_arg_actions()
+
+if __name__ == "__main__":
+ main()
diff --git a/src/seastar/dpdk/usertools/dpdk-pmdinfo.py b/src/seastar/dpdk/usertools/dpdk-pmdinfo.py
new file mode 100755
index 00000000..46c1be08
--- /dev/null
+++ b/src/seastar/dpdk/usertools/dpdk-pmdinfo.py
@@ -0,0 +1,610 @@
+#!/usr/bin/env python
+
+# -------------------------------------------------------------------------
+#
+# Utility to dump PMD_INFO_STRING support from an object file
+#
+# -------------------------------------------------------------------------
+from __future__ import print_function
+import json
+import os
+import platform
+import string
+import sys
+from elftools.common.exceptions import ELFError
+from elftools.common.py3compat import (byte2int, bytes2str, str2bytes)
+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
+ """
+ self.contents = open(filename).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
+ return self.elffile.get_section_by_name(str2bytes(spec))
+
+ 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
+
+ mystring = bytes2str(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():
+ if tag.entry.d_tag == 'DT_NEEDED':
+ if "librte_eal" in tag.needed:
+ return 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 = 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
+
+ mystring = bytes2str(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():
+ if tag.entry.d_tag == 'DT_RUNPATH':
+ return 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():
+ if tag.entry.d_tag == 'DT_NEEDED':
+ rc = tag.needed.find(b"librte_pmd")
+ if (rc != -1):
+ library = search_file(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 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()
+
+
+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 = 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 = 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 (autoload_path is None or autoload_path is ""):
+ 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':
+ 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 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/seastar/dpdk/usertools/dpdk-setup.sh b/src/seastar/dpdk/usertools/dpdk-setup.sh
new file mode 100755
index 00000000..c4fec5a6
--- /dev/null
+++ b/src/seastar/dpdk/usertools/dpdk-setup.sh
@@ -0,0 +1,634 @@
+#! /bin/bash
+
+# BSD LICENSE
+#
+# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# 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 linuxapp 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/Crypto device settings"
+ FUNC[6]="show_devices"
+
+ TEXT[7]="Bind Ethernet/Crypto device to IGB UIO module"
+ FUNC[7]="bind_devices_to_igb_uio"
+
+ TEXT[8]="Bind Ethernet/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 linuxapp 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