X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;ds=inline;f=tools%2Fdpdk_nic_bind.py;h=28eace33d7e01d0fe4e3946228b8acbb28254f95;hb=4a7e4626975a73cd84909a059c0396746de057ef;hp=e87a05ea8ba082311346578562e2121f7db3af99;hpb=f0e14c5f66c9c137b3e9ffea30acdcfdaa98b2af;p=dpdk.git diff --git a/tools/dpdk_nic_bind.py b/tools/dpdk_nic_bind.py index e87a05ea8b..28eace33d7 100755 --- a/tools/dpdk_nic_bind.py +++ b/tools/dpdk_nic_bind.py @@ -32,21 +32,32 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -import sys, os, getopt, subprocess +import sys +import os +import getopt +import 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 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 """ + print(""" Usage: ------ @@ -60,7 +71,7 @@ Options: --help, --usage: Display usage information and quit - --status: + -s, --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 @@ -70,8 +81,9 @@ Options: * 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. + 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 @@ -102,7 +114,8 @@ To unbind 0000:01:00.0 from using any driver 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 + """ % 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. @@ -111,6 +124,7 @@ def check_output(args, stderr=None): 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 @@ -118,20 +132,20 @@ def find_module(mod): 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'],\ + 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], \ + 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 + except: # if modinfo can't find module, it fails, so continue pass # check for a copy based off current path @@ -139,33 +153,60 @@ def find_module(mod): 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 + 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 - fd = file("/proc/modules") - loaded_mods = fd.readlines() - fd.close() - mod = "igb_uio" + # list of supported modules + mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers] # 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) + 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): '''This function gets additional details for a PCI device''' device = {} @@ -176,7 +217,7 @@ def get_pci_device_details(dev_id): for line in extra_info: if len(line) == 0: continue - name, value = line.split("\t", 1) + name, value = line.decode().split("\t", 1) name = name.strip(":") + "_str" device[name] = value # check for a unix interface name @@ -191,27 +232,30 @@ def get_pci_device_details(dev_id): return device + 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 + global dpdk_drivers # clear any old data devices = {} # first loop through and read details for all devices # request machine readable format, with numeric IDs - dev = {}; + 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 + # convert device and vendor ids to numbers, then add to global + dev["Vendor"] = int(dev["Vendor"], 16) + dev["Device"] = int(dev["Device"], 16) + # use dict to make copy of dev + devices[dev["Slot"]] = dict(dev) else: - name, value = dev_line.split("\t", 1) + name, value = dev_line.decode().split("\t", 1) dev[name.rstrip(":")] = value # check what is the interface if any for an ssh connection if @@ -220,30 +264,32 @@ def get_nic_details(): 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())) + route.decode().splitlines())) rt_info = route.split() - for i in xrange(len(rt_info) - 1): + 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(): # get additional info and add it to existing data - devices[d] = dict(devices[d].items() + - get_pci_device_details(d).items()) + devices[d] = devices[d].copy() + devices[d].update(get_pci_device_details(d).items()) for _if in ssh_if: if _if in devices[d]["Interface"].split(","): devices[d]["Ssh_if"] = True devices[d]["Active"] = "*Active*" - break; + break # add igb_uio to list of supporting modules if needed 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" + 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"] = "igb_uio" + devices[d]["Module_str"] = ",".join(dpdk_drivers) # make sure the driver and module strings do not have any duplicates if has_driver(d): @@ -252,11 +298,12 @@ def get_nic_details(): 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 @@ -269,22 +316,23 @@ def dev_id_from_dev_name(dev_name): 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 + 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"]) + 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) + print("Routing table indicates that interface %s is active. " + "Skipping unbind" % (dev_id)) return # write to /sys to unbind @@ -292,47 +340,51 @@ def unbind_one(dev_id, force): try: f = open(filename, "a") except: - print "Error: unbind failed for %s - Cannot open %s" % (dev_id, filename) - sys/exit(1) + 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 + 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) + 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) + 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 + dev["Driver_str"] = "" # clear driver string # if we are binding to one of DPDK drivers, add PCI id's to that driver - if driver == "igb_uio": + if driver in dpdk_drivers: 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) + print("Error: bind failed for %s - Cannot open %s" + % (dev_id, filename)) return try: f.write("%04x %04x" % (dev["Vendor"], dev["Device"])) f.close() except: - print "Error: bind failed for %s - Cannot write new PCI ID to " \ - "driver %s" % (dev_id, driver) + 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 @@ -340,8 +392,9 @@ def bind_one(dev_id, driver, force): 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 + 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: @@ -354,8 +407,9 @@ def bind_one(dev_id, driver, force): tmp = get_pci_device_details(dev_id) 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 + 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 @@ -366,102 +420,139 @@ def unbind_all(dev_list, force=False): 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""" + """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) -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) + # when binding devices to a generic driver (i.e. one that doesn't have a + # PCI ID table), 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. + 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).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("") 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)) + 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 + 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''' + '''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''' + global dpdk_drivers kernel_drv = [] - uio_drv = [] + dpdk_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]) + 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("Network devices using IGB_UIO driver", uio_drv, \ + display_devices("Network devices using DPDK-compatible driver", dpdk_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") + "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 + global b_flag + global status_flag + global force_flag + global args 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] + opts, args = getopt.getopt(sys.argv[1:], "b:us", + ["help", "usage", "status", "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": + if opt == "--status" or opt == "-s": 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" + 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] + 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] + 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": @@ -470,14 +561,16 @@ def parse_args(): 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 + get_nic_details() # refresh if we have changed anything show_status() + def main(): '''program main function''' + parse_args() check_modules() get_nic_details() - parse_args() + do_arg_actions() if __name__ == "__main__": main()