xref: /f-stack/dpdk/usertools/dpdk-devbind.py (revision d30ea906)
12bfe3f2eSlogwang#! /usr/bin/env python
2*d30ea906Sjfb8856606# SPDX-License-Identifier: BSD-3-Clause
3*d30ea906Sjfb8856606# Copyright(c) 2010-2014 Intel Corporation
42bfe3f2eSlogwang#
52bfe3f2eSlogwang
62bfe3f2eSlogwangimport sys
72bfe3f2eSlogwangimport os
82bfe3f2eSlogwangimport getopt
92bfe3f2eSlogwangimport subprocess
102bfe3f2eSlogwangfrom os.path import exists, abspath, dirname, basename
112bfe3f2eSlogwang
122bfe3f2eSlogwang# The PCI base class for all devices
132bfe3f2eSlogwangnetwork_class = {'Class': '02', 'Vendor': None, 'Device': None,
142bfe3f2eSlogwang                    'SVendor': None, 'SDevice': None}
152bfe3f2eSlogwangencryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
162bfe3f2eSlogwang                   'SVendor': None, 'SDevice': None}
172bfe3f2eSlogwangintel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
182bfe3f2eSlogwang                   'SVendor': None, 'SDevice': None}
192bfe3f2eSlogwangcavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
202bfe3f2eSlogwang              'SVendor': None, 'SDevice': None}
212bfe3f2eSlogwangcavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
222bfe3f2eSlogwang              'SVendor': None, 'SDevice': None}
232bfe3f2eSlogwangcavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
242bfe3f2eSlogwang              'SVendor': None, 'SDevice': None}
25*d30ea906Sjfb8856606cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
26*d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
27*d30ea906Sjfb8856606cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
28*d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
29*d30ea906Sjfb8856606avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
30*d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
312bfe3f2eSlogwang
32*d30ea906Sjfb8856606network_devices = [network_class, cavium_pkx, avp_vnic]
332bfe3f2eSlogwangcrypto_devices = [encryption_class, intel_processor_class]
34*d30ea906Sjfb8856606eventdev_devices = [cavium_sso, cavium_tim]
352bfe3f2eSlogwangmempool_devices = [cavium_fpa]
36*d30ea906Sjfb8856606compress_devices = [cavium_zip]
372bfe3f2eSlogwang
382bfe3f2eSlogwang# global dict ethernet devices present. Dictionary indexed by PCI address.
392bfe3f2eSlogwang# Each device within this is itself a dictionary of device properties
402bfe3f2eSlogwangdevices = {}
412bfe3f2eSlogwang# list of supported DPDK drivers
422bfe3f2eSlogwangdpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
432bfe3f2eSlogwang
442bfe3f2eSlogwang# command-line arg flags
452bfe3f2eSlogwangb_flag = None
462bfe3f2eSlogwangstatus_flag = False
472bfe3f2eSlogwangforce_flag = False
482bfe3f2eSlogwangargs = []
492bfe3f2eSlogwang
502bfe3f2eSlogwang
512bfe3f2eSlogwangdef usage():
522bfe3f2eSlogwang    '''Print usage information for the program'''
532bfe3f2eSlogwang    argv0 = basename(sys.argv[0])
542bfe3f2eSlogwang    print("""
552bfe3f2eSlogwangUsage:
562bfe3f2eSlogwang------
572bfe3f2eSlogwang
582bfe3f2eSlogwang     %(argv0)s [options] DEVICE1 DEVICE2 ....
592bfe3f2eSlogwang
602bfe3f2eSlogwangwhere DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
612bfe3f2eSlogwangor "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
622bfe3f2eSlogwangalso be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
632bfe3f2eSlogwang
642bfe3f2eSlogwangOptions:
652bfe3f2eSlogwang    --help, --usage:
662bfe3f2eSlogwang        Display usage information and quit
672bfe3f2eSlogwang
682bfe3f2eSlogwang    -s, --status:
692bfe3f2eSlogwang        Print the current status of all known network, crypto, event
702bfe3f2eSlogwang        and mempool devices.
712bfe3f2eSlogwang        For each device, it displays the PCI domain, bus, slot and function,
722bfe3f2eSlogwang        along with a text description of the device. Depending upon whether the
732bfe3f2eSlogwang        device is being used by a kernel driver, the igb_uio driver, or no
742bfe3f2eSlogwang        driver, other relevant information will be displayed:
752bfe3f2eSlogwang        * the Linux interface name e.g. if=eth0
762bfe3f2eSlogwang        * the driver being used e.g. drv=igb_uio
772bfe3f2eSlogwang        * any suitable drivers not currently using that device
782bfe3f2eSlogwang            e.g. unused=igb_uio
792bfe3f2eSlogwang        NOTE: if this flag is passed along with a bind/unbind option, the
802bfe3f2eSlogwang        status display will always occur after the other operations have taken
812bfe3f2eSlogwang        place.
822bfe3f2eSlogwang
832bfe3f2eSlogwang    --status-dev:
842bfe3f2eSlogwang        Print the status of given device group. Supported device groups are:
85*d30ea906Sjfb8856606        "net", "crypto", "event", "mempool" and "compress"
862bfe3f2eSlogwang
872bfe3f2eSlogwang    -b driver, --bind=driver:
882bfe3f2eSlogwang        Select the driver to use or \"none\" to unbind the device
892bfe3f2eSlogwang
902bfe3f2eSlogwang    -u, --unbind:
912bfe3f2eSlogwang        Unbind a device (Equivalent to \"-b none\")
922bfe3f2eSlogwang
932bfe3f2eSlogwang    --force:
942bfe3f2eSlogwang        By default, network devices which are used by Linux - as indicated by
952bfe3f2eSlogwang        having routes in the routing table - cannot be modified. Using the
962bfe3f2eSlogwang        --force flag overrides this behavior, allowing active links to be
972bfe3f2eSlogwang        forcibly unbound.
982bfe3f2eSlogwang        WARNING: This can lead to loss of network connection and should be used
992bfe3f2eSlogwang        with caution.
1002bfe3f2eSlogwang
1012bfe3f2eSlogwangExamples:
1022bfe3f2eSlogwang---------
1032bfe3f2eSlogwang
1042bfe3f2eSlogwangTo display current device status:
1052bfe3f2eSlogwang        %(argv0)s --status
1062bfe3f2eSlogwang
1072bfe3f2eSlogwangTo display current network device status:
1082bfe3f2eSlogwang        %(argv0)s --status-dev net
1092bfe3f2eSlogwang
1102bfe3f2eSlogwangTo bind eth1 from the current driver and move to use igb_uio
1112bfe3f2eSlogwang        %(argv0)s --bind=igb_uio eth1
1122bfe3f2eSlogwang
1132bfe3f2eSlogwangTo unbind 0000:01:00.0 from using any driver
1142bfe3f2eSlogwang        %(argv0)s -u 0000:01:00.0
1152bfe3f2eSlogwang
1162bfe3f2eSlogwangTo bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
1172bfe3f2eSlogwang        %(argv0)s -b ixgbe 02:00.0 02:00.1
1182bfe3f2eSlogwang
1192bfe3f2eSlogwang    """ % locals())  # replace items from local variables
1202bfe3f2eSlogwang
1212bfe3f2eSlogwang
1222bfe3f2eSlogwang# This is roughly compatible with check_output function in subprocess module
1232bfe3f2eSlogwang# which is only available in python 2.7.
1242bfe3f2eSlogwangdef check_output(args, stderr=None):
1252bfe3f2eSlogwang    '''Run a command and capture its output'''
1262bfe3f2eSlogwang    return subprocess.Popen(args, stdout=subprocess.PIPE,
1272bfe3f2eSlogwang                            stderr=stderr).communicate()[0]
1282bfe3f2eSlogwang
1292bfe3f2eSlogwang
1302bfe3f2eSlogwangdef check_modules():
1312bfe3f2eSlogwang    '''Checks that igb_uio is loaded'''
1322bfe3f2eSlogwang    global dpdk_drivers
1332bfe3f2eSlogwang
1342bfe3f2eSlogwang    # list of supported modules
1352bfe3f2eSlogwang    mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
1362bfe3f2eSlogwang
1372bfe3f2eSlogwang    # first check if module is loaded
1382bfe3f2eSlogwang    try:
1392bfe3f2eSlogwang        # Get list of sysfs modules (both built-in and dynamically loaded)
1402bfe3f2eSlogwang        sysfs_path = '/sys/module/'
1412bfe3f2eSlogwang
1422bfe3f2eSlogwang        # Get the list of directories in sysfs_path
1432bfe3f2eSlogwang        sysfs_mods = [os.path.join(sysfs_path, o) for o
1442bfe3f2eSlogwang                      in os.listdir(sysfs_path)
1452bfe3f2eSlogwang                      if os.path.isdir(os.path.join(sysfs_path, o))]
1462bfe3f2eSlogwang
1472bfe3f2eSlogwang        # Extract the last element of '/sys/module/abc' in the array
1482bfe3f2eSlogwang        sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
1492bfe3f2eSlogwang
1502bfe3f2eSlogwang        # special case for vfio_pci (module is named vfio-pci,
1512bfe3f2eSlogwang        # but its .ko is named vfio_pci)
1522bfe3f2eSlogwang        sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
1532bfe3f2eSlogwang
1542bfe3f2eSlogwang        for mod in mods:
1552bfe3f2eSlogwang            if mod["Name"] in sysfs_mods:
1562bfe3f2eSlogwang                mod["Found"] = True
1572bfe3f2eSlogwang    except:
1582bfe3f2eSlogwang        pass
1592bfe3f2eSlogwang
1602bfe3f2eSlogwang    # check if we have at least one loaded module
1612bfe3f2eSlogwang    if True not in [mod["Found"] for mod in mods] and b_flag is not None:
1622bfe3f2eSlogwang        if b_flag in dpdk_drivers:
1632bfe3f2eSlogwang            print("Error - no supported modules(DPDK driver) are loaded")
1642bfe3f2eSlogwang            sys.exit(1)
1652bfe3f2eSlogwang        else:
1662bfe3f2eSlogwang            print("Warning - no supported modules(DPDK driver) are loaded")
1672bfe3f2eSlogwang
1682bfe3f2eSlogwang    # change DPDK driver list to only contain drivers that are loaded
1692bfe3f2eSlogwang    dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
1702bfe3f2eSlogwang
1712bfe3f2eSlogwang
1722bfe3f2eSlogwangdef has_driver(dev_id):
1732bfe3f2eSlogwang    '''return true if a device is assigned to a driver. False otherwise'''
1742bfe3f2eSlogwang    return "Driver_str" in devices[dev_id]
1752bfe3f2eSlogwang
1762bfe3f2eSlogwang
1772bfe3f2eSlogwangdef get_pci_device_details(dev_id, probe_lspci):
1782bfe3f2eSlogwang    '''This function gets additional details for a PCI device'''
1792bfe3f2eSlogwang    device = {}
1802bfe3f2eSlogwang
1812bfe3f2eSlogwang    if probe_lspci:
1822bfe3f2eSlogwang        extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
1832bfe3f2eSlogwang
1842bfe3f2eSlogwang        # parse lspci details
1852bfe3f2eSlogwang        for line in extra_info:
1862bfe3f2eSlogwang            if len(line) == 0:
1872bfe3f2eSlogwang                continue
1882bfe3f2eSlogwang            name, value = line.decode().split("\t", 1)
1892bfe3f2eSlogwang            name = name.strip(":") + "_str"
1902bfe3f2eSlogwang            device[name] = value
1912bfe3f2eSlogwang    # check for a unix interface name
1922bfe3f2eSlogwang    device["Interface"] = ""
1932bfe3f2eSlogwang    for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
1942bfe3f2eSlogwang        if "net" in dirs:
1952bfe3f2eSlogwang            device["Interface"] = \
1962bfe3f2eSlogwang                ",".join(os.listdir(os.path.join(base, "net")))
1972bfe3f2eSlogwang            break
1982bfe3f2eSlogwang    # check if a port is used for ssh connection
1992bfe3f2eSlogwang    device["Ssh_if"] = False
2002bfe3f2eSlogwang    device["Active"] = ""
2012bfe3f2eSlogwang
2022bfe3f2eSlogwang    return device
2032bfe3f2eSlogwang
2042bfe3f2eSlogwangdef clear_data():
2052bfe3f2eSlogwang    '''This function clears any old data'''
2062bfe3f2eSlogwang    devices = {}
2072bfe3f2eSlogwang
2082bfe3f2eSlogwangdef get_device_details(devices_type):
2092bfe3f2eSlogwang    '''This function populates the "devices" dictionary. The keys used are
2102bfe3f2eSlogwang    the pci addresses (domain:bus:slot.func). The values are themselves
2112bfe3f2eSlogwang    dictionaries - one for each NIC.'''
2122bfe3f2eSlogwang    global devices
2132bfe3f2eSlogwang    global dpdk_drivers
2142bfe3f2eSlogwang
2152bfe3f2eSlogwang    # first loop through and read details for all devices
2162bfe3f2eSlogwang    # request machine readable format, with numeric IDs and String
2172bfe3f2eSlogwang    dev = {}
2182bfe3f2eSlogwang    dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
2192bfe3f2eSlogwang    for dev_line in dev_lines:
2202bfe3f2eSlogwang        if len(dev_line) == 0:
2212bfe3f2eSlogwang            if device_type_match(dev, devices_type):
2222bfe3f2eSlogwang                # Replace "Driver" with "Driver_str" to have consistency of
2232bfe3f2eSlogwang                # of dictionary key names
2242bfe3f2eSlogwang                if "Driver" in dev.keys():
2252bfe3f2eSlogwang                    dev["Driver_str"] = dev.pop("Driver")
2262bfe3f2eSlogwang                if "Module" in dev.keys():
2272bfe3f2eSlogwang                    dev["Module_str"] = dev.pop("Module")
2282bfe3f2eSlogwang                # use dict to make copy of dev
2292bfe3f2eSlogwang                devices[dev["Slot"]] = dict(dev)
2302bfe3f2eSlogwang            # Clear previous device's data
2312bfe3f2eSlogwang            dev = {}
2322bfe3f2eSlogwang        else:
2332bfe3f2eSlogwang            name, value = dev_line.decode().split("\t", 1)
2342bfe3f2eSlogwang            value_list = value.rsplit(' ', 1)
2352bfe3f2eSlogwang            if len(value_list) > 1:
2362bfe3f2eSlogwang                # String stored in <name>_str
2372bfe3f2eSlogwang                dev[name.rstrip(":") + '_str'] = value_list[0]
2382bfe3f2eSlogwang            # Numeric IDs
2392bfe3f2eSlogwang            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
2402bfe3f2eSlogwang                .rstrip("]").lstrip("[")
2412bfe3f2eSlogwang
2422bfe3f2eSlogwang    if devices_type == network_devices:
2432bfe3f2eSlogwang        # check what is the interface if any for an ssh connection if
2442bfe3f2eSlogwang        # any to this host, so we can mark it later.
2452bfe3f2eSlogwang        ssh_if = []
2462bfe3f2eSlogwang        route = check_output(["ip", "-o", "route"])
2472bfe3f2eSlogwang        # filter out all lines for 169.254 routes
2482bfe3f2eSlogwang        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
2492bfe3f2eSlogwang                             route.decode().splitlines()))
2502bfe3f2eSlogwang        rt_info = route.split()
2512bfe3f2eSlogwang        for i in range(len(rt_info) - 1):
2522bfe3f2eSlogwang            if rt_info[i] == "dev":
2532bfe3f2eSlogwang                ssh_if.append(rt_info[i+1])
2542bfe3f2eSlogwang
2552bfe3f2eSlogwang    # based on the basic info, get extended text details
2562bfe3f2eSlogwang    for d in devices.keys():
2572bfe3f2eSlogwang        if not device_type_match(devices[d], devices_type):
2582bfe3f2eSlogwang            continue
2592bfe3f2eSlogwang
2602bfe3f2eSlogwang        # get additional info and add it to existing data
2612bfe3f2eSlogwang        devices[d] = devices[d].copy()
2622bfe3f2eSlogwang        # No need to probe lspci
2632bfe3f2eSlogwang        devices[d].update(get_pci_device_details(d, False).items())
2642bfe3f2eSlogwang
2652bfe3f2eSlogwang        if devices_type == network_devices:
2662bfe3f2eSlogwang            for _if in ssh_if:
2672bfe3f2eSlogwang                if _if in devices[d]["Interface"].split(","):
2682bfe3f2eSlogwang                    devices[d]["Ssh_if"] = True
2692bfe3f2eSlogwang                    devices[d]["Active"] = "*Active*"
2702bfe3f2eSlogwang                    break
2712bfe3f2eSlogwang
2722bfe3f2eSlogwang        # add igb_uio to list of supporting modules if needed
2732bfe3f2eSlogwang        if "Module_str" in devices[d]:
2742bfe3f2eSlogwang            for driver in dpdk_drivers:
2752bfe3f2eSlogwang                if driver not in devices[d]["Module_str"]:
2762bfe3f2eSlogwang                    devices[d]["Module_str"] = \
2772bfe3f2eSlogwang                        devices[d]["Module_str"] + ",%s" % driver
2782bfe3f2eSlogwang        else:
2792bfe3f2eSlogwang            devices[d]["Module_str"] = ",".join(dpdk_drivers)
2802bfe3f2eSlogwang
2812bfe3f2eSlogwang        # make sure the driver and module strings do not have any duplicates
2822bfe3f2eSlogwang        if has_driver(d):
2832bfe3f2eSlogwang            modules = devices[d]["Module_str"].split(",")
2842bfe3f2eSlogwang            if devices[d]["Driver_str"] in modules:
2852bfe3f2eSlogwang                modules.remove(devices[d]["Driver_str"])
2862bfe3f2eSlogwang                devices[d]["Module_str"] = ",".join(modules)
2872bfe3f2eSlogwang
2882bfe3f2eSlogwang
2892bfe3f2eSlogwangdef device_type_match(dev, devices_type):
2902bfe3f2eSlogwang    for i in range(len(devices_type)):
2912bfe3f2eSlogwang        param_count = len(
2922bfe3f2eSlogwang            [x for x in devices_type[i].values() if x is not None])
2932bfe3f2eSlogwang        match_count = 0
2942bfe3f2eSlogwang        if dev["Class"][0:2] == devices_type[i]["Class"]:
2952bfe3f2eSlogwang            match_count = match_count + 1
2962bfe3f2eSlogwang            for key in devices_type[i].keys():
2972bfe3f2eSlogwang                if key != 'Class' and devices_type[i][key]:
2982bfe3f2eSlogwang                    value_list = devices_type[i][key].split(',')
2992bfe3f2eSlogwang                    for value in value_list:
3002bfe3f2eSlogwang                        if value.strip(' ') == dev[key]:
3012bfe3f2eSlogwang                            match_count = match_count + 1
3022bfe3f2eSlogwang            # count must be the number of non None parameters to match
3032bfe3f2eSlogwang            if match_count == param_count:
3042bfe3f2eSlogwang                return True
3052bfe3f2eSlogwang    return False
3062bfe3f2eSlogwang
3072bfe3f2eSlogwangdef dev_id_from_dev_name(dev_name):
3082bfe3f2eSlogwang    '''Take a device "name" - a string passed in by user to identify a NIC
3092bfe3f2eSlogwang    device, and determine the device id - i.e. the domain:bus:slot.func - for
3102bfe3f2eSlogwang    it, which can then be used to index into the devices array'''
3112bfe3f2eSlogwang
3122bfe3f2eSlogwang    # check if it's already a suitable index
3132bfe3f2eSlogwang    if dev_name in devices:
3142bfe3f2eSlogwang        return dev_name
3152bfe3f2eSlogwang    # check if it's an index just missing the domain part
3162bfe3f2eSlogwang    elif "0000:" + dev_name in devices:
3172bfe3f2eSlogwang        return "0000:" + dev_name
3182bfe3f2eSlogwang    else:
3192bfe3f2eSlogwang        # check if it's an interface name, e.g. eth1
3202bfe3f2eSlogwang        for d in devices.keys():
3212bfe3f2eSlogwang            if dev_name in devices[d]["Interface"].split(","):
3222bfe3f2eSlogwang                return devices[d]["Slot"]
3232bfe3f2eSlogwang    # if nothing else matches - error
3242bfe3f2eSlogwang    print("Unknown device: %s. "
3252bfe3f2eSlogwang          "Please specify device in \"bus:slot.func\" format" % dev_name)
3262bfe3f2eSlogwang    sys.exit(1)
3272bfe3f2eSlogwang
3282bfe3f2eSlogwang
3292bfe3f2eSlogwangdef unbind_one(dev_id, force):
3302bfe3f2eSlogwang    '''Unbind the device identified by "dev_id" from its current driver'''
3312bfe3f2eSlogwang    dev = devices[dev_id]
3322bfe3f2eSlogwang    if not has_driver(dev_id):
3332bfe3f2eSlogwang        print("%s %s %s is not currently managed by any driver\n" %
3342bfe3f2eSlogwang              (dev["Slot"], dev["Device_str"], dev["Interface"]))
3352bfe3f2eSlogwang        return
3362bfe3f2eSlogwang
3372bfe3f2eSlogwang    # prevent us disconnecting ourselves
3382bfe3f2eSlogwang    if dev["Ssh_if"] and not force:
3392bfe3f2eSlogwang        print("Routing table indicates that interface %s is active. "
3402bfe3f2eSlogwang              "Skipping unbind" % (dev_id))
3412bfe3f2eSlogwang        return
3422bfe3f2eSlogwang
3432bfe3f2eSlogwang    # write to /sys to unbind
3442bfe3f2eSlogwang    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
3452bfe3f2eSlogwang    try:
3462bfe3f2eSlogwang        f = open(filename, "a")
3472bfe3f2eSlogwang    except:
3482bfe3f2eSlogwang        print("Error: unbind failed for %s - Cannot open %s"
3492bfe3f2eSlogwang              % (dev_id, filename))
3502bfe3f2eSlogwang        sys.exit(1)
3512bfe3f2eSlogwang    f.write(dev_id)
3522bfe3f2eSlogwang    f.close()
3532bfe3f2eSlogwang
3542bfe3f2eSlogwang
3552bfe3f2eSlogwangdef bind_one(dev_id, driver, force):
3562bfe3f2eSlogwang    '''Bind the device given by "dev_id" to the driver "driver". If the device
3572bfe3f2eSlogwang    is already bound to a different driver, it will be unbound first'''
3582bfe3f2eSlogwang    dev = devices[dev_id]
3592bfe3f2eSlogwang    saved_driver = None  # used to rollback any unbind in case of failure
3602bfe3f2eSlogwang
3612bfe3f2eSlogwang    # prevent disconnection of our ssh session
3622bfe3f2eSlogwang    if dev["Ssh_if"] and not force:
3632bfe3f2eSlogwang        print("Routing table indicates that interface %s is active. "
3642bfe3f2eSlogwang              "Not modifying" % (dev_id))
3652bfe3f2eSlogwang        return
3662bfe3f2eSlogwang
3672bfe3f2eSlogwang    # unbind any existing drivers we don't want
3682bfe3f2eSlogwang    if has_driver(dev_id):
3692bfe3f2eSlogwang        if dev["Driver_str"] == driver:
3702bfe3f2eSlogwang            print("%s already bound to driver %s, skipping\n"
3712bfe3f2eSlogwang                  % (dev_id, driver))
3722bfe3f2eSlogwang            return
3732bfe3f2eSlogwang        else:
3742bfe3f2eSlogwang            saved_driver = dev["Driver_str"]
3752bfe3f2eSlogwang            unbind_one(dev_id, force)
3762bfe3f2eSlogwang            dev["Driver_str"] = ""  # clear driver string
3772bfe3f2eSlogwang
3782bfe3f2eSlogwang    # For kernels >= 3.15 driver_override can be used to specify the driver
3792bfe3f2eSlogwang    # for a device rather than relying on the driver to provide a positive
3802bfe3f2eSlogwang    # match of the device.  The existing process of looking up
3812bfe3f2eSlogwang    # the vendor and device ID, adding them to the driver new_id,
3822bfe3f2eSlogwang    # will erroneously bind other devices too which has the additional burden
3832bfe3f2eSlogwang    # of unbinding those devices
3842bfe3f2eSlogwang    if driver in dpdk_drivers:
3852bfe3f2eSlogwang        filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
3862bfe3f2eSlogwang        if os.path.exists(filename):
3872bfe3f2eSlogwang            try:
3882bfe3f2eSlogwang                f = open(filename, "w")
3892bfe3f2eSlogwang            except:
3902bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot open %s"
3912bfe3f2eSlogwang                      % (dev_id, filename))
3922bfe3f2eSlogwang                return
3932bfe3f2eSlogwang            try:
3942bfe3f2eSlogwang                f.write("%s" % driver)
3952bfe3f2eSlogwang                f.close()
3962bfe3f2eSlogwang            except:
3972bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot write driver %s to "
3982bfe3f2eSlogwang                      "PCI ID " % (dev_id, driver))
3992bfe3f2eSlogwang                return
4002bfe3f2eSlogwang        # For kernels < 3.15 use new_id to add PCI id's to the driver
4012bfe3f2eSlogwang        else:
4022bfe3f2eSlogwang            filename = "/sys/bus/pci/drivers/%s/new_id" % driver
4032bfe3f2eSlogwang            try:
4042bfe3f2eSlogwang                f = open(filename, "w")
4052bfe3f2eSlogwang            except:
4062bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot open %s"
4072bfe3f2eSlogwang                      % (dev_id, filename))
4082bfe3f2eSlogwang                return
4092bfe3f2eSlogwang            try:
4102bfe3f2eSlogwang                # Convert Device and Vendor Id to int to write to new_id
4112bfe3f2eSlogwang                f.write("%04x %04x" % (int(dev["Vendor"],16),
4122bfe3f2eSlogwang                        int(dev["Device"], 16)))
4132bfe3f2eSlogwang                f.close()
4142bfe3f2eSlogwang            except:
4152bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot write new PCI ID to "
4162bfe3f2eSlogwang                      "driver %s" % (dev_id, driver))
4172bfe3f2eSlogwang                return
4182bfe3f2eSlogwang
4192bfe3f2eSlogwang    # do the bind by writing to /sys
4202bfe3f2eSlogwang    filename = "/sys/bus/pci/drivers/%s/bind" % driver
4212bfe3f2eSlogwang    try:
4222bfe3f2eSlogwang        f = open(filename, "a")
4232bfe3f2eSlogwang    except:
4242bfe3f2eSlogwang        print("Error: bind failed for %s - Cannot open %s"
4252bfe3f2eSlogwang              % (dev_id, filename))
4262bfe3f2eSlogwang        if saved_driver is not None:  # restore any previous driver
4272bfe3f2eSlogwang            bind_one(dev_id, saved_driver, force)
4282bfe3f2eSlogwang        return
4292bfe3f2eSlogwang    try:
4302bfe3f2eSlogwang        f.write(dev_id)
4312bfe3f2eSlogwang        f.close()
4322bfe3f2eSlogwang    except:
4332bfe3f2eSlogwang        # for some reason, closing dev_id after adding a new PCI ID to new_id
4342bfe3f2eSlogwang        # results in IOError. however, if the device was successfully bound,
4352bfe3f2eSlogwang        # we don't care for any errors and can safely ignore IOError
4362bfe3f2eSlogwang        tmp = get_pci_device_details(dev_id, True)
4372bfe3f2eSlogwang        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
4382bfe3f2eSlogwang            return
4392bfe3f2eSlogwang        print("Error: bind failed for %s - Cannot bind to driver %s"
4402bfe3f2eSlogwang              % (dev_id, driver))
4412bfe3f2eSlogwang        if saved_driver is not None:  # restore any previous driver
4422bfe3f2eSlogwang            bind_one(dev_id, saved_driver, force)
4432bfe3f2eSlogwang        return
4442bfe3f2eSlogwang
4452bfe3f2eSlogwang    # For kernels > 3.15 driver_override is used to bind a device to a driver.
4462bfe3f2eSlogwang    # Before unbinding it, overwrite driver_override with empty string so that
4472bfe3f2eSlogwang    # the device can be bound to any other driver
4482bfe3f2eSlogwang    filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
4492bfe3f2eSlogwang    if os.path.exists(filename):
4502bfe3f2eSlogwang        try:
4512bfe3f2eSlogwang            f = open(filename, "w")
4522bfe3f2eSlogwang        except:
4532bfe3f2eSlogwang            print("Error: unbind failed for %s - Cannot open %s"
4542bfe3f2eSlogwang                  % (dev_id, filename))
4552bfe3f2eSlogwang            sys.exit(1)
4562bfe3f2eSlogwang        try:
4572bfe3f2eSlogwang            f.write("\00")
4582bfe3f2eSlogwang            f.close()
4592bfe3f2eSlogwang        except:
4602bfe3f2eSlogwang            print("Error: unbind failed for %s - Cannot open %s"
4612bfe3f2eSlogwang                  % (dev_id, filename))
4622bfe3f2eSlogwang            sys.exit(1)
4632bfe3f2eSlogwang
4642bfe3f2eSlogwang
4652bfe3f2eSlogwangdef unbind_all(dev_list, force=False):
4662bfe3f2eSlogwang    """Unbind method, takes a list of device locations"""
4672bfe3f2eSlogwang
4682bfe3f2eSlogwang    if dev_list[0] == "dpdk":
4692bfe3f2eSlogwang        for d in devices.keys():
4702bfe3f2eSlogwang            if "Driver_str" in devices[d]:
4712bfe3f2eSlogwang                if devices[d]["Driver_str"] in dpdk_drivers:
4722bfe3f2eSlogwang                    unbind_one(devices[d]["Slot"], force)
4732bfe3f2eSlogwang        return
4742bfe3f2eSlogwang
4752bfe3f2eSlogwang    dev_list = map(dev_id_from_dev_name, dev_list)
4762bfe3f2eSlogwang    for d in dev_list:
4772bfe3f2eSlogwang        unbind_one(d, force)
4782bfe3f2eSlogwang
4792bfe3f2eSlogwang
4802bfe3f2eSlogwangdef bind_all(dev_list, driver, force=False):
4812bfe3f2eSlogwang    """Bind method, takes a list of device locations"""
4822bfe3f2eSlogwang    global devices
4832bfe3f2eSlogwang
4842bfe3f2eSlogwang    dev_list = map(dev_id_from_dev_name, dev_list)
4852bfe3f2eSlogwang
4862bfe3f2eSlogwang    for d in dev_list:
4872bfe3f2eSlogwang        bind_one(d, driver, force)
4882bfe3f2eSlogwang
4892bfe3f2eSlogwang    # For kernels < 3.15 when binding devices to a generic driver
4902bfe3f2eSlogwang    # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
4912bfe3f2eSlogwang    # that are not bound to any other driver could be bound even if no one has
4922bfe3f2eSlogwang    # asked them to. hence, we check the list of drivers again, and see if
4932bfe3f2eSlogwang    # some of the previously-unbound devices were erroneously bound.
4942bfe3f2eSlogwang    if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
4952bfe3f2eSlogwang        for d in devices.keys():
4962bfe3f2eSlogwang            # skip devices that were already bound or that we know should be bound
4972bfe3f2eSlogwang            if "Driver_str" in devices[d] or d in dev_list:
4982bfe3f2eSlogwang                continue
4992bfe3f2eSlogwang
5002bfe3f2eSlogwang            # update information about this device
5012bfe3f2eSlogwang            devices[d] = dict(devices[d].items() +
5022bfe3f2eSlogwang                              get_pci_device_details(d, True).items())
5032bfe3f2eSlogwang
5042bfe3f2eSlogwang            # check if updated information indicates that the device was bound
5052bfe3f2eSlogwang            if "Driver_str" in devices[d]:
5062bfe3f2eSlogwang                unbind_one(d, force)
5072bfe3f2eSlogwang
5082bfe3f2eSlogwang
5092bfe3f2eSlogwangdef display_devices(title, dev_list, extra_params=None):
5102bfe3f2eSlogwang    '''Displays to the user the details of a list of devices given in
5112bfe3f2eSlogwang    "dev_list". The "extra_params" parameter, if given, should contain a string
5122bfe3f2eSlogwang     with %()s fields in it for replacement by the named fields in each
5132bfe3f2eSlogwang     device's dictionary.'''
5142bfe3f2eSlogwang    strings = []  # this holds the strings to print. We sort before printing
5152bfe3f2eSlogwang    print("\n%s" % title)
5162bfe3f2eSlogwang    print("="*len(title))
5172bfe3f2eSlogwang    if len(dev_list) == 0:
5182bfe3f2eSlogwang        strings.append("<none>")
5192bfe3f2eSlogwang    else:
5202bfe3f2eSlogwang        for dev in dev_list:
5212bfe3f2eSlogwang            if extra_params is not None:
5222bfe3f2eSlogwang                strings.append("%s '%s %s' %s" % (dev["Slot"],
5232bfe3f2eSlogwang                                               dev["Device_str"],
5242bfe3f2eSlogwang                                               dev["Device"],
5252bfe3f2eSlogwang                                               extra_params % dev))
5262bfe3f2eSlogwang            else:
5272bfe3f2eSlogwang                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
5282bfe3f2eSlogwang    # sort before printing, so that the entries appear in PCI order
5292bfe3f2eSlogwang    strings.sort()
5302bfe3f2eSlogwang    print("\n".join(strings))  # print one per line
5312bfe3f2eSlogwang
5322bfe3f2eSlogwangdef show_device_status(devices_type, device_name):
5332bfe3f2eSlogwang    global dpdk_drivers
5342bfe3f2eSlogwang    kernel_drv = []
5352bfe3f2eSlogwang    dpdk_drv = []
5362bfe3f2eSlogwang    no_drv = []
5372bfe3f2eSlogwang
5382bfe3f2eSlogwang    # split our list of network devices into the three categories above
5392bfe3f2eSlogwang    for d in devices.keys():
5402bfe3f2eSlogwang        if device_type_match(devices[d], devices_type):
5412bfe3f2eSlogwang            if not has_driver(d):
5422bfe3f2eSlogwang                no_drv.append(devices[d])
5432bfe3f2eSlogwang                continue
5442bfe3f2eSlogwang            if devices[d]["Driver_str"] in dpdk_drivers:
5452bfe3f2eSlogwang                dpdk_drv.append(devices[d])
5462bfe3f2eSlogwang            else:
5472bfe3f2eSlogwang                kernel_drv.append(devices[d])
5482bfe3f2eSlogwang
549*d30ea906Sjfb8856606    n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
550*d30ea906Sjfb8856606
551*d30ea906Sjfb8856606    # don't bother displaying anything if there are no devices
552*d30ea906Sjfb8856606    if n_devs == 0:
553*d30ea906Sjfb8856606        msg = "No '%s' devices detected" % device_name
554*d30ea906Sjfb8856606        print("")
555*d30ea906Sjfb8856606        print(msg)
556*d30ea906Sjfb8856606        print("".join('=' * len(msg)))
557*d30ea906Sjfb8856606        return
558*d30ea906Sjfb8856606
5592bfe3f2eSlogwang    # print each category separately, so we can clearly see what's used by DPDK
560*d30ea906Sjfb8856606    if len(dpdk_drv) != 0:
5612bfe3f2eSlogwang        display_devices("%s devices using DPDK-compatible driver" % device_name,
5622bfe3f2eSlogwang                        dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
563*d30ea906Sjfb8856606    if len(kernel_drv) != 0:
5642bfe3f2eSlogwang        display_devices("%s devices using kernel driver" % device_name, kernel_drv,
5652bfe3f2eSlogwang                        "if=%(Interface)s drv=%(Driver_str)s "
5662bfe3f2eSlogwang                        "unused=%(Module_str)s %(Active)s")
567*d30ea906Sjfb8856606    if len(no_drv) != 0:
5682bfe3f2eSlogwang        display_devices("Other %s devices" % device_name, no_drv,
5692bfe3f2eSlogwang                        "unused=%(Module_str)s")
5702bfe3f2eSlogwang
5712bfe3f2eSlogwangdef show_status():
5722bfe3f2eSlogwang    '''Function called when the script is passed the "--status" option.
5732bfe3f2eSlogwang    Displays to the user what devices are bound to the igb_uio driver, the
5742bfe3f2eSlogwang    kernel driver or to no driver'''
5752bfe3f2eSlogwang
5762bfe3f2eSlogwang    if status_dev == "net" or status_dev == "all":
5772bfe3f2eSlogwang        show_device_status(network_devices, "Network")
5782bfe3f2eSlogwang
5792bfe3f2eSlogwang    if status_dev == "crypto" or status_dev == "all":
5802bfe3f2eSlogwang        show_device_status(crypto_devices, "Crypto")
5812bfe3f2eSlogwang
5822bfe3f2eSlogwang    if status_dev == "event" or status_dev == "all":
5832bfe3f2eSlogwang        show_device_status(eventdev_devices, "Eventdev")
5842bfe3f2eSlogwang
5852bfe3f2eSlogwang    if status_dev == "mempool" or status_dev == "all":
5862bfe3f2eSlogwang        show_device_status(mempool_devices, "Mempool")
5872bfe3f2eSlogwang
588*d30ea906Sjfb8856606    if status_dev == "compress" or status_dev == "all":
589*d30ea906Sjfb8856606        show_device_status(compress_devices , "Compress")
590*d30ea906Sjfb8856606
591*d30ea906Sjfb8856606
5922bfe3f2eSlogwangdef parse_args():
5932bfe3f2eSlogwang    '''Parses the command-line arguments given by the user and takes the
5942bfe3f2eSlogwang    appropriate action for each'''
5952bfe3f2eSlogwang    global b_flag
5962bfe3f2eSlogwang    global status_flag
5972bfe3f2eSlogwang    global status_dev
5982bfe3f2eSlogwang    global force_flag
5992bfe3f2eSlogwang    global args
6002bfe3f2eSlogwang    if len(sys.argv) <= 1:
6012bfe3f2eSlogwang        usage()
6022bfe3f2eSlogwang        sys.exit(0)
6032bfe3f2eSlogwang
6042bfe3f2eSlogwang    try:
6052bfe3f2eSlogwang        opts, args = getopt.getopt(sys.argv[1:], "b:us",
6062bfe3f2eSlogwang                                   ["help", "usage", "status", "status-dev=",
6072bfe3f2eSlogwang                                    "force", "bind=", "unbind", ])
6082bfe3f2eSlogwang    except getopt.GetoptError as error:
6092bfe3f2eSlogwang        print(str(error))
6102bfe3f2eSlogwang        print("Run '%s --usage' for further information" % sys.argv[0])
6112bfe3f2eSlogwang        sys.exit(1)
6122bfe3f2eSlogwang
6132bfe3f2eSlogwang    for opt, arg in opts:
6142bfe3f2eSlogwang        if opt == "--help" or opt == "--usage":
6152bfe3f2eSlogwang            usage()
6162bfe3f2eSlogwang            sys.exit(0)
6172bfe3f2eSlogwang        if opt == "--status-dev":
6182bfe3f2eSlogwang            status_flag = True
6192bfe3f2eSlogwang            status_dev = arg
6202bfe3f2eSlogwang        if opt == "--status" or opt == "-s":
6212bfe3f2eSlogwang            status_flag = True
6222bfe3f2eSlogwang            status_dev = "all"
6232bfe3f2eSlogwang        if opt == "--force":
6242bfe3f2eSlogwang            force_flag = True
6252bfe3f2eSlogwang        if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
6262bfe3f2eSlogwang            if b_flag is not None:
6272bfe3f2eSlogwang                print("Error - Only one bind or unbind may be specified\n")
6282bfe3f2eSlogwang                sys.exit(1)
6292bfe3f2eSlogwang            if opt == "-u" or opt == "--unbind":
6302bfe3f2eSlogwang                b_flag = "none"
6312bfe3f2eSlogwang            else:
6322bfe3f2eSlogwang                b_flag = arg
6332bfe3f2eSlogwang
6342bfe3f2eSlogwang
6352bfe3f2eSlogwangdef do_arg_actions():
6362bfe3f2eSlogwang    '''do the actual action requested by the user'''
6372bfe3f2eSlogwang    global b_flag
6382bfe3f2eSlogwang    global status_flag
6392bfe3f2eSlogwang    global force_flag
6402bfe3f2eSlogwang    global args
6412bfe3f2eSlogwang
6422bfe3f2eSlogwang    if b_flag is None and not status_flag:
6432bfe3f2eSlogwang        print("Error: No action specified for devices."
6442bfe3f2eSlogwang              "Please give a -b or -u option")
6452bfe3f2eSlogwang        print("Run '%s --usage' for further information" % sys.argv[0])
6462bfe3f2eSlogwang        sys.exit(1)
6472bfe3f2eSlogwang
6482bfe3f2eSlogwang    if b_flag is not None and len(args) == 0:
6492bfe3f2eSlogwang        print("Error: No devices specified.")
6502bfe3f2eSlogwang        print("Run '%s --usage' for further information" % sys.argv[0])
6512bfe3f2eSlogwang        sys.exit(1)
6522bfe3f2eSlogwang
6532bfe3f2eSlogwang    if b_flag == "none" or b_flag == "None":
6542bfe3f2eSlogwang        unbind_all(args, force_flag)
6552bfe3f2eSlogwang    elif b_flag is not None:
6562bfe3f2eSlogwang        bind_all(args, b_flag, force_flag)
6572bfe3f2eSlogwang    if status_flag:
6582bfe3f2eSlogwang        if b_flag is not None:
6592bfe3f2eSlogwang            clear_data()
6602bfe3f2eSlogwang            # refresh if we have changed anything
6612bfe3f2eSlogwang            get_device_details(network_devices)
6622bfe3f2eSlogwang            get_device_details(crypto_devices)
6632bfe3f2eSlogwang            get_device_details(eventdev_devices)
6642bfe3f2eSlogwang            get_device_details(mempool_devices)
665*d30ea906Sjfb8856606            get_device_details(compress_devices)
6662bfe3f2eSlogwang        show_status()
6672bfe3f2eSlogwang
6682bfe3f2eSlogwang
6692bfe3f2eSlogwangdef main():
6702bfe3f2eSlogwang    '''program main function'''
671*d30ea906Sjfb8856606    # check if lspci is installed, suppress any output
672*d30ea906Sjfb8856606    with open(os.devnull, 'w') as devnull:
673*d30ea906Sjfb8856606        ret = subprocess.call(['which', 'lspci'],
674*d30ea906Sjfb8856606                              stdout=devnull, stderr=devnull)
675*d30ea906Sjfb8856606        if ret != 0:
676*d30ea906Sjfb8856606            print("'lspci' not found - please install 'pciutils'")
677*d30ea906Sjfb8856606            sys.exit(1)
6782bfe3f2eSlogwang    parse_args()
6792bfe3f2eSlogwang    check_modules()
6802bfe3f2eSlogwang    clear_data()
6812bfe3f2eSlogwang    get_device_details(network_devices)
6822bfe3f2eSlogwang    get_device_details(crypto_devices)
6832bfe3f2eSlogwang    get_device_details(eventdev_devices)
6842bfe3f2eSlogwang    get_device_details(mempool_devices)
685*d30ea906Sjfb8856606    get_device_details(compress_devices)
6862bfe3f2eSlogwang    do_arg_actions()
6872bfe3f2eSlogwang
6882bfe3f2eSlogwangif __name__ == "__main__":
6892bfe3f2eSlogwang    main()
690