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