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