usertools: optimize lspci invocation
[dpdk.git] / usertools / dpdk-devbind.py
index 1b9c651..9beadde 100755 (executable)
@@ -208,19 +208,20 @@ def has_driver(dev_id):
     return "Driver_str" in devices[dev_id]
 
 
-def get_pci_device_details(dev_id):
+def get_pci_device_details(dev_id, probe_lspci):
     '''This function gets additional details for a PCI device'''
     device = {}
 
-    extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
+    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
+        # 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):
@@ -234,106 +235,72 @@ 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
+def clear_data():
+    '''This function clears 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"][0:2] == NETWORK_BASE_CLASS:
-                # 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.decode().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.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():
-        # get additional info and add it to existing data
-        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
-
-        # 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 get_crypto_details():
+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
 
-    # clear any old data
-    # devices = {}
     # first loop through and read details for all devices
-    # request machine readable format, with numeric IDs
+    # request machine readable format, with numeric IDs and String
     dev = {}
-    dev_lines = check_output(["lspci", "-Dvmmn"]).splitlines()
+    dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
     for dev_line in dev_lines:
         if len(dev_line) == 0:
-            if dev["Class"][0:2] == CRYPTO_BASE_CLASS:
+            if dev["Class"][0:2] == devices_type:
                 # convert device and vendor ids to numbers, then add to global
                 dev["Vendor"] = int(dev["Vendor"], 16)
                 dev["Device"] = int(dev["Device"], 16)
+                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)
-            dev[name.rstrip(":")] = value
+            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_BASE_CLASS:
+        # 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 devices[d]["Class"][0:2] != CRYPTO_BASE_CLASS:
+        if devices[d]["Class"][0:2] != devices_type:
             continue
 
         # get additional info and add it to existing data
         devices[d] = devices[d].copy()
-        devices[d].update(get_pci_device_details(d).items())
+        # No need to probe lspci
+        devices[d].update(get_pci_device_details(d, False).items())
+
+        if devices_type == NETWORK_BASE_CLASS:
+            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]:
@@ -457,7 +424,7 @@ def bind_one(dev_id, driver, force):
         # 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)
+        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"
@@ -495,7 +462,7 @@ def bind_all(dev_list, driver, force=False):
 
         # update information about this device
         devices[d] = dict(devices[d].items() +
-                          get_pci_device_details(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]:
@@ -515,8 +482,9 @@ def display_devices(title, dev_list, extra_params=None):
     else:
         for dev in dev_list:
             if extra_params is not None:
-                strings.append("%s '%s' %s" % (dev["Slot"],
+                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"]))
@@ -524,11 +492,7 @@ def display_devices(title, dev_list, extra_params=None):
     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'''
+def show_device_status(devices_type, device_name):
     global dpdk_drivers
     kernel_drv = []
     dpdk_drv = []
@@ -536,7 +500,7 @@ def show_status():
 
     # split our list of network devices into the three categories above
     for d in devices.keys():
-        if NETWORK_BASE_CLASS in devices[d]["Class"]:
+        if devices_type in devices[d]["Class"]:
             if not has_driver(d):
                 no_drv.append(devices[d])
                 continue
@@ -546,35 +510,21 @@ def show_status():
                 kernel_drv.append(devices[d])
 
     # print each category separately, so we can clearly see what's used by DPDK
-    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,
+    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 network devices", no_drv, "unused=%(Module_str)s")
-
-    # split our list of crypto devices into the three categories above
-    kernel_drv = []
-    dpdk_drv = []
-    no_drv = []
-
-    for d in devices.keys():
-        if CRYPTO_BASE_CLASS in devices[d]["Class"]:
-            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])
-
-    display_devices("Crypto devices using DPDK-compatible driver", dpdk_drv,
-                    "drv=%(Driver_str)s unused=%(Module_str)s")
-    display_devices("Crypto devices using kernel driver", kernel_drv,
-                    "drv=%(Driver_str)s "
+    display_devices("Other %s devices" % device_name, no_drv,
                     "unused=%(Module_str)s")
-    display_devices("Other crypto devices", 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'''
+
+    show_device_status(network_devices, "Network")
+    show_device_status(crypto_devices, "Crypto")
 
 def parse_args():
     '''Parses the command-line arguments given by the user and takes the
@@ -638,8 +588,9 @@ def do_arg_actions():
         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_crypto_details()  # refresh if we have changed anything
+            clear_data()
+            get_device_details(NETWORK_BASE_CLASS)  # refresh if we have changed anything
+            get_device_details(CRYPTO_BASE_CLASS)  # refresh if we have changed anything
         show_status()
 
 
@@ -647,8 +598,9 @@ def main():
     '''program main function'''
     parse_args()
     check_modules()
-    get_nic_details()
-    get_crypto_details()
+    clear_data()
+    get_device_details(NETWORK_BASE_CLASS)
+    get_device_details(CRYPTO_BASE_CLASS)
     do_arg_actions()
 
 if __name__ == "__main__":