[dpdk-dev] [PATCH v2 15/16] Added support for VFIO drivers in dpdk_nic_bind.py

Xu, HuilongX huilongx.xu at intel.com
Tue May 27 05:43:10 CEST 2014


Since igb_uio no longer has a PCI ID list, the script will no
longer distinguish between supported and unsupported NICs.

There's a weird behaviour of sysfs when a new device ID is added to
new_id. Subsequent writing to "bind" will result in IOError on
closing the file. This error is harmless but it triggers the
exception anyway, so in order to work around that, we check if the
device was actually bound to the driver before raising an error.

Signed-off-by: Anatoly Burakov <anatoly.burakov at intel.com>

Test-by: HuilongX Xu <huilongx.xu at intel.com>

Compile pass

     >>Compile OS: FC20 x86_64

     >>Kernel version: 3.13.6-200

     >>GCC version: 4.8.2

     >>Server: Crownpass

---
tools/dpdk_nic_bind.py |  163 ++++++++++++++++++++++++++----------------------
1 files changed, 89 insertions(+), 74 deletions(-)

diff --git a/tools/dpdk_nic_bind.py b/tools/dpdk_nic_bind.py
index 824aa2b..06fb28a 100755
--- a/tools/dpdk_nic_bind.py
+++ b/tools/dpdk_nic_bind.py
@@ -42,8 +42,8 @@ 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 = []
+# list of supported DPDK drivers
+dpdk_drivers = [ "igb_uio", "vfio-pci" ]
 def usage():
     '''Print usage information for the program'''
@@ -147,64 +147,70 @@ def find_module(mod):
                 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
+    '''Checks that the needed modules (igb_uio or vfio_pci) are 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)
-
-    # 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")
+        for mod in mods:
+            if line.startswith(mod["Name"]):
+                mod["Found"] = True
+            # special case for vfio_pci (module is named vfio-pci,
+            # but its .ko is named vfio_pci)
+            elif line.replace("_", "-").startswith(mod["Name"]):
+                mod["Found"] = True
+
+    # check if we have at least one loaded module
+    if True not in [mod["Found"] for mod in mods]:
+        print "Error - no supported modules are loaded"
         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
+
+    # 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 = {}
+
+    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.split("\t", 1)
+        name = name.strip(":") + "_str"
+        device[name] = value
+    # check for a unix interface name
+    sys_path = "/sys/bus/pci/devices/%s/net/" % dev_id
+    if exists(sys_path):
+        device["Interface"] = ",".join(os.listdir(sys_path))
+    else:
+        device["Interface"] = ""
+    # check if a port is used for ssh connection
+    device["Ssh_if"] = False
+    device["Active"] = ""
+
+    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 = {}
@@ -237,38 +243,23 @@ def get_nic_details():
     # 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"] = ""
+        # get additional info and add it to existing data
+        devices[d] = dict(devices[d].items() + 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;
-        # 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"] = "<none>"
+        # add DPDK drivers 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(",")
@@ -298,7 +289,7 @@ def dev_id_from_dev_name(dev_name):
     sys.exit(1)
 def unbind_one(dev_id, force):
-    '''Unbind the device identified by "dev_id" from its current driver'''
+    '''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" % \
@@ -329,8 +320,8 @@ def bind_one(dev_id, driver, force):

     # 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
@@ -343,6 +334,22 @@ def bind_one(dev_id, driver, force):
             unbind_one(dev_id, force)
             dev["Driver_str"] = "" # clear driver string
+    # if we are binding to one of DPDK drivers, add PCI id's to that driver
+    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)
+            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)
+            return
+
     # do the bind by writing to /sys
     filename = "/sys/bus/pci/drivers/%s/bind" % driver
     try:
@@ -356,6 +363,12 @@ def bind_one(dev_id, driver, force):
         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)
+        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)
@@ -399,21 +412,23 @@ 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'''
+    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 IGB_UIO 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")
--
1.7.0.7


More information about the dev mailing list