xref: /f-stack/dpdk/usertools/dpdk-devbind.py (revision 4b05018f)
12bfe3f2eSlogwang#! /usr/bin/env python
2d30ea906Sjfb8856606# SPDX-License-Identifier: BSD-3-Clause
3d30ea906Sjfb8856606# 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}
25d30ea906Sjfb8856606cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
26d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
27d30ea906Sjfb8856606cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
28d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
29d30ea906Sjfb8856606avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
30d30ea906Sjfb8856606              'SVendor': None, 'SDevice': None}
312bfe3f2eSlogwang
32d30ea906Sjfb8856606network_devices = [network_class, cavium_pkx, avp_vnic]
332bfe3f2eSlogwangcrypto_devices = [encryption_class, intel_processor_class]
34d30ea906Sjfb8856606eventdev_devices = [cavium_sso, cavium_tim]
352bfe3f2eSlogwangmempool_devices = [cavium_fpa]
36d30ea906Sjfb8856606compress_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:
85d30ea906Sjfb8856606        "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'''
206*4b05018fSfengbojiang    global devices
2072bfe3f2eSlogwang    devices = {}
2082bfe3f2eSlogwang
2092bfe3f2eSlogwangdef get_device_details(devices_type):
2102bfe3f2eSlogwang    '''This function populates the "devices" dictionary. The keys used are
2112bfe3f2eSlogwang    the pci addresses (domain:bus:slot.func). The values are themselves
2122bfe3f2eSlogwang    dictionaries - one for each NIC.'''
2132bfe3f2eSlogwang    global devices
2142bfe3f2eSlogwang    global dpdk_drivers
2152bfe3f2eSlogwang
2162bfe3f2eSlogwang    # first loop through and read details for all devices
2172bfe3f2eSlogwang    # request machine readable format, with numeric IDs and String
2182bfe3f2eSlogwang    dev = {}
2192bfe3f2eSlogwang    dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
2202bfe3f2eSlogwang    for dev_line in dev_lines:
2212bfe3f2eSlogwang        if len(dev_line) == 0:
2222bfe3f2eSlogwang            if device_type_match(dev, devices_type):
2232bfe3f2eSlogwang                # Replace "Driver" with "Driver_str" to have consistency of
2242bfe3f2eSlogwang                # of dictionary key names
2252bfe3f2eSlogwang                if "Driver" in dev.keys():
2262bfe3f2eSlogwang                    dev["Driver_str"] = dev.pop("Driver")
2272bfe3f2eSlogwang                if "Module" in dev.keys():
2282bfe3f2eSlogwang                    dev["Module_str"] = dev.pop("Module")
2292bfe3f2eSlogwang                # use dict to make copy of dev
2302bfe3f2eSlogwang                devices[dev["Slot"]] = dict(dev)
2312bfe3f2eSlogwang            # Clear previous device's data
2322bfe3f2eSlogwang            dev = {}
2332bfe3f2eSlogwang        else:
2342bfe3f2eSlogwang            name, value = dev_line.decode().split("\t", 1)
2352bfe3f2eSlogwang            value_list = value.rsplit(' ', 1)
2362bfe3f2eSlogwang            if len(value_list) > 1:
2372bfe3f2eSlogwang                # String stored in <name>_str
2382bfe3f2eSlogwang                dev[name.rstrip(":") + '_str'] = value_list[0]
2392bfe3f2eSlogwang            # Numeric IDs
2402bfe3f2eSlogwang            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
2412bfe3f2eSlogwang                .rstrip("]").lstrip("[")
2422bfe3f2eSlogwang
2432bfe3f2eSlogwang    if devices_type == network_devices:
2442bfe3f2eSlogwang        # check what is the interface if any for an ssh connection if
2452bfe3f2eSlogwang        # any to this host, so we can mark it later.
2462bfe3f2eSlogwang        ssh_if = []
2472bfe3f2eSlogwang        route = check_output(["ip", "-o", "route"])
2482bfe3f2eSlogwang        # filter out all lines for 169.254 routes
2492bfe3f2eSlogwang        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
2502bfe3f2eSlogwang                             route.decode().splitlines()))
2512bfe3f2eSlogwang        rt_info = route.split()
2522bfe3f2eSlogwang        for i in range(len(rt_info) - 1):
2532bfe3f2eSlogwang            if rt_info[i] == "dev":
2542bfe3f2eSlogwang                ssh_if.append(rt_info[i+1])
2552bfe3f2eSlogwang
2562bfe3f2eSlogwang    # based on the basic info, get extended text details
2572bfe3f2eSlogwang    for d in devices.keys():
2582bfe3f2eSlogwang        if not device_type_match(devices[d], devices_type):
2592bfe3f2eSlogwang            continue
2602bfe3f2eSlogwang
2612bfe3f2eSlogwang        # get additional info and add it to existing data
2622bfe3f2eSlogwang        devices[d] = devices[d].copy()
2632bfe3f2eSlogwang        # No need to probe lspci
2642bfe3f2eSlogwang        devices[d].update(get_pci_device_details(d, False).items())
2652bfe3f2eSlogwang
2662bfe3f2eSlogwang        if devices_type == network_devices:
2672bfe3f2eSlogwang            for _if in ssh_if:
2682bfe3f2eSlogwang                if _if in devices[d]["Interface"].split(","):
2692bfe3f2eSlogwang                    devices[d]["Ssh_if"] = True
2702bfe3f2eSlogwang                    devices[d]["Active"] = "*Active*"
2712bfe3f2eSlogwang                    break
2722bfe3f2eSlogwang
2732bfe3f2eSlogwang        # add igb_uio to list of supporting modules if needed
2742bfe3f2eSlogwang        if "Module_str" in devices[d]:
2752bfe3f2eSlogwang            for driver in dpdk_drivers:
2762bfe3f2eSlogwang                if driver not in devices[d]["Module_str"]:
2772bfe3f2eSlogwang                    devices[d]["Module_str"] = \
2782bfe3f2eSlogwang                        devices[d]["Module_str"] + ",%s" % driver
2792bfe3f2eSlogwang        else:
2802bfe3f2eSlogwang            devices[d]["Module_str"] = ",".join(dpdk_drivers)
2812bfe3f2eSlogwang
2822bfe3f2eSlogwang        # make sure the driver and module strings do not have any duplicates
2832bfe3f2eSlogwang        if has_driver(d):
2842bfe3f2eSlogwang            modules = devices[d]["Module_str"].split(",")
2852bfe3f2eSlogwang            if devices[d]["Driver_str"] in modules:
2862bfe3f2eSlogwang                modules.remove(devices[d]["Driver_str"])
2872bfe3f2eSlogwang                devices[d]["Module_str"] = ",".join(modules)
2882bfe3f2eSlogwang
2892bfe3f2eSlogwang
2902bfe3f2eSlogwangdef device_type_match(dev, devices_type):
2912bfe3f2eSlogwang    for i in range(len(devices_type)):
2922bfe3f2eSlogwang        param_count = len(
2932bfe3f2eSlogwang            [x for x in devices_type[i].values() if x is not None])
2942bfe3f2eSlogwang        match_count = 0
2952bfe3f2eSlogwang        if dev["Class"][0:2] == devices_type[i]["Class"]:
2962bfe3f2eSlogwang            match_count = match_count + 1
2972bfe3f2eSlogwang            for key in devices_type[i].keys():
2982bfe3f2eSlogwang                if key != 'Class' and devices_type[i][key]:
2992bfe3f2eSlogwang                    value_list = devices_type[i][key].split(',')
3002bfe3f2eSlogwang                    for value in value_list:
3012bfe3f2eSlogwang                        if value.strip(' ') == dev[key]:
3022bfe3f2eSlogwang                            match_count = match_count + 1
3032bfe3f2eSlogwang            # count must be the number of non None parameters to match
3042bfe3f2eSlogwang            if match_count == param_count:
3052bfe3f2eSlogwang                return True
3062bfe3f2eSlogwang    return False
3072bfe3f2eSlogwang
3082bfe3f2eSlogwangdef dev_id_from_dev_name(dev_name):
3092bfe3f2eSlogwang    '''Take a device "name" - a string passed in by user to identify a NIC
3102bfe3f2eSlogwang    device, and determine the device id - i.e. the domain:bus:slot.func - for
3112bfe3f2eSlogwang    it, which can then be used to index into the devices array'''
3122bfe3f2eSlogwang
3132bfe3f2eSlogwang    # check if it's already a suitable index
3142bfe3f2eSlogwang    if dev_name in devices:
3152bfe3f2eSlogwang        return dev_name
3162bfe3f2eSlogwang    # check if it's an index just missing the domain part
3172bfe3f2eSlogwang    elif "0000:" + dev_name in devices:
3182bfe3f2eSlogwang        return "0000:" + dev_name
3192bfe3f2eSlogwang    else:
3202bfe3f2eSlogwang        # check if it's an interface name, e.g. eth1
3212bfe3f2eSlogwang        for d in devices.keys():
3222bfe3f2eSlogwang            if dev_name in devices[d]["Interface"].split(","):
3232bfe3f2eSlogwang                return devices[d]["Slot"]
3242bfe3f2eSlogwang    # if nothing else matches - error
3252bfe3f2eSlogwang    print("Unknown device: %s. "
3262bfe3f2eSlogwang          "Please specify device in \"bus:slot.func\" format" % dev_name)
3272bfe3f2eSlogwang    sys.exit(1)
3282bfe3f2eSlogwang
3292bfe3f2eSlogwang
3302bfe3f2eSlogwangdef unbind_one(dev_id, force):
3312bfe3f2eSlogwang    '''Unbind the device identified by "dev_id" from its current driver'''
3322bfe3f2eSlogwang    dev = devices[dev_id]
3332bfe3f2eSlogwang    if not has_driver(dev_id):
3342bfe3f2eSlogwang        print("%s %s %s is not currently managed by any driver\n" %
3352bfe3f2eSlogwang              (dev["Slot"], dev["Device_str"], dev["Interface"]))
3362bfe3f2eSlogwang        return
3372bfe3f2eSlogwang
3382bfe3f2eSlogwang    # prevent us disconnecting ourselves
3392bfe3f2eSlogwang    if dev["Ssh_if"] and not force:
3402bfe3f2eSlogwang        print("Routing table indicates that interface %s is active. "
3412bfe3f2eSlogwang              "Skipping unbind" % (dev_id))
3422bfe3f2eSlogwang        return
3432bfe3f2eSlogwang
3442bfe3f2eSlogwang    # write to /sys to unbind
3452bfe3f2eSlogwang    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
3462bfe3f2eSlogwang    try:
3472bfe3f2eSlogwang        f = open(filename, "a")
3482bfe3f2eSlogwang    except:
3492bfe3f2eSlogwang        print("Error: unbind failed for %s - Cannot open %s"
3502bfe3f2eSlogwang              % (dev_id, filename))
3512bfe3f2eSlogwang        sys.exit(1)
3522bfe3f2eSlogwang    f.write(dev_id)
3532bfe3f2eSlogwang    f.close()
3542bfe3f2eSlogwang
3552bfe3f2eSlogwang
3562bfe3f2eSlogwangdef bind_one(dev_id, driver, force):
3572bfe3f2eSlogwang    '''Bind the device given by "dev_id" to the driver "driver". If the device
3582bfe3f2eSlogwang    is already bound to a different driver, it will be unbound first'''
3592bfe3f2eSlogwang    dev = devices[dev_id]
3602bfe3f2eSlogwang    saved_driver = None  # used to rollback any unbind in case of failure
3612bfe3f2eSlogwang
3622bfe3f2eSlogwang    # prevent disconnection of our ssh session
3632bfe3f2eSlogwang    if dev["Ssh_if"] and not force:
3642bfe3f2eSlogwang        print("Routing table indicates that interface %s is active. "
3652bfe3f2eSlogwang              "Not modifying" % (dev_id))
3662bfe3f2eSlogwang        return
3672bfe3f2eSlogwang
3682bfe3f2eSlogwang    # unbind any existing drivers we don't want
3692bfe3f2eSlogwang    if has_driver(dev_id):
3702bfe3f2eSlogwang        if dev["Driver_str"] == driver:
3712bfe3f2eSlogwang            print("%s already bound to driver %s, skipping\n"
3722bfe3f2eSlogwang                  % (dev_id, driver))
3732bfe3f2eSlogwang            return
3742bfe3f2eSlogwang        else:
3752bfe3f2eSlogwang            saved_driver = dev["Driver_str"]
3762bfe3f2eSlogwang            unbind_one(dev_id, force)
3772bfe3f2eSlogwang            dev["Driver_str"] = ""  # clear driver string
3782bfe3f2eSlogwang
3792bfe3f2eSlogwang    # For kernels >= 3.15 driver_override can be used to specify the driver
3802bfe3f2eSlogwang    # for a device rather than relying on the driver to provide a positive
3812bfe3f2eSlogwang    # match of the device.  The existing process of looking up
3822bfe3f2eSlogwang    # the vendor and device ID, adding them to the driver new_id,
3832bfe3f2eSlogwang    # will erroneously bind other devices too which has the additional burden
3842bfe3f2eSlogwang    # of unbinding those devices
3852bfe3f2eSlogwang    if driver in dpdk_drivers:
3862bfe3f2eSlogwang        filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
3872bfe3f2eSlogwang        if os.path.exists(filename):
3882bfe3f2eSlogwang            try:
3892bfe3f2eSlogwang                f = open(filename, "w")
3902bfe3f2eSlogwang            except:
3912bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot open %s"
3922bfe3f2eSlogwang                      % (dev_id, filename))
3932bfe3f2eSlogwang                return
3942bfe3f2eSlogwang            try:
3952bfe3f2eSlogwang                f.write("%s" % driver)
3962bfe3f2eSlogwang                f.close()
3972bfe3f2eSlogwang            except:
3982bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot write driver %s to "
3992bfe3f2eSlogwang                      "PCI ID " % (dev_id, driver))
4002bfe3f2eSlogwang                return
4012bfe3f2eSlogwang        # For kernels < 3.15 use new_id to add PCI id's to the driver
4022bfe3f2eSlogwang        else:
4032bfe3f2eSlogwang            filename = "/sys/bus/pci/drivers/%s/new_id" % driver
4042bfe3f2eSlogwang            try:
4052bfe3f2eSlogwang                f = open(filename, "w")
4062bfe3f2eSlogwang            except:
4072bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot open %s"
4082bfe3f2eSlogwang                      % (dev_id, filename))
4092bfe3f2eSlogwang                return
4102bfe3f2eSlogwang            try:
4112bfe3f2eSlogwang                # Convert Device and Vendor Id to int to write to new_id
4122bfe3f2eSlogwang                f.write("%04x %04x" % (int(dev["Vendor"],16),
4132bfe3f2eSlogwang                        int(dev["Device"], 16)))
4142bfe3f2eSlogwang                f.close()
4152bfe3f2eSlogwang            except:
4162bfe3f2eSlogwang                print("Error: bind failed for %s - Cannot write new PCI ID to "
4172bfe3f2eSlogwang                      "driver %s" % (dev_id, driver))
4182bfe3f2eSlogwang                return
4192bfe3f2eSlogwang
4202bfe3f2eSlogwang    # do the bind by writing to /sys
4212bfe3f2eSlogwang    filename = "/sys/bus/pci/drivers/%s/bind" % driver
4222bfe3f2eSlogwang    try:
4232bfe3f2eSlogwang        f = open(filename, "a")
4242bfe3f2eSlogwang    except:
4252bfe3f2eSlogwang        print("Error: bind failed for %s - Cannot open %s"
4262bfe3f2eSlogwang              % (dev_id, filename))
4272bfe3f2eSlogwang        if saved_driver is not None:  # restore any previous driver
4282bfe3f2eSlogwang            bind_one(dev_id, saved_driver, force)
4292bfe3f2eSlogwang        return
4302bfe3f2eSlogwang    try:
4312bfe3f2eSlogwang        f.write(dev_id)
4322bfe3f2eSlogwang        f.close()
4332bfe3f2eSlogwang    except:
4342bfe3f2eSlogwang        # for some reason, closing dev_id after adding a new PCI ID to new_id
4352bfe3f2eSlogwang        # results in IOError. however, if the device was successfully bound,
4362bfe3f2eSlogwang        # we don't care for any errors and can safely ignore IOError
4372bfe3f2eSlogwang        tmp = get_pci_device_details(dev_id, True)
4382bfe3f2eSlogwang        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
4392bfe3f2eSlogwang            return
4402bfe3f2eSlogwang        print("Error: bind failed for %s - Cannot bind to driver %s"
4412bfe3f2eSlogwang              % (dev_id, driver))
4422bfe3f2eSlogwang        if saved_driver is not None:  # restore any previous driver
4432bfe3f2eSlogwang            bind_one(dev_id, saved_driver, force)
4442bfe3f2eSlogwang        return
4452bfe3f2eSlogwang
4462bfe3f2eSlogwang    # For kernels > 3.15 driver_override is used to bind a device to a driver.
4472bfe3f2eSlogwang    # Before unbinding it, overwrite driver_override with empty string so that
4482bfe3f2eSlogwang    # the device can be bound to any other driver
4492bfe3f2eSlogwang    filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
4502bfe3f2eSlogwang    if os.path.exists(filename):
4512bfe3f2eSlogwang        try:
4522bfe3f2eSlogwang            f = open(filename, "w")
4532bfe3f2eSlogwang        except:
4542bfe3f2eSlogwang            print("Error: unbind failed for %s - Cannot open %s"
4552bfe3f2eSlogwang                  % (dev_id, filename))
4562bfe3f2eSlogwang            sys.exit(1)
4572bfe3f2eSlogwang        try:
4582bfe3f2eSlogwang            f.write("\00")
4592bfe3f2eSlogwang            f.close()
4602bfe3f2eSlogwang        except:
4612bfe3f2eSlogwang            print("Error: unbind failed for %s - Cannot open %s"
4622bfe3f2eSlogwang                  % (dev_id, filename))
4632bfe3f2eSlogwang            sys.exit(1)
4642bfe3f2eSlogwang
4652bfe3f2eSlogwang
4662bfe3f2eSlogwangdef unbind_all(dev_list, force=False):
4672bfe3f2eSlogwang    """Unbind method, takes a list of device locations"""
4682bfe3f2eSlogwang
4692bfe3f2eSlogwang    if dev_list[0] == "dpdk":
4702bfe3f2eSlogwang        for d in devices.keys():
4712bfe3f2eSlogwang            if "Driver_str" in devices[d]:
4722bfe3f2eSlogwang                if devices[d]["Driver_str"] in dpdk_drivers:
4732bfe3f2eSlogwang                    unbind_one(devices[d]["Slot"], force)
4742bfe3f2eSlogwang        return
4752bfe3f2eSlogwang
4762bfe3f2eSlogwang    dev_list = map(dev_id_from_dev_name, dev_list)
4772bfe3f2eSlogwang    for d in dev_list:
4782bfe3f2eSlogwang        unbind_one(d, force)
4792bfe3f2eSlogwang
4802bfe3f2eSlogwang
4812bfe3f2eSlogwangdef bind_all(dev_list, driver, force=False):
4822bfe3f2eSlogwang    """Bind method, takes a list of device locations"""
4832bfe3f2eSlogwang    global devices
4842bfe3f2eSlogwang
4852bfe3f2eSlogwang    dev_list = map(dev_id_from_dev_name, dev_list)
4862bfe3f2eSlogwang
4872bfe3f2eSlogwang    for d in dev_list:
4882bfe3f2eSlogwang        bind_one(d, driver, force)
4892bfe3f2eSlogwang
4902bfe3f2eSlogwang    # For kernels < 3.15 when binding devices to a generic driver
4912bfe3f2eSlogwang    # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
4922bfe3f2eSlogwang    # that are not bound to any other driver could be bound even if no one has
4932bfe3f2eSlogwang    # asked them to. hence, we check the list of drivers again, and see if
4942bfe3f2eSlogwang    # some of the previously-unbound devices were erroneously bound.
4952bfe3f2eSlogwang    if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
4962bfe3f2eSlogwang        for d in devices.keys():
4972bfe3f2eSlogwang            # skip devices that were already bound or that we know should be bound
4982bfe3f2eSlogwang            if "Driver_str" in devices[d] or d in dev_list:
4992bfe3f2eSlogwang                continue
5002bfe3f2eSlogwang
5012bfe3f2eSlogwang            # update information about this device
5022bfe3f2eSlogwang            devices[d] = dict(devices[d].items() +
5032bfe3f2eSlogwang                              get_pci_device_details(d, True).items())
5042bfe3f2eSlogwang
5052bfe3f2eSlogwang            # check if updated information indicates that the device was bound
5062bfe3f2eSlogwang            if "Driver_str" in devices[d]:
5072bfe3f2eSlogwang                unbind_one(d, force)
5082bfe3f2eSlogwang
5092bfe3f2eSlogwang
5102bfe3f2eSlogwangdef display_devices(title, dev_list, extra_params=None):
5112bfe3f2eSlogwang    '''Displays to the user the details of a list of devices given in
5122bfe3f2eSlogwang    "dev_list". The "extra_params" parameter, if given, should contain a string
5132bfe3f2eSlogwang     with %()s fields in it for replacement by the named fields in each
5142bfe3f2eSlogwang     device's dictionary.'''
5152bfe3f2eSlogwang    strings = []  # this holds the strings to print. We sort before printing
5162bfe3f2eSlogwang    print("\n%s" % title)
5172bfe3f2eSlogwang    print("="*len(title))
5182bfe3f2eSlogwang    if len(dev_list) == 0:
5192bfe3f2eSlogwang        strings.append("<none>")
5202bfe3f2eSlogwang    else:
5212bfe3f2eSlogwang        for dev in dev_list:
5222bfe3f2eSlogwang            if extra_params is not None:
5232bfe3f2eSlogwang                strings.append("%s '%s %s' %s" % (dev["Slot"],
5242bfe3f2eSlogwang                                               dev["Device_str"],
5252bfe3f2eSlogwang                                               dev["Device"],
5262bfe3f2eSlogwang                                               extra_params % dev))
5272bfe3f2eSlogwang            else:
5282bfe3f2eSlogwang                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
5292bfe3f2eSlogwang    # sort before printing, so that the entries appear in PCI order
5302bfe3f2eSlogwang    strings.sort()
5312bfe3f2eSlogwang    print("\n".join(strings))  # print one per line
5322bfe3f2eSlogwang
5332bfe3f2eSlogwangdef show_device_status(devices_type, device_name):
5342bfe3f2eSlogwang    global dpdk_drivers
5352bfe3f2eSlogwang    kernel_drv = []
5362bfe3f2eSlogwang    dpdk_drv = []
5372bfe3f2eSlogwang    no_drv = []
5382bfe3f2eSlogwang
5392bfe3f2eSlogwang    # split our list of network devices into the three categories above
5402bfe3f2eSlogwang    for d in devices.keys():
5412bfe3f2eSlogwang        if device_type_match(devices[d], devices_type):
5422bfe3f2eSlogwang            if not has_driver(d):
5432bfe3f2eSlogwang                no_drv.append(devices[d])
5442bfe3f2eSlogwang                continue
5452bfe3f2eSlogwang            if devices[d]["Driver_str"] in dpdk_drivers:
5462bfe3f2eSlogwang                dpdk_drv.append(devices[d])
5472bfe3f2eSlogwang            else:
5482bfe3f2eSlogwang                kernel_drv.append(devices[d])
5492bfe3f2eSlogwang
550d30ea906Sjfb8856606    n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
551d30ea906Sjfb8856606
552d30ea906Sjfb8856606    # don't bother displaying anything if there are no devices
553d30ea906Sjfb8856606    if n_devs == 0:
554d30ea906Sjfb8856606        msg = "No '%s' devices detected" % device_name
555d30ea906Sjfb8856606        print("")
556d30ea906Sjfb8856606        print(msg)
557d30ea906Sjfb8856606        print("".join('=' * len(msg)))
558d30ea906Sjfb8856606        return
559d30ea906Sjfb8856606
5602bfe3f2eSlogwang    # print each category separately, so we can clearly see what's used by DPDK
561d30ea906Sjfb8856606    if len(dpdk_drv) != 0:
5622bfe3f2eSlogwang        display_devices("%s devices using DPDK-compatible driver" % device_name,
5632bfe3f2eSlogwang                        dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
564d30ea906Sjfb8856606    if len(kernel_drv) != 0:
5652bfe3f2eSlogwang        display_devices("%s devices using kernel driver" % device_name, kernel_drv,
5662bfe3f2eSlogwang                        "if=%(Interface)s drv=%(Driver_str)s "
5672bfe3f2eSlogwang                        "unused=%(Module_str)s %(Active)s")
568d30ea906Sjfb8856606    if len(no_drv) != 0:
5692bfe3f2eSlogwang        display_devices("Other %s devices" % device_name, no_drv,
5702bfe3f2eSlogwang                        "unused=%(Module_str)s")
5712bfe3f2eSlogwang
5722bfe3f2eSlogwangdef show_status():
5732bfe3f2eSlogwang    '''Function called when the script is passed the "--status" option.
5742bfe3f2eSlogwang    Displays to the user what devices are bound to the igb_uio driver, the
5752bfe3f2eSlogwang    kernel driver or to no driver'''
5762bfe3f2eSlogwang
5772bfe3f2eSlogwang    if status_dev == "net" or status_dev == "all":
5782bfe3f2eSlogwang        show_device_status(network_devices, "Network")
5792bfe3f2eSlogwang
5802bfe3f2eSlogwang    if status_dev == "crypto" or status_dev == "all":
5812bfe3f2eSlogwang        show_device_status(crypto_devices, "Crypto")
5822bfe3f2eSlogwang
5832bfe3f2eSlogwang    if status_dev == "event" or status_dev == "all":
5842bfe3f2eSlogwang        show_device_status(eventdev_devices, "Eventdev")
5852bfe3f2eSlogwang
5862bfe3f2eSlogwang    if status_dev == "mempool" or status_dev == "all":
5872bfe3f2eSlogwang        show_device_status(mempool_devices, "Mempool")
5882bfe3f2eSlogwang
589d30ea906Sjfb8856606    if status_dev == "compress" or status_dev == "all":
590d30ea906Sjfb8856606        show_device_status(compress_devices , "Compress")
591d30ea906Sjfb8856606
592d30ea906Sjfb8856606
5932bfe3f2eSlogwangdef parse_args():
5942bfe3f2eSlogwang    '''Parses the command-line arguments given by the user and takes the
5952bfe3f2eSlogwang    appropriate action for each'''
5962bfe3f2eSlogwang    global b_flag
5972bfe3f2eSlogwang    global status_flag
5982bfe3f2eSlogwang    global status_dev
5992bfe3f2eSlogwang    global force_flag
6002bfe3f2eSlogwang    global args
6012bfe3f2eSlogwang    if len(sys.argv) <= 1:
6022bfe3f2eSlogwang        usage()
6032bfe3f2eSlogwang        sys.exit(0)
6042bfe3f2eSlogwang
6052bfe3f2eSlogwang    try:
6062bfe3f2eSlogwang        opts, args = getopt.getopt(sys.argv[1:], "b:us",
6072bfe3f2eSlogwang                                   ["help", "usage", "status", "status-dev=",
6082bfe3f2eSlogwang                                    "force", "bind=", "unbind", ])
6092bfe3f2eSlogwang    except getopt.GetoptError as error:
6102bfe3f2eSlogwang        print(str(error))
6112bfe3f2eSlogwang        print("Run '%s --usage' for further information" % sys.argv[0])
6122bfe3f2eSlogwang        sys.exit(1)
6132bfe3f2eSlogwang
6142bfe3f2eSlogwang    for opt, arg in opts:
6152bfe3f2eSlogwang        if opt == "--help" or opt == "--usage":
6162bfe3f2eSlogwang            usage()
6172bfe3f2eSlogwang            sys.exit(0)
6182bfe3f2eSlogwang        if opt == "--status-dev":
6192bfe3f2eSlogwang            status_flag = True
6202bfe3f2eSlogwang            status_dev = arg
6212bfe3f2eSlogwang        if opt == "--status" or opt == "-s":
6222bfe3f2eSlogwang            status_flag = True
6232bfe3f2eSlogwang            status_dev = "all"
6242bfe3f2eSlogwang        if opt == "--force":
6252bfe3f2eSlogwang            force_flag = True
6262bfe3f2eSlogwang        if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
6272bfe3f2eSlogwang            if b_flag is not None:
6282bfe3f2eSlogwang                print("Error - Only one bind or unbind may be specified\n")
6292bfe3f2eSlogwang                sys.exit(1)
6302bfe3f2eSlogwang            if opt == "-u" or opt == "--unbind":
6312bfe3f2eSlogwang                b_flag = "none"
6322bfe3f2eSlogwang            else:
6332bfe3f2eSlogwang                b_flag = arg
6342bfe3f2eSlogwang
6352bfe3f2eSlogwang
6362bfe3f2eSlogwangdef do_arg_actions():
6372bfe3f2eSlogwang    '''do the actual action requested by the user'''
6382bfe3f2eSlogwang    global b_flag
6392bfe3f2eSlogwang    global status_flag
6402bfe3f2eSlogwang    global force_flag
6412bfe3f2eSlogwang    global args
6422bfe3f2eSlogwang
6432bfe3f2eSlogwang    if b_flag is None and not status_flag:
6442bfe3f2eSlogwang        print("Error: No action specified for devices."
6452bfe3f2eSlogwang              "Please give a -b or -u option")
6462bfe3f2eSlogwang        print("Run '%s --usage' for further information" % sys.argv[0])
6472bfe3f2eSlogwang        sys.exit(1)
6482bfe3f2eSlogwang
6492bfe3f2eSlogwang    if b_flag is not None and len(args) == 0:
6502bfe3f2eSlogwang        print("Error: No devices specified.")
6512bfe3f2eSlogwang        print("Run '%s --usage' for further information" % sys.argv[0])
6522bfe3f2eSlogwang        sys.exit(1)
6532bfe3f2eSlogwang
6542bfe3f2eSlogwang    if b_flag == "none" or b_flag == "None":
6552bfe3f2eSlogwang        unbind_all(args, force_flag)
6562bfe3f2eSlogwang    elif b_flag is not None:
6572bfe3f2eSlogwang        bind_all(args, b_flag, force_flag)
6582bfe3f2eSlogwang    if status_flag:
6592bfe3f2eSlogwang        if b_flag is not None:
6602bfe3f2eSlogwang            clear_data()
6612bfe3f2eSlogwang            # refresh if we have changed anything
6622bfe3f2eSlogwang            get_device_details(network_devices)
6632bfe3f2eSlogwang            get_device_details(crypto_devices)
6642bfe3f2eSlogwang            get_device_details(eventdev_devices)
6652bfe3f2eSlogwang            get_device_details(mempool_devices)
666d30ea906Sjfb8856606            get_device_details(compress_devices)
6672bfe3f2eSlogwang        show_status()
6682bfe3f2eSlogwang
6692bfe3f2eSlogwang
6702bfe3f2eSlogwangdef main():
6712bfe3f2eSlogwang    '''program main function'''
672d30ea906Sjfb8856606    # check if lspci is installed, suppress any output
673d30ea906Sjfb8856606    with open(os.devnull, 'w') as devnull:
674d30ea906Sjfb8856606        ret = subprocess.call(['which', 'lspci'],
675d30ea906Sjfb8856606                              stdout=devnull, stderr=devnull)
676d30ea906Sjfb8856606        if ret != 0:
677d30ea906Sjfb8856606            print("'lspci' not found - please install 'pciutils'")
678d30ea906Sjfb8856606            sys.exit(1)
6792bfe3f2eSlogwang    parse_args()
6802bfe3f2eSlogwang    check_modules()
6812bfe3f2eSlogwang    clear_data()
6822bfe3f2eSlogwang    get_device_details(network_devices)
6832bfe3f2eSlogwang    get_device_details(crypto_devices)
6842bfe3f2eSlogwang    get_device_details(eventdev_devices)
6852bfe3f2eSlogwang    get_device_details(mempool_devices)
686d30ea906Sjfb8856606    get_device_details(compress_devices)
6872bfe3f2eSlogwang    do_arg_actions()
6882bfe3f2eSlogwang
6892bfe3f2eSlogwangif __name__ == "__main__":
6902bfe3f2eSlogwang    main()
691