xref: /f-stack/dpdk/usertools/dpdk-devbind.py (revision 2d9fd380)
1*2d9fd380Sjfb8856606#!/usr/bin/env python3
2d30ea906Sjfb8856606# SPDX-License-Identifier: BSD-3-Clause
3d30ea906Sjfb8856606# Copyright(c) 2010-2014 Intel Corporation
42bfe3f2eSlogwang#
52bfe3f2eSlogwang
62bfe3f2eSlogwangimport sys
72bfe3f2eSlogwangimport os
82bfe3f2eSlogwangimport subprocess
9*2d9fd380Sjfb8856606import argparse
10*2d9fd380Sjfb8856606
11*2d9fd380Sjfb8856606from glob import glob
12*2d9fd380Sjfb8856606from os.path import exists, basename
13*2d9fd380Sjfb8856606from os.path import join as path_join
142bfe3f2eSlogwang
152bfe3f2eSlogwang# The PCI base class for all devices
162bfe3f2eSlogwangnetwork_class = {'Class': '02', 'Vendor': None, 'Device': None,
172bfe3f2eSlogwang                 'SVendor': None, 'SDevice': None}
184418919fSjohnjiangacceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
194418919fSjohnjiang                      'SVendor': None, 'SDevice': None}
204418919fSjohnjiangifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
214418919fSjohnjiang               'SVendor': None, 'SDevice': None}
222bfe3f2eSlogwangencryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
232bfe3f2eSlogwang                    'SVendor': None, 'SDevice': None}
242bfe3f2eSlogwangintel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
252bfe3f2eSlogwang                         'SVendor': None, 'SDevice': None}
262bfe3f2eSlogwangcavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
272bfe3f2eSlogwang              'SVendor': None, 'SDevice': None}
282bfe3f2eSlogwangcavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
292bfe3f2eSlogwang              'SVendor': None, 'SDevice': None}
302bfe3f2eSlogwangcavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
312bfe3f2eSlogwang              'SVendor': None, 'SDevice': None}
32d30ea906Sjfb8856606cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
33d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
34d30ea906Sjfb8856606cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
35d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
36d30ea906Sjfb8856606avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
37d30ea906Sjfb8856606            'SVendor': None, 'SDevice': None}
382bfe3f2eSlogwang
394418919fSjohnjiangocteontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
404418919fSjohnjiang                 'SVendor': None, 'SDevice': None}
414418919fSjohnjiangocteontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
424418919fSjohnjiang                 'SVendor': None, 'SDevice': None}
434418919fSjohnjiangocteontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
444418919fSjohnjiang                 'SVendor': None, 'SDevice': None}
45*2d9fd380Sjfb8856606octeontx2_ree = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f4',
46*2d9fd380Sjfb8856606                 'SVendor': None, 'SDevice': None}
474418919fSjohnjiang
48*2d9fd380Sjfb8856606intel_ioat_bdw = {'Class': '08', 'Vendor': '8086',
49*2d9fd380Sjfb8856606                  'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
504418919fSjohnjiang                  'SVendor': None, 'SDevice': None}
514418919fSjohnjiangintel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
524418919fSjohnjiang                  'SVendor': None, 'SDevice': None}
53*2d9fd380Sjfb8856606intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00',
54*2d9fd380Sjfb8856606                  'SVendor': None, 'SDevice': None}
55*2d9fd380Sjfb8856606intel_idxd_spr = {'Class': '08', 'Vendor': '8086', 'Device': '0b25',
56*2d9fd380Sjfb8856606                  'SVendor': None, 'SDevice': None}
574418919fSjohnjiangintel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
584418919fSjohnjiang                 'SVendor': None, 'SDevice': None}
59*2d9fd380Sjfb8856606intel_ntb_icx = {'Class': '06', 'Vendor': '8086', 'Device': '347e',
60*2d9fd380Sjfb8856606                 'SVendor': None, 'SDevice': None}
614418919fSjohnjiang
624418919fSjohnjiangnetwork_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
634418919fSjohnjiangbaseband_devices = [acceleration_class]
642bfe3f2eSlogwangcrypto_devices = [encryption_class, intel_processor_class]
654418919fSjohnjiangeventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
664418919fSjohnjiangmempool_devices = [cavium_fpa, octeontx2_npa]
67d30ea906Sjfb8856606compress_devices = [cavium_zip]
68*2d9fd380Sjfb8856606regex_devices = [octeontx2_ree]
69*2d9fd380Sjfb8856606misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ioat_icx, intel_idxd_spr,
70*2d9fd380Sjfb8856606                intel_ntb_skx, intel_ntb_icx,
71*2d9fd380Sjfb8856606                octeontx2_dma]
722bfe3f2eSlogwang
732bfe3f2eSlogwang# global dict ethernet devices present. Dictionary indexed by PCI address.
742bfe3f2eSlogwang# Each device within this is itself a dictionary of device properties
752bfe3f2eSlogwangdevices = {}
762bfe3f2eSlogwang# list of supported DPDK drivers
772bfe3f2eSlogwangdpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
784418919fSjohnjiang# list of currently loaded kernel modules
794418919fSjohnjiangloaded_modules = None
802bfe3f2eSlogwang
812bfe3f2eSlogwang# command-line arg flags
822bfe3f2eSlogwangb_flag = None
832bfe3f2eSlogwangstatus_flag = False
842bfe3f2eSlogwangforce_flag = False
852bfe3f2eSlogwangargs = []
862bfe3f2eSlogwang
874418919fSjohnjiang# check if a specific kernel module is loaded
884418919fSjohnjiangdef module_is_loaded(module):
894418919fSjohnjiang    global loaded_modules
904418919fSjohnjiang
914418919fSjohnjiang    if module == 'vfio_pci':
924418919fSjohnjiang        module = 'vfio-pci'
934418919fSjohnjiang
944418919fSjohnjiang    if loaded_modules:
954418919fSjohnjiang        return module in loaded_modules
964418919fSjohnjiang
974418919fSjohnjiang    # Get list of sysfs modules (both built-in and dynamically loaded)
984418919fSjohnjiang    sysfs_path = '/sys/module/'
994418919fSjohnjiang
1004418919fSjohnjiang    # Get the list of directories in sysfs_path
1014418919fSjohnjiang    sysfs_mods = [m for m in os.listdir(sysfs_path)
1024418919fSjohnjiang                  if os.path.isdir(os.path.join(sysfs_path, m))]
1034418919fSjohnjiang
1044418919fSjohnjiang    # special case for vfio_pci (module is named vfio-pci,
1054418919fSjohnjiang    # but its .ko is named vfio_pci)
1064418919fSjohnjiang    sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
1074418919fSjohnjiang
1084418919fSjohnjiang    loaded_modules = sysfs_mods
1094418919fSjohnjiang
1104418919fSjohnjiang    return module in sysfs_mods
1114418919fSjohnjiang
1122bfe3f2eSlogwang
1132bfe3f2eSlogwangdef check_modules():
1142bfe3f2eSlogwang    '''Checks that igb_uio is loaded'''
1152bfe3f2eSlogwang    global dpdk_drivers
1162bfe3f2eSlogwang
1172bfe3f2eSlogwang    # list of supported modules
1182bfe3f2eSlogwang    mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
1192bfe3f2eSlogwang
1202bfe3f2eSlogwang    # first check if module is loaded
1212bfe3f2eSlogwang    for mod in mods:
1224418919fSjohnjiang        if module_is_loaded(mod["Name"]):
1232bfe3f2eSlogwang            mod["Found"] = True
1242bfe3f2eSlogwang
1252bfe3f2eSlogwang    # check if we have at least one loaded module
1262bfe3f2eSlogwang    if True not in [mod["Found"] for mod in mods] and b_flag is not None:
1274418919fSjohnjiang        print("Warning: no supported DPDK kernel modules are loaded", file=sys.stderr)
1282bfe3f2eSlogwang
1292bfe3f2eSlogwang    # change DPDK driver list to only contain drivers that are loaded
1302bfe3f2eSlogwang    dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
1312bfe3f2eSlogwang
1322bfe3f2eSlogwang
1332bfe3f2eSlogwangdef has_driver(dev_id):
1342bfe3f2eSlogwang    '''return true if a device is assigned to a driver. False otherwise'''
1352bfe3f2eSlogwang    return "Driver_str" in devices[dev_id]
1362bfe3f2eSlogwang
1372bfe3f2eSlogwang
1382bfe3f2eSlogwangdef get_pci_device_details(dev_id, probe_lspci):
1392bfe3f2eSlogwang    '''This function gets additional details for a PCI device'''
1402bfe3f2eSlogwang    device = {}
1412bfe3f2eSlogwang
1422bfe3f2eSlogwang    if probe_lspci:
143*2d9fd380Sjfb8856606        extra_info = subprocess.check_output(["lspci", "-vmmks", dev_id]).splitlines()
1442bfe3f2eSlogwang        # parse lspci details
1452bfe3f2eSlogwang        for line in extra_info:
146*2d9fd380Sjfb8856606            if not line:
1472bfe3f2eSlogwang                continue
148*2d9fd380Sjfb8856606            name, value = line.decode("utf8").split("\t", 1)
1492bfe3f2eSlogwang            name = name.strip(":") + "_str"
1502bfe3f2eSlogwang            device[name] = value
1512bfe3f2eSlogwang    # check for a unix interface name
1522bfe3f2eSlogwang    device["Interface"] = ""
1532bfe3f2eSlogwang    for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
1542bfe3f2eSlogwang        if "net" in dirs:
1552bfe3f2eSlogwang            device["Interface"] = \
1562bfe3f2eSlogwang                ",".join(os.listdir(os.path.join(base, "net")))
1572bfe3f2eSlogwang            break
1582bfe3f2eSlogwang    # check if a port is used for ssh connection
1592bfe3f2eSlogwang    device["Ssh_if"] = False
1602bfe3f2eSlogwang    device["Active"] = ""
1612bfe3f2eSlogwang
1622bfe3f2eSlogwang    return device
1632bfe3f2eSlogwang
1642bfe3f2eSlogwangdef clear_data():
1652bfe3f2eSlogwang    '''This function clears any old data'''
1664b05018fSfengbojiang    global devices
1672bfe3f2eSlogwang    devices = {}
1682bfe3f2eSlogwang
1692bfe3f2eSlogwangdef get_device_details(devices_type):
1702bfe3f2eSlogwang    '''This function populates the "devices" dictionary. The keys used are
1712bfe3f2eSlogwang    the pci addresses (domain:bus:slot.func). The values are themselves
1722bfe3f2eSlogwang    dictionaries - one for each NIC.'''
1732bfe3f2eSlogwang    global devices
1742bfe3f2eSlogwang    global dpdk_drivers
1752bfe3f2eSlogwang
1762bfe3f2eSlogwang    # first loop through and read details for all devices
1772bfe3f2eSlogwang    # request machine readable format, with numeric IDs and String
1782bfe3f2eSlogwang    dev = {}
179*2d9fd380Sjfb8856606    dev_lines = subprocess.check_output(["lspci", "-Dvmmnnk"]).splitlines()
1802bfe3f2eSlogwang    for dev_line in dev_lines:
181*2d9fd380Sjfb8856606        if not dev_line:
1822bfe3f2eSlogwang            if device_type_match(dev, devices_type):
1832bfe3f2eSlogwang                # Replace "Driver" with "Driver_str" to have consistency of
1842bfe3f2eSlogwang                # of dictionary key names
1852bfe3f2eSlogwang                if "Driver" in dev.keys():
1862bfe3f2eSlogwang                    dev["Driver_str"] = dev.pop("Driver")
1872bfe3f2eSlogwang                if "Module" in dev.keys():
1882bfe3f2eSlogwang                    dev["Module_str"] = dev.pop("Module")
1892bfe3f2eSlogwang                # use dict to make copy of dev
1902bfe3f2eSlogwang                devices[dev["Slot"]] = dict(dev)
1912bfe3f2eSlogwang            # Clear previous device's data
1922bfe3f2eSlogwang            dev = {}
1932bfe3f2eSlogwang        else:
194*2d9fd380Sjfb8856606            name, value = dev_line.decode("utf8").split("\t", 1)
1952bfe3f2eSlogwang            value_list = value.rsplit(' ', 1)
196*2d9fd380Sjfb8856606            if value_list:
1972bfe3f2eSlogwang                # String stored in <name>_str
1982bfe3f2eSlogwang                dev[name.rstrip(":") + '_str'] = value_list[0]
1992bfe3f2eSlogwang            # Numeric IDs
2002bfe3f2eSlogwang            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
2012bfe3f2eSlogwang                .rstrip("]").lstrip("[")
2022bfe3f2eSlogwang
2032bfe3f2eSlogwang    if devices_type == network_devices:
2042bfe3f2eSlogwang        # check what is the interface if any for an ssh connection if
2052bfe3f2eSlogwang        # any to this host, so we can mark it later.
2062bfe3f2eSlogwang        ssh_if = []
207*2d9fd380Sjfb8856606        route = subprocess.check_output(["ip", "-o", "route"])
2082bfe3f2eSlogwang        # filter out all lines for 169.254 routes
2092bfe3f2eSlogwang        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
2102bfe3f2eSlogwang                                 route.decode().splitlines()))
2112bfe3f2eSlogwang        rt_info = route.split()
2122bfe3f2eSlogwang        for i in range(len(rt_info) - 1):
2132bfe3f2eSlogwang            if rt_info[i] == "dev":
2142bfe3f2eSlogwang                ssh_if.append(rt_info[i+1])
2152bfe3f2eSlogwang
2162bfe3f2eSlogwang    # based on the basic info, get extended text details
2172bfe3f2eSlogwang    for d in devices.keys():
2182bfe3f2eSlogwang        if not device_type_match(devices[d], devices_type):
2192bfe3f2eSlogwang            continue
2202bfe3f2eSlogwang
2212bfe3f2eSlogwang        # get additional info and add it to existing data
2222bfe3f2eSlogwang        devices[d] = devices[d].copy()
2232bfe3f2eSlogwang        # No need to probe lspci
2242bfe3f2eSlogwang        devices[d].update(get_pci_device_details(d, False).items())
2252bfe3f2eSlogwang
2262bfe3f2eSlogwang        if devices_type == network_devices:
2272bfe3f2eSlogwang            for _if in ssh_if:
2282bfe3f2eSlogwang                if _if in devices[d]["Interface"].split(","):
2292bfe3f2eSlogwang                    devices[d]["Ssh_if"] = True
2302bfe3f2eSlogwang                    devices[d]["Active"] = "*Active*"
2312bfe3f2eSlogwang                    break
2322bfe3f2eSlogwang
2332bfe3f2eSlogwang        # add igb_uio to list of supporting modules if needed
2342bfe3f2eSlogwang        if "Module_str" in devices[d]:
2352bfe3f2eSlogwang            for driver in dpdk_drivers:
2362bfe3f2eSlogwang                if driver not in devices[d]["Module_str"]:
2372bfe3f2eSlogwang                    devices[d]["Module_str"] = \
2382bfe3f2eSlogwang                        devices[d]["Module_str"] + ",%s" % driver
2392bfe3f2eSlogwang        else:
2402bfe3f2eSlogwang            devices[d]["Module_str"] = ",".join(dpdk_drivers)
2412bfe3f2eSlogwang
2422bfe3f2eSlogwang        # make sure the driver and module strings do not have any duplicates
2432bfe3f2eSlogwang        if has_driver(d):
2442bfe3f2eSlogwang            modules = devices[d]["Module_str"].split(",")
2452bfe3f2eSlogwang            if devices[d]["Driver_str"] in modules:
2462bfe3f2eSlogwang                modules.remove(devices[d]["Driver_str"])
2472bfe3f2eSlogwang                devices[d]["Module_str"] = ",".join(modules)
2482bfe3f2eSlogwang
2492bfe3f2eSlogwang
2502bfe3f2eSlogwangdef device_type_match(dev, devices_type):
2512bfe3f2eSlogwang    for i in range(len(devices_type)):
2522bfe3f2eSlogwang        param_count = len(
2532bfe3f2eSlogwang            [x for x in devices_type[i].values() if x is not None])
2542bfe3f2eSlogwang        match_count = 0
2552bfe3f2eSlogwang        if dev["Class"][0:2] == devices_type[i]["Class"]:
2562bfe3f2eSlogwang            match_count = match_count + 1
2572bfe3f2eSlogwang            for key in devices_type[i].keys():
2582bfe3f2eSlogwang                if key != 'Class' and devices_type[i][key]:
2592bfe3f2eSlogwang                    value_list = devices_type[i][key].split(',')
2602bfe3f2eSlogwang                    for value in value_list:
2612bfe3f2eSlogwang                        if value.strip(' ') == dev[key]:
2622bfe3f2eSlogwang                            match_count = match_count + 1
2632bfe3f2eSlogwang            # count must be the number of non None parameters to match
2642bfe3f2eSlogwang            if match_count == param_count:
2652bfe3f2eSlogwang                return True
2662bfe3f2eSlogwang    return False
2672bfe3f2eSlogwang
2682bfe3f2eSlogwangdef dev_id_from_dev_name(dev_name):
2692bfe3f2eSlogwang    '''Take a device "name" - a string passed in by user to identify a NIC
2702bfe3f2eSlogwang    device, and determine the device id - i.e. the domain:bus:slot.func - for
2712bfe3f2eSlogwang    it, which can then be used to index into the devices array'''
2722bfe3f2eSlogwang
2732bfe3f2eSlogwang    # check if it's already a suitable index
2742bfe3f2eSlogwang    if dev_name in devices:
2752bfe3f2eSlogwang        return dev_name
2762bfe3f2eSlogwang    # check if it's an index just missing the domain part
277*2d9fd380Sjfb8856606    if "0000:" + dev_name in devices:
2782bfe3f2eSlogwang        return "0000:" + dev_name
279*2d9fd380Sjfb8856606
2802bfe3f2eSlogwang    # check if it's an interface name, e.g. eth1
2812bfe3f2eSlogwang    for d in devices.keys():
2822bfe3f2eSlogwang        if dev_name in devices[d]["Interface"].split(","):
2832bfe3f2eSlogwang            return devices[d]["Slot"]
2842bfe3f2eSlogwang    # if nothing else matches - error
2854418919fSjohnjiang    raise ValueError("Unknown device: %s. "
2862bfe3f2eSlogwang                     "Please specify device in \"bus:slot.func\" format" % dev_name)
2872bfe3f2eSlogwang
2882bfe3f2eSlogwang
2892bfe3f2eSlogwangdef unbind_one(dev_id, force):
2902bfe3f2eSlogwang    '''Unbind the device identified by "dev_id" from its current driver'''
2912bfe3f2eSlogwang    dev = devices[dev_id]
2922bfe3f2eSlogwang    if not has_driver(dev_id):
2934418919fSjohnjiang        print("Notice: %s %s %s is not currently managed by any driver" %
2944418919fSjohnjiang              (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr)
2952bfe3f2eSlogwang        return
2962bfe3f2eSlogwang
2972bfe3f2eSlogwang    # prevent us disconnecting ourselves
2982bfe3f2eSlogwang    if dev["Ssh_if"] and not force:
2994418919fSjohnjiang        print("Warning: routing table indicates that interface %s is active. "
3004418919fSjohnjiang              "Skipping unbind" % dev_id, file=sys.stderr)
3012bfe3f2eSlogwang        return
3022bfe3f2eSlogwang
3032bfe3f2eSlogwang    # write to /sys to unbind
3042bfe3f2eSlogwang    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
3052bfe3f2eSlogwang    try:
3062bfe3f2eSlogwang        f = open(filename, "a")
3072bfe3f2eSlogwang    except:
3084418919fSjohnjiang        sys.exit("Error: unbind failed for %s - Cannot open %s" %
3094418919fSjohnjiang                 (dev_id, filename))
3102bfe3f2eSlogwang    f.write(dev_id)
3112bfe3f2eSlogwang    f.close()
3122bfe3f2eSlogwang
3132bfe3f2eSlogwang
3142bfe3f2eSlogwangdef bind_one(dev_id, driver, force):
3152bfe3f2eSlogwang    '''Bind the device given by "dev_id" to the driver "driver". If the device
3162bfe3f2eSlogwang    is already bound to a different driver, it will be unbound first'''
3172bfe3f2eSlogwang    dev = devices[dev_id]
3182bfe3f2eSlogwang    saved_driver = None  # used to rollback any unbind in case of failure
3192bfe3f2eSlogwang
3202bfe3f2eSlogwang    # prevent disconnection of our ssh session
3212bfe3f2eSlogwang    if dev["Ssh_if"] and not force:
3224418919fSjohnjiang        print("Warning: routing table indicates that interface %s is active. "
3234418919fSjohnjiang              "Not modifying" % dev_id, file=sys.stderr)
3242bfe3f2eSlogwang        return
3252bfe3f2eSlogwang
3262bfe3f2eSlogwang    # unbind any existing drivers we don't want
3272bfe3f2eSlogwang    if has_driver(dev_id):
3282bfe3f2eSlogwang        if dev["Driver_str"] == driver:
3294418919fSjohnjiang            print("Notice: %s already bound to driver %s, skipping" %
3304418919fSjohnjiang                  (dev_id, driver), file=sys.stderr)
3312bfe3f2eSlogwang            return
3322bfe3f2eSlogwang        saved_driver = dev["Driver_str"]
3332bfe3f2eSlogwang        unbind_one(dev_id, force)
3342bfe3f2eSlogwang        dev["Driver_str"] = ""  # clear driver string
3352bfe3f2eSlogwang
3362bfe3f2eSlogwang    # For kernels >= 3.15 driver_override can be used to specify the driver
3372bfe3f2eSlogwang    # for a device rather than relying on the driver to provide a positive
3382bfe3f2eSlogwang    # match of the device.  The existing process of looking up
3392bfe3f2eSlogwang    # the vendor and device ID, adding them to the driver new_id,
3402bfe3f2eSlogwang    # will erroneously bind other devices too which has the additional burden
3412bfe3f2eSlogwang    # of unbinding those devices
3422bfe3f2eSlogwang    if driver in dpdk_drivers:
3432bfe3f2eSlogwang        filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
344*2d9fd380Sjfb8856606        if exists(filename):
3452bfe3f2eSlogwang            try:
3462bfe3f2eSlogwang                f = open(filename, "w")
3472bfe3f2eSlogwang            except:
3482bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot open %s"
3494418919fSjohnjiang                      % (dev_id, filename), file=sys.stderr)
3502bfe3f2eSlogwang                return
3512bfe3f2eSlogwang            try:
3522bfe3f2eSlogwang                f.write("%s" % driver)
3532bfe3f2eSlogwang                f.close()
3542bfe3f2eSlogwang            except:
3552bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot write driver %s to "
3564418919fSjohnjiang                      "PCI ID " % (dev_id, driver), file=sys.stderr)
3572bfe3f2eSlogwang                return
3582bfe3f2eSlogwang        # For kernels < 3.15 use new_id to add PCI id's to the driver
3592bfe3f2eSlogwang        else:
3602bfe3f2eSlogwang            filename = "/sys/bus/pci/drivers/%s/new_id" % driver
3612bfe3f2eSlogwang            try:
3622bfe3f2eSlogwang                f = open(filename, "w")
3632bfe3f2eSlogwang            except:
3642bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot open %s"
3654418919fSjohnjiang                      % (dev_id, filename), file=sys.stderr)
3662bfe3f2eSlogwang                return
3672bfe3f2eSlogwang            try:
3682bfe3f2eSlogwang                # Convert Device and Vendor Id to int to write to new_id
3692bfe3f2eSlogwang                f.write("%04x %04x" % (int(dev["Vendor"], 16),
3702bfe3f2eSlogwang                                       int(dev["Device"], 16)))
3712bfe3f2eSlogwang                f.close()
3722bfe3f2eSlogwang            except:
3732bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot write new PCI ID to "
3744418919fSjohnjiang                      "driver %s" % (dev_id, driver), file=sys.stderr)
3752bfe3f2eSlogwang                return
3762bfe3f2eSlogwang
3772bfe3f2eSlogwang    # do the bind by writing to /sys
3782bfe3f2eSlogwang    filename = "/sys/bus/pci/drivers/%s/bind" % driver
3792bfe3f2eSlogwang    try:
3802bfe3f2eSlogwang        f = open(filename, "a")
3812bfe3f2eSlogwang    except:
3822bfe3f2eSlogwang        print("Error: bind failed for %s - Cannot open %s"
3834418919fSjohnjiang              % (dev_id, filename), file=sys.stderr)
3842bfe3f2eSlogwang        if saved_driver is not None:  # restore any previous driver
3852bfe3f2eSlogwang            bind_one(dev_id, saved_driver, force)
3862bfe3f2eSlogwang        return
3872bfe3f2eSlogwang    try:
3882bfe3f2eSlogwang        f.write(dev_id)
3892bfe3f2eSlogwang        f.close()
3902bfe3f2eSlogwang    except:
3912bfe3f2eSlogwang        # for some reason, closing dev_id after adding a new PCI ID to new_id
3922bfe3f2eSlogwang        # results in IOError. however, if the device was successfully bound,
3932bfe3f2eSlogwang        # we don't care for any errors and can safely ignore IOError
3942bfe3f2eSlogwang        tmp = get_pci_device_details(dev_id, True)
3952bfe3f2eSlogwang        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
3962bfe3f2eSlogwang            return
3972bfe3f2eSlogwang        print("Error: bind failed for %s - Cannot bind to driver %s"
3984418919fSjohnjiang              % (dev_id, driver), file=sys.stderr)
3992bfe3f2eSlogwang        if saved_driver is not None:  # restore any previous driver
4002bfe3f2eSlogwang            bind_one(dev_id, saved_driver, force)
4012bfe3f2eSlogwang        return
4022bfe3f2eSlogwang
4032bfe3f2eSlogwang    # For kernels > 3.15 driver_override is used to bind a device to a driver.
4042bfe3f2eSlogwang    # Before unbinding it, overwrite driver_override with empty string so that
4052bfe3f2eSlogwang    # the device can be bound to any other driver
4062bfe3f2eSlogwang    filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
407*2d9fd380Sjfb8856606    if exists(filename):
4082bfe3f2eSlogwang        try:
4092bfe3f2eSlogwang            f = open(filename, "w")
4102bfe3f2eSlogwang        except:
4114418919fSjohnjiang            sys.exit("Error: unbind failed for %s - Cannot open %s"
4122bfe3f2eSlogwang                     % (dev_id, filename))
4132bfe3f2eSlogwang        try:
4142bfe3f2eSlogwang            f.write("\00")
4152bfe3f2eSlogwang            f.close()
4162bfe3f2eSlogwang        except:
4174418919fSjohnjiang            sys.exit("Error: unbind failed for %s - Cannot open %s"
4182bfe3f2eSlogwang                     % (dev_id, filename))
4192bfe3f2eSlogwang
4202bfe3f2eSlogwang
4212bfe3f2eSlogwangdef unbind_all(dev_list, force=False):
4222bfe3f2eSlogwang    """Unbind method, takes a list of device locations"""
4232bfe3f2eSlogwang
4242bfe3f2eSlogwang    if dev_list[0] == "dpdk":
4252bfe3f2eSlogwang        for d in devices.keys():
4262bfe3f2eSlogwang            if "Driver_str" in devices[d]:
4272bfe3f2eSlogwang                if devices[d]["Driver_str"] in dpdk_drivers:
4282bfe3f2eSlogwang                    unbind_one(devices[d]["Slot"], force)
4292bfe3f2eSlogwang        return
4302bfe3f2eSlogwang
4314418919fSjohnjiang    try:
4322bfe3f2eSlogwang        dev_list = map(dev_id_from_dev_name, dev_list)
4334418919fSjohnjiang    except ValueError as ex:
4344418919fSjohnjiang        print(ex)
4354418919fSjohnjiang        sys.exit(1)
4364418919fSjohnjiang
4372bfe3f2eSlogwang    for d in dev_list:
4382bfe3f2eSlogwang        unbind_one(d, force)
4392bfe3f2eSlogwang
4402bfe3f2eSlogwang
4412bfe3f2eSlogwangdef bind_all(dev_list, driver, force=False):
4422bfe3f2eSlogwang    """Bind method, takes a list of device locations"""
4432bfe3f2eSlogwang    global devices
4442bfe3f2eSlogwang
4454418919fSjohnjiang    # a common user error is to forget to specify the driver the devices need to
4464418919fSjohnjiang    # be bound to. check if the driver is a valid device, and if it is, show
4474418919fSjohnjiang    # a meaningful error.
4484418919fSjohnjiang    try:
4494418919fSjohnjiang        dev_id_from_dev_name(driver)
4504418919fSjohnjiang        # if we've made it this far, this means that the "driver" was a valid
4514418919fSjohnjiang        # device string, so it's probably not a valid driver name.
4524418919fSjohnjiang        sys.exit("Error: Driver '%s' does not look like a valid driver. " \
4534418919fSjohnjiang                 "Did you forget to specify the driver to bind devices to?" % driver)
4544418919fSjohnjiang    except ValueError:
4554418919fSjohnjiang        # driver generated error - it's not a valid device ID, so all is well
4564418919fSjohnjiang        pass
4574418919fSjohnjiang
4584418919fSjohnjiang    # check if we're attempting to bind to a driver that isn't loaded
4594418919fSjohnjiang    if not module_is_loaded(driver.replace('-', '_')):
4604418919fSjohnjiang        sys.exit("Error: Driver '%s' is not loaded." % driver)
4614418919fSjohnjiang
4624418919fSjohnjiang    try:
4632bfe3f2eSlogwang        dev_list = map(dev_id_from_dev_name, dev_list)
4644418919fSjohnjiang    except ValueError as ex:
4654418919fSjohnjiang        sys.exit(ex)
4662bfe3f2eSlogwang
4672bfe3f2eSlogwang    for d in dev_list:
4682bfe3f2eSlogwang        bind_one(d, driver, force)
4692bfe3f2eSlogwang
4702bfe3f2eSlogwang    # For kernels < 3.15 when binding devices to a generic driver
4712bfe3f2eSlogwang    # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
4722bfe3f2eSlogwang    # that are not bound to any other driver could be bound even if no one has
4732bfe3f2eSlogwang    # asked them to. hence, we check the list of drivers again, and see if
4742bfe3f2eSlogwang    # some of the previously-unbound devices were erroneously bound.
475*2d9fd380Sjfb8856606    if not exists("/sys/bus/pci/devices/%s/driver_override" % d):
4762bfe3f2eSlogwang        for d in devices.keys():
4772bfe3f2eSlogwang            # skip devices that were already bound or that we know should be bound
4782bfe3f2eSlogwang            if "Driver_str" in devices[d] or d in dev_list:
4792bfe3f2eSlogwang                continue
4802bfe3f2eSlogwang
4812bfe3f2eSlogwang            # update information about this device
4822bfe3f2eSlogwang            devices[d] = dict(devices[d].items() +
4832bfe3f2eSlogwang                              get_pci_device_details(d, True).items())
4842bfe3f2eSlogwang
4852bfe3f2eSlogwang            # check if updated information indicates that the device was bound
4862bfe3f2eSlogwang            if "Driver_str" in devices[d]:
4872bfe3f2eSlogwang                unbind_one(d, force)
4882bfe3f2eSlogwang
4892bfe3f2eSlogwang
4902bfe3f2eSlogwangdef display_devices(title, dev_list, extra_params=None):
4912bfe3f2eSlogwang    '''Displays to the user the details of a list of devices given in
4922bfe3f2eSlogwang    "dev_list". The "extra_params" parameter, if given, should contain a string
4932bfe3f2eSlogwang     with %()s fields in it for replacement by the named fields in each
4942bfe3f2eSlogwang     device's dictionary.'''
4952bfe3f2eSlogwang    strings = []  # this holds the strings to print. We sort before printing
4962bfe3f2eSlogwang    print("\n%s" % title)
4972bfe3f2eSlogwang    print("="*len(title))
498*2d9fd380Sjfb8856606    if not dev_list:
4992bfe3f2eSlogwang        strings.append("<none>")
5002bfe3f2eSlogwang    else:
5012bfe3f2eSlogwang        for dev in dev_list:
5022bfe3f2eSlogwang            if extra_params is not None:
5032bfe3f2eSlogwang                strings.append("%s '%s %s' %s" % (dev["Slot"],
5042bfe3f2eSlogwang                                                  dev["Device_str"],
5052bfe3f2eSlogwang                                                  dev["Device"],
5062bfe3f2eSlogwang                                                  extra_params % dev))
5072bfe3f2eSlogwang            else:
5082bfe3f2eSlogwang                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
5092bfe3f2eSlogwang    # sort before printing, so that the entries appear in PCI order
5102bfe3f2eSlogwang    strings.sort()
5112bfe3f2eSlogwang    print("\n".join(strings))  # print one per line
5122bfe3f2eSlogwang
513*2d9fd380Sjfb8856606def show_device_status(devices_type, device_name, if_field=False):
5142bfe3f2eSlogwang    global dpdk_drivers
5152bfe3f2eSlogwang    kernel_drv = []
5162bfe3f2eSlogwang    dpdk_drv = []
5172bfe3f2eSlogwang    no_drv = []
5182bfe3f2eSlogwang
5192bfe3f2eSlogwang    # split our list of network devices into the three categories above
5202bfe3f2eSlogwang    for d in devices.keys():
5212bfe3f2eSlogwang        if device_type_match(devices[d], devices_type):
5222bfe3f2eSlogwang            if not has_driver(d):
5232bfe3f2eSlogwang                no_drv.append(devices[d])
5242bfe3f2eSlogwang                continue
5252bfe3f2eSlogwang            if devices[d]["Driver_str"] in dpdk_drivers:
5262bfe3f2eSlogwang                dpdk_drv.append(devices[d])
5272bfe3f2eSlogwang            else:
5282bfe3f2eSlogwang                kernel_drv.append(devices[d])
5292bfe3f2eSlogwang
530d30ea906Sjfb8856606    n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
531d30ea906Sjfb8856606
532d30ea906Sjfb8856606    # don't bother displaying anything if there are no devices
533d30ea906Sjfb8856606    if n_devs == 0:
534d30ea906Sjfb8856606        msg = "No '%s' devices detected" % device_name
535d30ea906Sjfb8856606        print("")
536d30ea906Sjfb8856606        print(msg)
537d30ea906Sjfb8856606        print("".join('=' * len(msg)))
538d30ea906Sjfb8856606        return
539d30ea906Sjfb8856606
5402bfe3f2eSlogwang    # print each category separately, so we can clearly see what's used by DPDK
541*2d9fd380Sjfb8856606    if dpdk_drv:
5422bfe3f2eSlogwang        display_devices("%s devices using DPDK-compatible driver" % device_name,
5432bfe3f2eSlogwang                        dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
544*2d9fd380Sjfb8856606    if kernel_drv:
545*2d9fd380Sjfb8856606        if_text = ""
546*2d9fd380Sjfb8856606        if if_field:
547*2d9fd380Sjfb8856606            if_text = "if=%(Interface)s "
5482bfe3f2eSlogwang        display_devices("%s devices using kernel driver" % device_name, kernel_drv,
549*2d9fd380Sjfb8856606                        if_text + "drv=%(Driver_str)s "
5502bfe3f2eSlogwang                        "unused=%(Module_str)s %(Active)s")
551*2d9fd380Sjfb8856606    if no_drv:
5522bfe3f2eSlogwang        display_devices("Other %s devices" % device_name, no_drv,
5532bfe3f2eSlogwang                        "unused=%(Module_str)s")
5542bfe3f2eSlogwang
5552bfe3f2eSlogwangdef show_status():
5562bfe3f2eSlogwang    '''Function called when the script is passed the "--status" option.
5572bfe3f2eSlogwang    Displays to the user what devices are bound to the igb_uio driver, the
5582bfe3f2eSlogwang    kernel driver or to no driver'''
5592bfe3f2eSlogwang
560*2d9fd380Sjfb8856606    if status_dev in ["net", "all"]:
561*2d9fd380Sjfb8856606        show_device_status(network_devices, "Network", if_field=True)
5622bfe3f2eSlogwang
563*2d9fd380Sjfb8856606    if status_dev in ["baseband", "all"]:
5644418919fSjohnjiang        show_device_status(baseband_devices, "Baseband")
5654418919fSjohnjiang
566*2d9fd380Sjfb8856606    if status_dev in ["crypto", "all"]:
5672bfe3f2eSlogwang        show_device_status(crypto_devices, "Crypto")
5682bfe3f2eSlogwang
569*2d9fd380Sjfb8856606    if status_dev in ["event", "all"]:
5702bfe3f2eSlogwang        show_device_status(eventdev_devices, "Eventdev")
5712bfe3f2eSlogwang
572*2d9fd380Sjfb8856606    if status_dev in ["mempool", "all"]:
5732bfe3f2eSlogwang        show_device_status(mempool_devices, "Mempool")
5742bfe3f2eSlogwang
575*2d9fd380Sjfb8856606    if status_dev in ["compress", "all"]:
576d30ea906Sjfb8856606        show_device_status(compress_devices, "Compress")
577d30ea906Sjfb8856606
578*2d9fd380Sjfb8856606    if status_dev in ["misc", "all"]:
5794418919fSjohnjiang        show_device_status(misc_devices, "Misc (rawdev)")
580d30ea906Sjfb8856606
581*2d9fd380Sjfb8856606    if status_dev in ["regex", "all"]:
582*2d9fd380Sjfb8856606        show_device_status(regex_devices, "Regex")
583*2d9fd380Sjfb8856606
584*2d9fd380Sjfb8856606
585*2d9fd380Sjfb8856606def pci_glob(arg):
586*2d9fd380Sjfb8856606    '''Returns a list containing either:
587*2d9fd380Sjfb8856606    * List of PCI B:D:F matching arg, using shell wildcards e.g. 80:04.*
588*2d9fd380Sjfb8856606    * Only the passed arg if matching list is empty'''
589*2d9fd380Sjfb8856606    sysfs_path = "/sys/bus/pci/devices"
590*2d9fd380Sjfb8856606    for _glob in [arg, '0000:' + arg]:
591*2d9fd380Sjfb8856606        paths = [basename(path) for path in glob(path_join(sysfs_path, _glob))]
592*2d9fd380Sjfb8856606        if paths:
593*2d9fd380Sjfb8856606            return paths
594*2d9fd380Sjfb8856606    return [arg]
595*2d9fd380Sjfb8856606
596*2d9fd380Sjfb8856606
5972bfe3f2eSlogwangdef parse_args():
5982bfe3f2eSlogwang    '''Parses the command-line arguments given by the user and takes the
5992bfe3f2eSlogwang    appropriate action for each'''
6002bfe3f2eSlogwang    global b_flag
6012bfe3f2eSlogwang    global status_flag
6022bfe3f2eSlogwang    global status_dev
6032bfe3f2eSlogwang    global force_flag
6042bfe3f2eSlogwang    global args
6052bfe3f2eSlogwang
606*2d9fd380Sjfb8856606    parser = argparse.ArgumentParser(
607*2d9fd380Sjfb8856606        description='Utility to bind and unbind devices from Linux kernel',
608*2d9fd380Sjfb8856606        formatter_class=argparse.RawDescriptionHelpFormatter,
609*2d9fd380Sjfb8856606        epilog="""
610*2d9fd380Sjfb8856606Examples:
611*2d9fd380Sjfb8856606---------
6122bfe3f2eSlogwang
613*2d9fd380Sjfb8856606To display current device status:
614*2d9fd380Sjfb8856606        %(prog)s --status
615*2d9fd380Sjfb8856606
616*2d9fd380Sjfb8856606To display current network device status:
617*2d9fd380Sjfb8856606        %(prog)s --status-dev net
618*2d9fd380Sjfb8856606
619*2d9fd380Sjfb8856606To bind eth1 from the current driver and move to use vfio-pci
620*2d9fd380Sjfb8856606        %(prog)s --bind=vfio-pci eth1
621*2d9fd380Sjfb8856606
622*2d9fd380Sjfb8856606To unbind 0000:01:00.0 from using any driver
623*2d9fd380Sjfb8856606        %(prog)s -u 0000:01:00.0
624*2d9fd380Sjfb8856606
625*2d9fd380Sjfb8856606To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
626*2d9fd380Sjfb8856606        %(prog)s -b ixgbe 02:00.0 02:00.1
627*2d9fd380Sjfb8856606""")
628*2d9fd380Sjfb8856606
629*2d9fd380Sjfb8856606    parser.add_argument(
630*2d9fd380Sjfb8856606        '-s',
631*2d9fd380Sjfb8856606        '--status',
632*2d9fd380Sjfb8856606        action='store_true',
633*2d9fd380Sjfb8856606        help="Print the current status of all known devices.")
634*2d9fd380Sjfb8856606    parser.add_argument(
635*2d9fd380Sjfb8856606        '--status-dev',
636*2d9fd380Sjfb8856606        help="Print the status of given device group.",
637*2d9fd380Sjfb8856606        choices=['baseband', 'compress', 'crypto', 'event',
638*2d9fd380Sjfb8856606                'mempool', 'misc', 'net', 'regex'])
639*2d9fd380Sjfb8856606    bind_group = parser.add_mutually_exclusive_group()
640*2d9fd380Sjfb8856606    bind_group.add_argument(
641*2d9fd380Sjfb8856606        '-b',
642*2d9fd380Sjfb8856606        '--bind',
643*2d9fd380Sjfb8856606        metavar='DRIVER',
644*2d9fd380Sjfb8856606        help="Select the driver to use or \"none\" to unbind the device")
645*2d9fd380Sjfb8856606    bind_group.add_argument(
646*2d9fd380Sjfb8856606        '-u',
647*2d9fd380Sjfb8856606        '--unbind',
648*2d9fd380Sjfb8856606        action='store_true',
649*2d9fd380Sjfb8856606        help="Unbind a device (equivalent to \"-b none\")")
650*2d9fd380Sjfb8856606    parser.add_argument(
651*2d9fd380Sjfb8856606        '--force',
652*2d9fd380Sjfb8856606        action='store_true',
653*2d9fd380Sjfb8856606        help="""
654*2d9fd380Sjfb8856606Override restriction on binding devices in use by Linux"
655*2d9fd380Sjfb8856606WARNING: This can lead to loss of network connection and should be used with caution.
656*2d9fd380Sjfb8856606""")
657*2d9fd380Sjfb8856606    parser.add_argument(
658*2d9fd380Sjfb8856606        'devices',
659*2d9fd380Sjfb8856606        metavar='DEVICE',
660*2d9fd380Sjfb8856606        nargs='*',
661*2d9fd380Sjfb8856606        help="""
662*2d9fd380Sjfb8856606Device specified as PCI "domain:bus:slot.func" syntax or "bus:slot.func" syntax.
663*2d9fd380Sjfb8856606For devices bound to Linux kernel drivers, they may be referred to by interface name.
664*2d9fd380Sjfb8856606""")
665*2d9fd380Sjfb8856606
666*2d9fd380Sjfb8856606    opt = parser.parse_args()
667*2d9fd380Sjfb8856606
668*2d9fd380Sjfb8856606    if opt.status_dev:
6692bfe3f2eSlogwang        status_flag = True
670*2d9fd380Sjfb8856606        status_dev = opt.status_dev
671*2d9fd380Sjfb8856606    if opt.status:
6722bfe3f2eSlogwang        status_flag = True
6732bfe3f2eSlogwang        status_dev = "all"
674*2d9fd380Sjfb8856606    if opt.force:
6752bfe3f2eSlogwang        force_flag = True
676*2d9fd380Sjfb8856606    if opt.bind:
677*2d9fd380Sjfb8856606        b_flag = opt.bind
678*2d9fd380Sjfb8856606    elif opt.unbind:
6792bfe3f2eSlogwang        b_flag = "none"
680*2d9fd380Sjfb8856606    args = opt.devices
6812bfe3f2eSlogwang
682*2d9fd380Sjfb8856606    if not b_flag and not status_flag:
683*2d9fd380Sjfb8856606        print("Error: No action specified for devices. "
684*2d9fd380Sjfb8856606              "Please give a --bind, --ubind or --status option",
685*2d9fd380Sjfb8856606              file=sys.stderr)
686*2d9fd380Sjfb8856606        parser.print_usage()
687*2d9fd380Sjfb8856606        sys.exit(1)
688*2d9fd380Sjfb8856606
689*2d9fd380Sjfb8856606    if b_flag and not args:
690*2d9fd380Sjfb8856606        print("Error: No devices specified.", file=sys.stderr)
691*2d9fd380Sjfb8856606        parser.print_usage()
692*2d9fd380Sjfb8856606        sys.exit(1)
693*2d9fd380Sjfb8856606
694*2d9fd380Sjfb8856606    # resolve any PCI globs in the args
695*2d9fd380Sjfb8856606    new_args = []
696*2d9fd380Sjfb8856606    for arg in args:
697*2d9fd380Sjfb8856606        new_args.extend(pci_glob(arg))
698*2d9fd380Sjfb8856606    args = new_args
6992bfe3f2eSlogwang
7002bfe3f2eSlogwangdef do_arg_actions():
7012bfe3f2eSlogwang    '''do the actual action requested by the user'''
7022bfe3f2eSlogwang    global b_flag
7032bfe3f2eSlogwang    global status_flag
7042bfe3f2eSlogwang    global force_flag
7052bfe3f2eSlogwang    global args
7062bfe3f2eSlogwang
707*2d9fd380Sjfb8856606    if b_flag in ["none", "None"]:
7082bfe3f2eSlogwang        unbind_all(args, force_flag)
7092bfe3f2eSlogwang    elif b_flag is not None:
7102bfe3f2eSlogwang        bind_all(args, b_flag, force_flag)
7112bfe3f2eSlogwang    if status_flag:
7122bfe3f2eSlogwang        if b_flag is not None:
7132bfe3f2eSlogwang            clear_data()
7142bfe3f2eSlogwang            # refresh if we have changed anything
7152bfe3f2eSlogwang            get_device_details(network_devices)
7164418919fSjohnjiang            get_device_details(baseband_devices)
7172bfe3f2eSlogwang            get_device_details(crypto_devices)
7182bfe3f2eSlogwang            get_device_details(eventdev_devices)
7192bfe3f2eSlogwang            get_device_details(mempool_devices)
720d30ea906Sjfb8856606            get_device_details(compress_devices)
721*2d9fd380Sjfb8856606            get_device_details(regex_devices)
7224418919fSjohnjiang            get_device_details(misc_devices)
7232bfe3f2eSlogwang        show_status()
7242bfe3f2eSlogwang
7252bfe3f2eSlogwang
7262bfe3f2eSlogwangdef main():
7272bfe3f2eSlogwang    '''program main function'''
728d30ea906Sjfb8856606    # check if lspci is installed, suppress any output
729d30ea906Sjfb8856606    with open(os.devnull, 'w') as devnull:
730d30ea906Sjfb8856606        ret = subprocess.call(['which', 'lspci'],
731d30ea906Sjfb8856606                              stdout=devnull, stderr=devnull)
732d30ea906Sjfb8856606        if ret != 0:
7334418919fSjohnjiang            sys.exit("'lspci' not found - please install 'pciutils'")
7342bfe3f2eSlogwang    parse_args()
7352bfe3f2eSlogwang    check_modules()
7362bfe3f2eSlogwang    clear_data()
7372bfe3f2eSlogwang    get_device_details(network_devices)
7384418919fSjohnjiang    get_device_details(baseband_devices)
7392bfe3f2eSlogwang    get_device_details(crypto_devices)
7402bfe3f2eSlogwang    get_device_details(eventdev_devices)
7412bfe3f2eSlogwang    get_device_details(mempool_devices)
742d30ea906Sjfb8856606    get_device_details(compress_devices)
743*2d9fd380Sjfb8856606    get_device_details(regex_devices)
7444418919fSjohnjiang    get_device_details(misc_devices)
7452bfe3f2eSlogwang    do_arg_actions()
7462bfe3f2eSlogwang
7472bfe3f2eSlogwangif __name__ == "__main__":
7482bfe3f2eSlogwang    main()
749