1#!/usr/bin/env python3 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright(c) 2010-2014 Intel Corporation 4# 5 6import sys 7import os 8import getopt 9import subprocess 10from glob import glob 11from os.path import exists, abspath, dirname, basename 12from os.path import join as path_join 13 14# The PCI base class for all devices 15network_class = {'Class': '02', 'Vendor': None, 'Device': None, 16 'SVendor': None, 'SDevice': None} 17acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None, 18 'SVendor': None, 'SDevice': None} 19ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30', 20 'SVendor': None, 'SDevice': None} 21encryption_class = {'Class': '10', 'Vendor': None, 'Device': None, 22 'SVendor': None, 'SDevice': None} 23intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None, 24 'SVendor': None, 'SDevice': None} 25cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d', 26 'SVendor': None, 'SDevice': None} 27cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053', 28 'SVendor': None, 'SDevice': None} 29cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049', 30 'SVendor': None, 'SDevice': None} 31cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051', 32 'SVendor': None, 'SDevice': None} 33cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037', 34 'SVendor': None, 'SDevice': None} 35avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110', 36 'SVendor': None, 'SDevice': None} 37 38octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa', 39 'SVendor': None, 'SDevice': None} 40octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc', 41 'SVendor': None, 'SDevice': None} 42octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081', 43 'SVendor': None, 'SDevice': None} 44 45intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f', 46 'SVendor': None, 'SDevice': None} 47intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021', 48 'SVendor': None, 'SDevice': None} 49intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00', 50 'SVendor': None, 'SDevice': None} 51intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c', 52 'SVendor': None, 'SDevice': None} 53 54network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class] 55baseband_devices = [acceleration_class] 56crypto_devices = [encryption_class, intel_processor_class] 57eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso] 58mempool_devices = [cavium_fpa, octeontx2_npa] 59compress_devices = [cavium_zip] 60misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ioat_icx, intel_ntb_skx, octeontx2_dma] 61 62# global dict ethernet devices present. Dictionary indexed by PCI address. 63# Each device within this is itself a dictionary of device properties 64devices = {} 65# list of supported DPDK drivers 66dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"] 67# list of currently loaded kernel modules 68loaded_modules = None 69 70# command-line arg flags 71b_flag = None 72status_flag = False 73force_flag = False 74args = [] 75 76 77def usage(): 78 '''Print usage information for the program''' 79 argv0 = basename(sys.argv[0]) 80 print(""" 81Usage: 82------ 83 84 %(argv0)s [options] DEVICE1 DEVICE2 .... 85 86where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax 87or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may 88also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc. 89If devices are specified using PCI <domain:>bus:device:func format, then 90shell wildcards and ranges may be used, e.g. 80:04.*, 80:04.[0-3] 91 92Options: 93 --help, --usage: 94 Display usage information and quit 95 96 -s, --status: 97 Print the current status of all known network, crypto, event 98 and mempool devices. 99 For each device, it displays the PCI domain, bus, slot and function, 100 along with a text description of the device. Depending upon whether the 101 device is being used by a kernel driver, the igb_uio driver, or no 102 driver, other relevant information will be displayed: 103 * the Linux interface name e.g. if=eth0 104 * the driver being used e.g. drv=igb_uio 105 * any suitable drivers not currently using that device 106 e.g. unused=igb_uio 107 NOTE: if this flag is passed along with a bind/unbind option, the 108 status display will always occur after the other operations have taken 109 place. 110 111 --status-dev: 112 Print the status of given device group. Supported device groups are: 113 "net", "baseband", "crypto", "event", "mempool" and "compress" 114 115 -b driver, --bind=driver: 116 Select the driver to use or \"none\" to unbind the device 117 118 -u, --unbind: 119 Unbind a device (Equivalent to \"-b none\") 120 121 --force: 122 By default, network devices which are used by Linux - as indicated by 123 having routes in the routing table - cannot be modified. Using the 124 --force flag overrides this behavior, allowing active links to be 125 forcibly unbound. 126 WARNING: This can lead to loss of network connection and should be used 127 with caution. 128 129Examples: 130--------- 131 132To display current device status: 133 %(argv0)s --status 134 135To display current network device status: 136 %(argv0)s --status-dev net 137 138To bind eth1 from the current driver and move to use igb_uio 139 %(argv0)s --bind=igb_uio eth1 140 141To unbind 0000:01:00.0 from using any driver 142 %(argv0)s -u 0000:01:00.0 143 144To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver 145 %(argv0)s -b ixgbe 02:00.0 02:00.1 146 147To bind all functions on device 0000:02:00 to ixgbe kernel driver 148 %(argv0)s -b ixgbe 02:00.* 149 150 """ % locals()) # replace items from local variables 151 152# check if a specific kernel module is loaded 153def module_is_loaded(module): 154 global loaded_modules 155 156 if module == 'vfio_pci': 157 module = 'vfio-pci' 158 159 if loaded_modules: 160 return module in loaded_modules 161 162 # Get list of sysfs modules (both built-in and dynamically loaded) 163 sysfs_path = '/sys/module/' 164 165 # Get the list of directories in sysfs_path 166 sysfs_mods = [m for m in os.listdir(sysfs_path) 167 if os.path.isdir(os.path.join(sysfs_path, m))] 168 169 # special case for vfio_pci (module is named vfio-pci, 170 # but its .ko is named vfio_pci) 171 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods] 172 173 loaded_modules = sysfs_mods 174 175 return module in sysfs_mods 176 177 178def check_modules(): 179 '''Checks that igb_uio is loaded''' 180 global dpdk_drivers 181 182 # list of supported modules 183 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers] 184 185 # first check if module is loaded 186 for mod in mods: 187 if module_is_loaded(mod["Name"]): 188 mod["Found"] = True 189 190 # check if we have at least one loaded module 191 if True not in [mod["Found"] for mod in mods] and b_flag is not None: 192 print("Warning: no supported DPDK kernel modules are loaded", file=sys.stderr) 193 194 # change DPDK driver list to only contain drivers that are loaded 195 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]] 196 197 198def has_driver(dev_id): 199 '''return true if a device is assigned to a driver. False otherwise''' 200 return "Driver_str" in devices[dev_id] 201 202 203def get_pci_device_details(dev_id, probe_lspci): 204 '''This function gets additional details for a PCI device''' 205 device = {} 206 207 if probe_lspci: 208 extra_info = subprocess.check_output(["lspci", "-vmmks", dev_id]).splitlines() 209 # parse lspci details 210 for line in extra_info: 211 if len(line) == 0: 212 continue 213 name, value = line.decode("utf8").split("\t", 1) 214 name = name.strip(":") + "_str" 215 device[name] = value 216 # check for a unix interface name 217 device["Interface"] = "" 218 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id): 219 if "net" in dirs: 220 device["Interface"] = \ 221 ",".join(os.listdir(os.path.join(base, "net"))) 222 break 223 # check if a port is used for ssh connection 224 device["Ssh_if"] = False 225 device["Active"] = "" 226 227 return device 228 229def clear_data(): 230 '''This function clears any old data''' 231 global devices 232 devices = {} 233 234def get_device_details(devices_type): 235 '''This function populates the "devices" dictionary. The keys used are 236 the pci addresses (domain:bus:slot.func). The values are themselves 237 dictionaries - one for each NIC.''' 238 global devices 239 global dpdk_drivers 240 241 # first loop through and read details for all devices 242 # request machine readable format, with numeric IDs and String 243 dev = {} 244 dev_lines = subprocess.check_output(["lspci", "-Dvmmnnk"]).splitlines() 245 for dev_line in dev_lines: 246 if len(dev_line) == 0: 247 if device_type_match(dev, devices_type): 248 # Replace "Driver" with "Driver_str" to have consistency of 249 # of dictionary key names 250 if "Driver" in dev.keys(): 251 dev["Driver_str"] = dev.pop("Driver") 252 if "Module" in dev.keys(): 253 dev["Module_str"] = dev.pop("Module") 254 # use dict to make copy of dev 255 devices[dev["Slot"]] = dict(dev) 256 # Clear previous device's data 257 dev = {} 258 else: 259 name, value = dev_line.decode("utf8").split("\t", 1) 260 value_list = value.rsplit(' ', 1) 261 if len(value_list) > 1: 262 # String stored in <name>_str 263 dev[name.rstrip(":") + '_str'] = value_list[0] 264 # Numeric IDs 265 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \ 266 .rstrip("]").lstrip("[") 267 268 if devices_type == network_devices: 269 # check what is the interface if any for an ssh connection if 270 # any to this host, so we can mark it later. 271 ssh_if = [] 272 route = subprocess.check_output(["ip", "-o", "route"]) 273 # filter out all lines for 169.254 routes 274 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"), 275 route.decode().splitlines())) 276 rt_info = route.split() 277 for i in range(len(rt_info) - 1): 278 if rt_info[i] == "dev": 279 ssh_if.append(rt_info[i+1]) 280 281 # based on the basic info, get extended text details 282 for d in devices.keys(): 283 if not device_type_match(devices[d], devices_type): 284 continue 285 286 # get additional info and add it to existing data 287 devices[d] = devices[d].copy() 288 # No need to probe lspci 289 devices[d].update(get_pci_device_details(d, False).items()) 290 291 if devices_type == network_devices: 292 for _if in ssh_if: 293 if _if in devices[d]["Interface"].split(","): 294 devices[d]["Ssh_if"] = True 295 devices[d]["Active"] = "*Active*" 296 break 297 298 # add igb_uio to list of supporting modules if needed 299 if "Module_str" in devices[d]: 300 for driver in dpdk_drivers: 301 if driver not in devices[d]["Module_str"]: 302 devices[d]["Module_str"] = \ 303 devices[d]["Module_str"] + ",%s" % driver 304 else: 305 devices[d]["Module_str"] = ",".join(dpdk_drivers) 306 307 # make sure the driver and module strings do not have any duplicates 308 if has_driver(d): 309 modules = devices[d]["Module_str"].split(",") 310 if devices[d]["Driver_str"] in modules: 311 modules.remove(devices[d]["Driver_str"]) 312 devices[d]["Module_str"] = ",".join(modules) 313 314 315def device_type_match(dev, devices_type): 316 for i in range(len(devices_type)): 317 param_count = len( 318 [x for x in devices_type[i].values() if x is not None]) 319 match_count = 0 320 if dev["Class"][0:2] == devices_type[i]["Class"]: 321 match_count = match_count + 1 322 for key in devices_type[i].keys(): 323 if key != 'Class' and devices_type[i][key]: 324 value_list = devices_type[i][key].split(',') 325 for value in value_list: 326 if value.strip(' ') == dev[key]: 327 match_count = match_count + 1 328 # count must be the number of non None parameters to match 329 if match_count == param_count: 330 return True 331 return False 332 333def dev_id_from_dev_name(dev_name): 334 '''Take a device "name" - a string passed in by user to identify a NIC 335 device, and determine the device id - i.e. the domain:bus:slot.func - for 336 it, which can then be used to index into the devices array''' 337 338 # check if it's already a suitable index 339 if dev_name in devices: 340 return dev_name 341 # check if it's an index just missing the domain part 342 elif "0000:" + dev_name in devices: 343 return "0000:" + dev_name 344 else: 345 # check if it's an interface name, e.g. eth1 346 for d in devices.keys(): 347 if dev_name in devices[d]["Interface"].split(","): 348 return devices[d]["Slot"] 349 # if nothing else matches - error 350 raise ValueError("Unknown device: %s. " 351 "Please specify device in \"bus:slot.func\" format" % dev_name) 352 353 354def unbind_one(dev_id, force): 355 '''Unbind the device identified by "dev_id" from its current driver''' 356 dev = devices[dev_id] 357 if not has_driver(dev_id): 358 print("Notice: %s %s %s is not currently managed by any driver" % 359 (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr) 360 return 361 362 # prevent us disconnecting ourselves 363 if dev["Ssh_if"] and not force: 364 print("Warning: routing table indicates that interface %s is active. " 365 "Skipping unbind" % dev_id, file=sys.stderr) 366 return 367 368 # write to /sys to unbind 369 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"] 370 try: 371 f = open(filename, "a") 372 except: 373 sys.exit("Error: unbind failed for %s - Cannot open %s" % 374 (dev_id, filename)) 375 f.write(dev_id) 376 f.close() 377 378 379def bind_one(dev_id, driver, force): 380 '''Bind the device given by "dev_id" to the driver "driver". If the device 381 is already bound to a different driver, it will be unbound first''' 382 dev = devices[dev_id] 383 saved_driver = None # used to rollback any unbind in case of failure 384 385 # prevent disconnection of our ssh session 386 if dev["Ssh_if"] and not force: 387 print("Warning: routing table indicates that interface %s is active. " 388 "Not modifying" % dev_id, file=sys.stderr) 389 return 390 391 # unbind any existing drivers we don't want 392 if has_driver(dev_id): 393 if dev["Driver_str"] == driver: 394 print("Notice: %s already bound to driver %s, skipping" % 395 (dev_id, driver), file=sys.stderr) 396 return 397 else: 398 saved_driver = dev["Driver_str"] 399 unbind_one(dev_id, force) 400 dev["Driver_str"] = "" # clear driver string 401 402 # For kernels >= 3.15 driver_override can be used to specify the driver 403 # for a device rather than relying on the driver to provide a positive 404 # match of the device. The existing process of looking up 405 # the vendor and device ID, adding them to the driver new_id, 406 # will erroneously bind other devices too which has the additional burden 407 # of unbinding those devices 408 if driver in dpdk_drivers: 409 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id 410 if os.path.exists(filename): 411 try: 412 f = open(filename, "w") 413 except: 414 print("Error: bind failed for %s - Cannot open %s" 415 % (dev_id, filename), file=sys.stderr) 416 return 417 try: 418 f.write("%s" % driver) 419 f.close() 420 except: 421 print("Error: bind failed for %s - Cannot write driver %s to " 422 "PCI ID " % (dev_id, driver), file=sys.stderr) 423 return 424 # For kernels < 3.15 use new_id to add PCI id's to the driver 425 else: 426 filename = "/sys/bus/pci/drivers/%s/new_id" % driver 427 try: 428 f = open(filename, "w") 429 except: 430 print("Error: bind failed for %s - Cannot open %s" 431 % (dev_id, filename), file=sys.stderr) 432 return 433 try: 434 # Convert Device and Vendor Id to int to write to new_id 435 f.write("%04x %04x" % (int(dev["Vendor"],16), 436 int(dev["Device"], 16))) 437 f.close() 438 except: 439 print("Error: bind failed for %s - Cannot write new PCI ID to " 440 "driver %s" % (dev_id, driver), file=sys.stderr) 441 return 442 443 # do the bind by writing to /sys 444 filename = "/sys/bus/pci/drivers/%s/bind" % driver 445 try: 446 f = open(filename, "a") 447 except: 448 print("Error: bind failed for %s - Cannot open %s" 449 % (dev_id, filename), file=sys.stderr) 450 if saved_driver is not None: # restore any previous driver 451 bind_one(dev_id, saved_driver, force) 452 return 453 try: 454 f.write(dev_id) 455 f.close() 456 except: 457 # for some reason, closing dev_id after adding a new PCI ID to new_id 458 # results in IOError. however, if the device was successfully bound, 459 # we don't care for any errors and can safely ignore IOError 460 tmp = get_pci_device_details(dev_id, True) 461 if "Driver_str" in tmp and tmp["Driver_str"] == driver: 462 return 463 print("Error: bind failed for %s - Cannot bind to driver %s" 464 % (dev_id, driver), file=sys.stderr) 465 if saved_driver is not None: # restore any previous driver 466 bind_one(dev_id, saved_driver, force) 467 return 468 469 # For kernels > 3.15 driver_override is used to bind a device to a driver. 470 # Before unbinding it, overwrite driver_override with empty string so that 471 # the device can be bound to any other driver 472 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id 473 if os.path.exists(filename): 474 try: 475 f = open(filename, "w") 476 except: 477 sys.exit("Error: unbind failed for %s - Cannot open %s" 478 % (dev_id, filename)) 479 try: 480 f.write("\00") 481 f.close() 482 except: 483 sys.exit("Error: unbind failed for %s - Cannot open %s" 484 % (dev_id, filename)) 485 486 487def unbind_all(dev_list, force=False): 488 """Unbind method, takes a list of device locations""" 489 490 if dev_list[0] == "dpdk": 491 for d in devices.keys(): 492 if "Driver_str" in devices[d]: 493 if devices[d]["Driver_str"] in dpdk_drivers: 494 unbind_one(devices[d]["Slot"], force) 495 return 496 497 try: 498 dev_list = map(dev_id_from_dev_name, dev_list) 499 except ValueError as ex: 500 print(ex) 501 sys.exit(1) 502 503 for d in dev_list: 504 unbind_one(d, force) 505 506 507def bind_all(dev_list, driver, force=False): 508 """Bind method, takes a list of device locations""" 509 global devices 510 511 # a common user error is to forget to specify the driver the devices need to 512 # be bound to. check if the driver is a valid device, and if it is, show 513 # a meaningful error. 514 try: 515 dev_id_from_dev_name(driver) 516 # if we've made it this far, this means that the "driver" was a valid 517 # device string, so it's probably not a valid driver name. 518 sys.exit("Error: Driver '%s' does not look like a valid driver. " \ 519 "Did you forget to specify the driver to bind devices to?" % driver) 520 except ValueError: 521 # driver generated error - it's not a valid device ID, so all is well 522 pass 523 524 # check if we're attempting to bind to a driver that isn't loaded 525 if not module_is_loaded(driver.replace('-','_')): 526 sys.exit("Error: Driver '%s' is not loaded." % driver) 527 528 try: 529 dev_list = map(dev_id_from_dev_name, dev_list) 530 except ValueError as ex: 531 sys.exit(ex) 532 533 for d in dev_list: 534 bind_one(d, driver, force) 535 536 # For kernels < 3.15 when binding devices to a generic driver 537 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices 538 # that are not bound to any other driver could be bound even if no one has 539 # asked them to. hence, we check the list of drivers again, and see if 540 # some of the previously-unbound devices were erroneously bound. 541 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d): 542 for d in devices.keys(): 543 # skip devices that were already bound or that we know should be bound 544 if "Driver_str" in devices[d] or d in dev_list: 545 continue 546 547 # update information about this device 548 devices[d] = dict(devices[d].items() + 549 get_pci_device_details(d, True).items()) 550 551 # check if updated information indicates that the device was bound 552 if "Driver_str" in devices[d]: 553 unbind_one(d, force) 554 555 556def display_devices(title, dev_list, extra_params=None): 557 '''Displays to the user the details of a list of devices given in 558 "dev_list". The "extra_params" parameter, if given, should contain a string 559 with %()s fields in it for replacement by the named fields in each 560 device's dictionary.''' 561 strings = [] # this holds the strings to print. We sort before printing 562 print("\n%s" % title) 563 print("="*len(title)) 564 if len(dev_list) == 0: 565 strings.append("<none>") 566 else: 567 for dev in dev_list: 568 if extra_params is not None: 569 strings.append("%s '%s %s' %s" % (dev["Slot"], 570 dev["Device_str"], 571 dev["Device"], 572 extra_params % dev)) 573 else: 574 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"])) 575 # sort before printing, so that the entries appear in PCI order 576 strings.sort() 577 print("\n".join(strings)) # print one per line 578 579def show_device_status(devices_type, device_name, if_field=False): 580 global dpdk_drivers 581 kernel_drv = [] 582 dpdk_drv = [] 583 no_drv = [] 584 585 # split our list of network devices into the three categories above 586 for d in devices.keys(): 587 if device_type_match(devices[d], devices_type): 588 if not has_driver(d): 589 no_drv.append(devices[d]) 590 continue 591 if devices[d]["Driver_str"] in dpdk_drivers: 592 dpdk_drv.append(devices[d]) 593 else: 594 kernel_drv.append(devices[d]) 595 596 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv) 597 598 # don't bother displaying anything if there are no devices 599 if n_devs == 0: 600 msg = "No '%s' devices detected" % device_name 601 print("") 602 print(msg) 603 print("".join('=' * len(msg))) 604 return 605 606 # print each category separately, so we can clearly see what's used by DPDK 607 if len(dpdk_drv) != 0: 608 display_devices("%s devices using DPDK-compatible driver" % device_name, 609 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s") 610 if len(kernel_drv) != 0: 611 if_text = "" 612 if if_field: 613 if_text = "if=%(Interface)s " 614 display_devices("%s devices using kernel driver" % device_name, kernel_drv, 615 if_text + "drv=%(Driver_str)s " 616 "unused=%(Module_str)s %(Active)s") 617 if len(no_drv) != 0: 618 display_devices("Other %s devices" % device_name, no_drv, 619 "unused=%(Module_str)s") 620 621def show_status(): 622 '''Function called when the script is passed the "--status" option. 623 Displays to the user what devices are bound to the igb_uio driver, the 624 kernel driver or to no driver''' 625 626 if status_dev == "net" or status_dev == "all": 627 show_device_status(network_devices, "Network", if_field=True) 628 629 if status_dev == "baseband" or status_dev == "all": 630 show_device_status(baseband_devices, "Baseband") 631 632 if status_dev == "crypto" or status_dev == "all": 633 show_device_status(crypto_devices, "Crypto") 634 635 if status_dev == "event" or status_dev == "all": 636 show_device_status(eventdev_devices, "Eventdev") 637 638 if status_dev == "mempool" or status_dev == "all": 639 show_device_status(mempool_devices, "Mempool") 640 641 if status_dev == "compress" or status_dev == "all": 642 show_device_status(compress_devices , "Compress") 643 644 if status_dev == "misc" or status_dev == "all": 645 show_device_status(misc_devices, "Misc (rawdev)") 646 647 648def pci_glob(arg): 649 '''Returns a list containing either: 650 * List of PCI B:D:F matching arg, using shell wildcards e.g. 80:04.* 651 * Only the passed arg if matching list is empty''' 652 sysfs_path = "/sys/bus/pci/devices" 653 for _glob in [arg, '0000:' + arg]: 654 paths = [basename(path) for path in glob(path_join(sysfs_path, _glob))] 655 if paths: 656 return paths 657 return [arg] 658 659 660def parse_args(): 661 '''Parses the command-line arguments given by the user and takes the 662 appropriate action for each''' 663 global b_flag 664 global status_flag 665 global status_dev 666 global force_flag 667 global args 668 if len(sys.argv) <= 1: 669 usage() 670 sys.exit(0) 671 672 try: 673 opts, args = getopt.getopt(sys.argv[1:], "b:us", 674 ["help", "usage", "status", "status-dev=", 675 "force", "bind=", "unbind", ]) 676 except getopt.GetoptError as error: 677 print(str(error)) 678 print("Run '%s --usage' for further information" % sys.argv[0]) 679 sys.exit(1) 680 681 for opt, arg in opts: 682 if opt == "--help" or opt == "--usage": 683 usage() 684 sys.exit(0) 685 if opt == "--status-dev": 686 status_flag = True 687 status_dev = arg 688 if opt == "--status" or opt == "-s": 689 status_flag = True 690 status_dev = "all" 691 if opt == "--force": 692 force_flag = True 693 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind": 694 if b_flag is not None: 695 sys.exit("Error: binding and unbinding are mutually exclusive") 696 if opt == "-u" or opt == "--unbind": 697 b_flag = "none" 698 else: 699 b_flag = arg 700 701 # resolve any PCI globs in the args 702 new_args = [] 703 for arg in args: 704 new_args.extend(pci_glob(arg)) 705 args = new_args 706 707def do_arg_actions(): 708 '''do the actual action requested by the user''' 709 global b_flag 710 global status_flag 711 global force_flag 712 global args 713 714 if b_flag is None and not status_flag: 715 print("Error: No action specified for devices. " 716 "Please give a -b or -u option", file=sys.stderr) 717 usage() 718 sys.exit(1) 719 720 if b_flag is not None and len(args) == 0: 721 print("Error: No devices specified.", file=sys.stderr) 722 usage() 723 sys.exit(1) 724 725 if b_flag == "none" or b_flag == "None": 726 unbind_all(args, force_flag) 727 elif b_flag is not None: 728 bind_all(args, b_flag, force_flag) 729 if status_flag: 730 if b_flag is not None: 731 clear_data() 732 # refresh if we have changed anything 733 get_device_details(network_devices) 734 get_device_details(baseband_devices) 735 get_device_details(crypto_devices) 736 get_device_details(eventdev_devices) 737 get_device_details(mempool_devices) 738 get_device_details(compress_devices) 739 get_device_details(misc_devices) 740 show_status() 741 742 743def main(): 744 '''program main function''' 745 # check if lspci is installed, suppress any output 746 with open(os.devnull, 'w') as devnull: 747 ret = subprocess.call(['which', 'lspci'], 748 stdout=devnull, stderr=devnull) 749 if ret != 0: 750 sys.exit("'lspci' not found - please install 'pciutils'") 751 parse_args() 752 check_modules() 753 clear_data() 754 get_device_details(network_devices) 755 get_device_details(baseband_devices) 756 get_device_details(crypto_devices) 757 get_device_details(eventdev_devices) 758 get_device_details(mempool_devices) 759 get_device_details(compress_devices) 760 get_device_details(misc_devices) 761 do_arg_actions() 762 763if __name__ == "__main__": 764 main() 765