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