From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wg0-f51.google.com (mail-wg0-f51.google.com [74.125.82.51]) by dpdk.org (Postfix) with ESMTP id 094D54A63 for ; Tue, 4 Feb 2014 16:53:29 +0100 (CET) Received: by mail-wg0-f51.google.com with SMTP id z12so13165513wgg.30 for ; Tue, 04 Feb 2014 07:54:50 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=52mgJyuu0NUxilp3DmpNYD2kZXtCEhXxl9xhVM2fBBs=; b=bY1AYzqyC80i4vxSlz3vgeMiLs2oZ49jSmFRinIF963pzPqenZWhKb0Dd6zWDevpyd jVvBdoO7Of+2dBUVtfBqn1Jjca2h1kQM5I6jmapmVt2HDoTT+CjUj9qy7FwCmFWN6DUP xS4ghc/DsSm4ntQMj+N3v4QyiOyBgoh2QP1oES6j5ADBTzNcT6UnU+yofKbs49xhXfb0 HDNKjoUKSPrMjOA6WfUZuYT1WeKVmHXtqkkuE5kvU/c/95wxHDMU2RNlS5gKUsin2p5Z qVTGuXMQjHLwXmUZObl0Evso+NMNhn6zfe7Is+tT0/Jaj6eVcgx/iDC4jVXLu9dRHjHc PQeg== X-Gm-Message-State: ALoCoQnPJu/gkgqgijYkLaLGavtRNKfL2CQ59k+xhVzx3AJ84U83N1mwnAmpVi1aq/5Lmfez6lF7 X-Received: by 10.180.165.174 with SMTP id yz14mr13151327wib.34.1391529290619; Tue, 04 Feb 2014 07:54:50 -0800 (PST) Received: from angus.dev.6wind.com (6wind.net2.nerim.net. [213.41.180.237]) by mx.google.com with ESMTPSA id q2sm53639203wjq.0.2014.02.04.07.54.48 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 04 Feb 2014 07:54:49 -0800 (PST) From: Thomas Monjalon To: dev@dpdk.org Date: Tue, 4 Feb 2014 16:54:16 +0100 Message-Id: <1391529271-24606-2-git-send-email-thomas.monjalon@6wind.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1391529271-24606-1-git-send-email-thomas.monjalon@6wind.com> References: <1391529271-24606-1-git-send-email-thomas.monjalon@6wind.com> Subject: [dpdk-dev] [PATCH 01/16] tools: rename pci_unbind script X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 04 Feb 2014 15:53:30 -0000 In order to make this tool available among other system commands, the name must be more specific. Signed-off-by: Thomas Monjalon --- tools/igb_uio_bind.py | 485 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/pci_unbind.py | 485 ------------------------------------------------- tools/setup.sh | 16 +- 3 files changed, 493 insertions(+), 493 deletions(-) create mode 100755 tools/igb_uio_bind.py delete mode 100755 tools/pci_unbind.py diff --git a/tools/igb_uio_bind.py b/tools/igb_uio_bind.py new file mode 100755 index 0000000..528999a --- /dev/null +++ b/tools/igb_uio_bind.py @@ -0,0 +1,485 @@ +#! /usr/bin/python +# +# BSD LICENSE +# +# Copyright(c) 2010-2013 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, os, getopt, subprocess +from os.path import exists, abspath, dirname, basename + + +# The PCI device class for ETHERNET devices +ETHERNET_CLASS = "0200" + +# global dict ethernet devices present. Dictionary indexed by PCI address. +# Each device within this is itself a dictionary of device properties +devices = {} +# list of vendor:device pairs (again stored as dictionary) supported by igb_uio +module_dev_ids = [] + +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 + + --status: + Print the current status of all known network interfaces. + 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. + + -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, 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 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: + depmod_out = check_output(["modinfo", "-n", mod], \ + stderr=subprocess.STDOUT).lower() + if "error" not in depmod_out: + path = depmod_out.strip() + if 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 the needed modules (igb_uio) is loaded, and then + determine from the .ko file, what its supported device ids are''' + global module_dev_ids + + fd = file("/proc/modules") + loaded_mods = fd.readlines() + fd.close() + mod = "igb_uio" + + # first check if module is loaded + found = False + for line in loaded_mods: + if line.startswith(mod): + found = True + break + if not found: + print "Error - module %s not loaded" %mod + sys.exit(1) + + # now find the .ko and get list of supported vendor/dev-ids + modpath = find_module(mod) + if modpath is None: + print "Cannot find module file %s" % (mod + ".ko") + sys.exit(1) + depmod_output = check_output(["depmod", "-n", modpath]).splitlines() + for line in depmod_output: + if not line.startswith("alias"): + continue + if not line.endswith(mod): + continue + lineparts = line.split() + if not(lineparts[1].startswith("pci:")): + continue; + else: + lineparts[1] = lineparts[1][4:] + vendor = lineparts[1][:9] + device = lineparts[1][9:18] + if vendor.startswith("v") and device.startswith("d"): + module_dev_ids.append({"Vendor": int(vendor[1:],16), + "Device": int(device[1:],16)}) + +def is_supported_device(dev_id): + '''return true if device is supported by igb_uio, false otherwise''' + for dev in module_dev_ids: + if (dev["Vendor"] == devices[dev_id]["Vendor"] and + dev["Device"] == devices[dev_id]["Device"]): + return True + return False + +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_nic_details(): + '''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 + + # clear any old data + devices = {} + # first loop through and read details for all devices + # request machine readable format, with numeric IDs + dev = {}; + dev_lines = check_output(["lspci", "-Dvmmn"]).splitlines() + for dev_line in dev_lines: + if (len(dev_line) == 0): + if dev["Class"] == ETHERNET_CLASS: + #convert device and vendor ids to numbers, then add to global + dev["Vendor"] = int(dev["Vendor"],16) + dev["Device"] = int(dev["Device"],16) + devices[dev["Slot"]] = dict(dev) # use dict to make copy of dev + else: + name, value = dev_line.split("\t", 1) + dev[name.rstrip(":")] = value + + # 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.splitlines())) + rt_info = route.split() + for i in xrange(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(): + extra_info = check_output(["lspci", "-vmmks", d]).splitlines() + # parse lspci details + for line in extra_info: + if len(line) == 0: + continue + name, value = line.split("\t", 1) + name = name.strip(":") + "_str" + devices[d][name] = value + # check for a unix interface name + sys_path = "/sys/bus/pci/devices/%s/net/" % d + if exists(sys_path): + devices[d]["Interface"] = ",".join(os.listdir(sys_path)) + else: + devices[d]["Interface"] = "" + # check if a port is used for ssh connection + devices[d]["Ssh_if"] = False + devices[d]["Active"] = "" + 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 is_supported_device(d): + if "Module_str" in devices[d]: + if "igb_uio" not in devices[d]["Module_str"]: + devices[d]["Module_str"] = devices[d]["Module_str"] + ",igb_uio" + else: + devices[d]["Module_str"] = "igb_uio" + if "Module_str" not in devices[d]: + devices[d]["Module_str"] = "" + # 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 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''' + dev = None + # 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 + + # 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: + 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 + + +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): + """Unbind method, takes a list of device locations""" + dev_list = map(dev_id_from_dev_name, dev_list) + for d in dev_list: + bind_one(d, driver, 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("") + else: + for dev in dev_list: + if extra_params is not None: + strings.append("%s '%s' %s" % (dev["Slot"], \ + dev["Device_str"], 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_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''' + kernel_drv = [] + uio_drv = [] + no_drv = [] + # split our list of devices into the three categories above + for d in devices.keys(): + if not has_driver(d): + no_drv.append(devices[d]) + continue + if devices[d]["Driver_str"] == "igb_uio": + uio_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("Network devices using IGB_UIO driver", uio_drv, \ + "drv=%(Driver_str)s unused=%(Module_str)s") + display_devices("Network devices using kernel driver", kernel_drv, + "if=%(Interface)s drv=%(Driver_str)s unused=%(Module_str)s %(Active)s") + display_devices("Other network devices", no_drv,\ + "unused=%(Module_str)s") + +def parse_args(): + '''Parses the command-line arguments given by the user and takes the + appropriate action for each''' + b_flag = None + status_flag = False + force_flag = False + if len(sys.argv) <= 1: + usage() + sys.exit(0) + + try: + opts, args = getopt.getopt(sys.argv[1:], "b:u", + ["help", "usage", "status", "force", + "bind=", "unbind"]) + except getopt.GetoptError, 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": + status_flag = True + 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 + + 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: + get_nic_details() # refresh if we have changed anything + show_status() + +def main(): + '''program main function''' + check_modules() + get_nic_details() + parse_args() + +if __name__ == "__main__": + main() diff --git a/tools/pci_unbind.py b/tools/pci_unbind.py deleted file mode 100755 index 528999a..0000000 --- a/tools/pci_unbind.py +++ /dev/null @@ -1,485 +0,0 @@ -#! /usr/bin/python -# -# BSD LICENSE -# -# Copyright(c) 2010-2013 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, os, getopt, subprocess -from os.path import exists, abspath, dirname, basename - - -# The PCI device class for ETHERNET devices -ETHERNET_CLASS = "0200" - -# global dict ethernet devices present. Dictionary indexed by PCI address. -# Each device within this is itself a dictionary of device properties -devices = {} -# list of vendor:device pairs (again stored as dictionary) supported by igb_uio -module_dev_ids = [] - -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 - - --status: - Print the current status of all known network interfaces. - 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. - - -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, 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 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: - depmod_out = check_output(["modinfo", "-n", mod], \ - stderr=subprocess.STDOUT).lower() - if "error" not in depmod_out: - path = depmod_out.strip() - if 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 the needed modules (igb_uio) is loaded, and then - determine from the .ko file, what its supported device ids are''' - global module_dev_ids - - fd = file("/proc/modules") - loaded_mods = fd.readlines() - fd.close() - mod = "igb_uio" - - # first check if module is loaded - found = False - for line in loaded_mods: - if line.startswith(mod): - found = True - break - if not found: - print "Error - module %s not loaded" %mod - sys.exit(1) - - # now find the .ko and get list of supported vendor/dev-ids - modpath = find_module(mod) - if modpath is None: - print "Cannot find module file %s" % (mod + ".ko") - sys.exit(1) - depmod_output = check_output(["depmod", "-n", modpath]).splitlines() - for line in depmod_output: - if not line.startswith("alias"): - continue - if not line.endswith(mod): - continue - lineparts = line.split() - if not(lineparts[1].startswith("pci:")): - continue; - else: - lineparts[1] = lineparts[1][4:] - vendor = lineparts[1][:9] - device = lineparts[1][9:18] - if vendor.startswith("v") and device.startswith("d"): - module_dev_ids.append({"Vendor": int(vendor[1:],16), - "Device": int(device[1:],16)}) - -def is_supported_device(dev_id): - '''return true if device is supported by igb_uio, false otherwise''' - for dev in module_dev_ids: - if (dev["Vendor"] == devices[dev_id]["Vendor"] and - dev["Device"] == devices[dev_id]["Device"]): - return True - return False - -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_nic_details(): - '''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 - - # clear any old data - devices = {} - # first loop through and read details for all devices - # request machine readable format, with numeric IDs - dev = {}; - dev_lines = check_output(["lspci", "-Dvmmn"]).splitlines() - for dev_line in dev_lines: - if (len(dev_line) == 0): - if dev["Class"] == ETHERNET_CLASS: - #convert device and vendor ids to numbers, then add to global - dev["Vendor"] = int(dev["Vendor"],16) - dev["Device"] = int(dev["Device"],16) - devices[dev["Slot"]] = dict(dev) # use dict to make copy of dev - else: - name, value = dev_line.split("\t", 1) - dev[name.rstrip(":")] = value - - # 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.splitlines())) - rt_info = route.split() - for i in xrange(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(): - extra_info = check_output(["lspci", "-vmmks", d]).splitlines() - # parse lspci details - for line in extra_info: - if len(line) == 0: - continue - name, value = line.split("\t", 1) - name = name.strip(":") + "_str" - devices[d][name] = value - # check for a unix interface name - sys_path = "/sys/bus/pci/devices/%s/net/" % d - if exists(sys_path): - devices[d]["Interface"] = ",".join(os.listdir(sys_path)) - else: - devices[d]["Interface"] = "" - # check if a port is used for ssh connection - devices[d]["Ssh_if"] = False - devices[d]["Active"] = "" - 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 is_supported_device(d): - if "Module_str" in devices[d]: - if "igb_uio" not in devices[d]["Module_str"]: - devices[d]["Module_str"] = devices[d]["Module_str"] + ",igb_uio" - else: - devices[d]["Module_str"] = "igb_uio" - if "Module_str" not in devices[d]: - devices[d]["Module_str"] = "" - # 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 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''' - dev = None - # 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 - - # 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: - 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 - - -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): - """Unbind method, takes a list of device locations""" - dev_list = map(dev_id_from_dev_name, dev_list) - for d in dev_list: - bind_one(d, driver, 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("") - else: - for dev in dev_list: - if extra_params is not None: - strings.append("%s '%s' %s" % (dev["Slot"], \ - dev["Device_str"], 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_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''' - kernel_drv = [] - uio_drv = [] - no_drv = [] - # split our list of devices into the three categories above - for d in devices.keys(): - if not has_driver(d): - no_drv.append(devices[d]) - continue - if devices[d]["Driver_str"] == "igb_uio": - uio_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("Network devices using IGB_UIO driver", uio_drv, \ - "drv=%(Driver_str)s unused=%(Module_str)s") - display_devices("Network devices using kernel driver", kernel_drv, - "if=%(Interface)s drv=%(Driver_str)s unused=%(Module_str)s %(Active)s") - display_devices("Other network devices", no_drv,\ - "unused=%(Module_str)s") - -def parse_args(): - '''Parses the command-line arguments given by the user and takes the - appropriate action for each''' - b_flag = None - status_flag = False - force_flag = False - if len(sys.argv) <= 1: - usage() - sys.exit(0) - - try: - opts, args = getopt.getopt(sys.argv[1:], "b:u", - ["help", "usage", "status", "force", - "bind=", "unbind"]) - except getopt.GetoptError, 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": - status_flag = True - 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 - - 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: - get_nic_details() # refresh if we have changed anything - show_status() - -def main(): - '''program main function''' - check_modules() - get_nic_details() - parse_args() - -if __name__ == "__main__": - main() diff --git a/tools/setup.sh b/tools/setup.sh index cddcbdc..c8d42bf 100755 --- a/tools/setup.sh +++ b/tools/setup.sh @@ -324,13 +324,13 @@ grep_meminfo() } # -# Calls pci_unbind.py --status to show the NIC and what they +# Calls igb_uio_bind.py --status to show the NIC and what they # are all bound to, in terms of drivers. # show_nics() { if /sbin/lsmod | grep -q igb_uio ; then - ${RTE_SDK}/tools/pci_unbind.py --status + ${RTE_SDK}/tools/igb_uio_bind.py --status else echo "# Please load the 'igb_uio' kernel module before querying or " echo "# adjusting NIC device bindings" @@ -338,16 +338,16 @@ show_nics() } # -# Uses pci_unbind.py to move devices to work with igb_uio +# Uses igb_uio_bind.py to move devices to work with igb_uio # bind_nics() { if /sbin/lsmod | grep -q igb_uio ; then - ${RTE_SDK}/tools/pci_unbind.py --status + ${RTE_SDK}/tools/igb_uio_bind.py --status echo "" echo -n "Enter PCI address of device to bind to IGB UIO driver: " read PCI_PATH - sudo ${RTE_SDK}/tools/pci_unbind.py -b igb_uio $PCI_PATH && echo "OK" + sudo ${RTE_SDK}/tools/igb_uio_bind.py -b igb_uio $PCI_PATH && echo "OK" else echo "# Please load the 'igb_uio' kernel module before querying or " echo "# adjusting NIC device bindings" @@ -355,18 +355,18 @@ bind_nics() } # -# Uses pci_unbind.py to move devices to work with kernel drivers again +# Uses igb_uio_bind.py to move devices to work with kernel drivers again # unbind_nics() { - ${RTE_SDK}/tools/pci_unbind.py --status + ${RTE_SDK}/tools/igb_uio_bind.py --status echo "" echo -n "Enter PCI address of device to bind to IGB UIO driver: " read PCI_PATH echo "" echo -n "Enter name of kernel driver to bind the device to: " read DRV - sudo ${RTE_SDK}/tools/pci_unbind.py -b $DRV $PCI_PATH && echo "OK" + sudo ${RTE_SDK}/tools/igb_uio_bind.py -b $DRV $PCI_PATH && echo "OK" } # -- 1.7.10.4