2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2010-2014 Intel Corporation
6 from __future__ import print_function
11 from os.path import exists, abspath, dirname, basename
13 if sys.version_info.major < 3:
14 print("WARNING: Python 2 is deprecated for use in DPDK, and will not work in future releases.", file=sys.stderr)
15 print("Please use Python 3 instead", file=sys.stderr)
17 # The PCI base class for all devices
18 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
19 'SVendor': None, 'SDevice': None}
20 acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
21 'SVendor': None, 'SDevice': None}
22 ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
23 'SVendor': None, 'SDevice': None}
24 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
25 'SVendor': None, 'SDevice': None}
26 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
27 'SVendor': None, 'SDevice': None}
28 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
29 'SVendor': None, 'SDevice': None}
30 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
31 'SVendor': None, 'SDevice': None}
32 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
33 'SVendor': None, 'SDevice': None}
34 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
35 'SVendor': None, 'SDevice': None}
36 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
37 'SVendor': None, 'SDevice': None}
38 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
39 'SVendor': None, 'SDevice': None}
41 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
42 'SVendor': None, 'SDevice': None}
43 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
44 'SVendor': None, 'SDevice': None}
45 octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
46 'SVendor': None, 'SDevice': None}
48 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
49 'SVendor': None, 'SDevice': None}
50 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
51 'SVendor': None, 'SDevice': None}
52 intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00',
53 'SVendor': None, 'SDevice': None}
54 intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
55 'SVendor': None, 'SDevice': None}
57 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
58 baseband_devices = [acceleration_class]
59 crypto_devices = [encryption_class, intel_processor_class]
60 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
61 mempool_devices = [cavium_fpa, octeontx2_npa]
62 compress_devices = [cavium_zip]
63 misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ioat_icx, intel_ntb_skx, octeontx2_dma]
65 # global dict ethernet devices present. Dictionary indexed by PCI address.
66 # Each device within this is itself a dictionary of device properties
68 # list of supported DPDK drivers
69 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
70 # list of currently loaded kernel modules
73 # command-line arg flags
81 '''Print usage information for the program'''
82 argv0 = basename(sys.argv[0])
87 %(argv0)s [options] DEVICE1 DEVICE2 ....
89 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
90 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
91 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
95 Display usage information and quit
98 Print the current status of all known network, crypto, event
100 For each device, it displays the PCI domain, bus, slot and function,
101 along with a text description of the device. Depending upon whether the
102 device is being used by a kernel driver, the igb_uio driver, or no
103 driver, other relevant information will be displayed:
104 * the Linux interface name e.g. if=eth0
105 * the driver being used e.g. drv=igb_uio
106 * any suitable drivers not currently using that device
108 NOTE: if this flag is passed along with a bind/unbind option, the
109 status display will always occur after the other operations have taken
113 Print the status of given device group. Supported device groups are:
114 "net", "baseband", "crypto", "event", "mempool" and "compress"
116 -b driver, --bind=driver:
117 Select the driver to use or \"none\" to unbind the device
120 Unbind a device (Equivalent to \"-b none\")
123 By default, network devices which are used by Linux - as indicated by
124 having routes in the routing table - cannot be modified. Using the
125 --force flag overrides this behavior, allowing active links to be
127 WARNING: This can lead to loss of network connection and should be used
133 To display current device status:
136 To display current network device status:
137 %(argv0)s --status-dev net
139 To bind eth1 from the current driver and move to use igb_uio
140 %(argv0)s --bind=igb_uio eth1
142 To unbind 0000:01:00.0 from using any driver
143 %(argv0)s -u 0000:01:00.0
145 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
146 %(argv0)s -b ixgbe 02:00.0 02:00.1
148 """ % locals()) # replace items from local variables
151 # This is roughly compatible with check_output function in subprocess module
152 # which is only available in python 2.7.
153 def check_output(args, stderr=None):
154 '''Run a command and capture its output'''
155 return subprocess.Popen(args, stdout=subprocess.PIPE,
156 stderr=stderr).communicate()[0]
158 # check if a specific kernel module is loaded
159 def module_is_loaded(module):
160 global loaded_modules
162 if module == 'vfio_pci':
166 return module in loaded_modules
168 # Get list of sysfs modules (both built-in and dynamically loaded)
169 sysfs_path = '/sys/module/'
171 # Get the list of directories in sysfs_path
172 sysfs_mods = [m for m in os.listdir(sysfs_path)
173 if os.path.isdir(os.path.join(sysfs_path, m))]
175 # special case for vfio_pci (module is named vfio-pci,
176 # but its .ko is named vfio_pci)
177 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
179 loaded_modules = sysfs_mods
181 return module in sysfs_mods
185 '''Checks that igb_uio is loaded'''
188 # list of supported modules
189 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
191 # first check if module is loaded
193 if module_is_loaded(mod["Name"]):
196 # check if we have at least one loaded module
197 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
198 print("Warning: no supported DPDK kernel modules are loaded", file=sys.stderr)
200 # change DPDK driver list to only contain drivers that are loaded
201 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
204 def has_driver(dev_id):
205 '''return true if a device is assigned to a driver. False otherwise'''
206 return "Driver_str" in devices[dev_id]
209 def get_pci_device_details(dev_id, probe_lspci):
210 '''This function gets additional details for a PCI device'''
214 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
216 # parse lspci details
217 for line in extra_info:
220 name, value = line.decode("utf8").split("\t", 1)
221 name = name.strip(":") + "_str"
223 # check for a unix interface name
224 device["Interface"] = ""
225 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
227 device["Interface"] = \
228 ",".join(os.listdir(os.path.join(base, "net")))
230 # check if a port is used for ssh connection
231 device["Ssh_if"] = False
232 device["Active"] = ""
237 '''This function clears any old data'''
241 def get_device_details(devices_type):
242 '''This function populates the "devices" dictionary. The keys used are
243 the pci addresses (domain:bus:slot.func). The values are themselves
244 dictionaries - one for each NIC.'''
248 # first loop through and read details for all devices
249 # request machine readable format, with numeric IDs and String
251 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
252 for dev_line in dev_lines:
253 if len(dev_line) == 0:
254 if device_type_match(dev, devices_type):
255 # Replace "Driver" with "Driver_str" to have consistency of
256 # of dictionary key names
257 if "Driver" in dev.keys():
258 dev["Driver_str"] = dev.pop("Driver")
259 if "Module" in dev.keys():
260 dev["Module_str"] = dev.pop("Module")
261 # use dict to make copy of dev
262 devices[dev["Slot"]] = dict(dev)
263 # Clear previous device's data
266 name, value = dev_line.decode("utf8").split("\t", 1)
267 value_list = value.rsplit(' ', 1)
268 if len(value_list) > 1:
269 # String stored in <name>_str
270 dev[name.rstrip(":") + '_str'] = value_list[0]
272 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
273 .rstrip("]").lstrip("[")
275 if devices_type == network_devices:
276 # check what is the interface if any for an ssh connection if
277 # any to this host, so we can mark it later.
279 route = check_output(["ip", "-o", "route"])
280 # filter out all lines for 169.254 routes
281 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
282 route.decode().splitlines()))
283 rt_info = route.split()
284 for i in range(len(rt_info) - 1):
285 if rt_info[i] == "dev":
286 ssh_if.append(rt_info[i+1])
288 # based on the basic info, get extended text details
289 for d in devices.keys():
290 if not device_type_match(devices[d], devices_type):
293 # get additional info and add it to existing data
294 devices[d] = devices[d].copy()
295 # No need to probe lspci
296 devices[d].update(get_pci_device_details(d, False).items())
298 if devices_type == network_devices:
300 if _if in devices[d]["Interface"].split(","):
301 devices[d]["Ssh_if"] = True
302 devices[d]["Active"] = "*Active*"
305 # add igb_uio to list of supporting modules if needed
306 if "Module_str" in devices[d]:
307 for driver in dpdk_drivers:
308 if driver not in devices[d]["Module_str"]:
309 devices[d]["Module_str"] = \
310 devices[d]["Module_str"] + ",%s" % driver
312 devices[d]["Module_str"] = ",".join(dpdk_drivers)
314 # make sure the driver and module strings do not have any duplicates
316 modules = devices[d]["Module_str"].split(",")
317 if devices[d]["Driver_str"] in modules:
318 modules.remove(devices[d]["Driver_str"])
319 devices[d]["Module_str"] = ",".join(modules)
322 def device_type_match(dev, devices_type):
323 for i in range(len(devices_type)):
325 [x for x in devices_type[i].values() if x is not None])
327 if dev["Class"][0:2] == devices_type[i]["Class"]:
328 match_count = match_count + 1
329 for key in devices_type[i].keys():
330 if key != 'Class' and devices_type[i][key]:
331 value_list = devices_type[i][key].split(',')
332 for value in value_list:
333 if value.strip(' ') == dev[key]:
334 match_count = match_count + 1
335 # count must be the number of non None parameters to match
336 if match_count == param_count:
340 def dev_id_from_dev_name(dev_name):
341 '''Take a device "name" - a string passed in by user to identify a NIC
342 device, and determine the device id - i.e. the domain:bus:slot.func - for
343 it, which can then be used to index into the devices array'''
345 # check if it's already a suitable index
346 if dev_name in devices:
348 # check if it's an index just missing the domain part
349 elif "0000:" + dev_name in devices:
350 return "0000:" + dev_name
352 # check if it's an interface name, e.g. eth1
353 for d in devices.keys():
354 if dev_name in devices[d]["Interface"].split(","):
355 return devices[d]["Slot"]
356 # if nothing else matches - error
357 raise ValueError("Unknown device: %s. "
358 "Please specify device in \"bus:slot.func\" format" % dev_name)
361 def unbind_one(dev_id, force):
362 '''Unbind the device identified by "dev_id" from its current driver'''
363 dev = devices[dev_id]
364 if not has_driver(dev_id):
365 print("Notice: %s %s %s is not currently managed by any driver" %
366 (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr)
369 # prevent us disconnecting ourselves
370 if dev["Ssh_if"] and not force:
371 print("Warning: routing table indicates that interface %s is active. "
372 "Skipping unbind" % dev_id, file=sys.stderr)
375 # write to /sys to unbind
376 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
378 f = open(filename, "a")
380 sys.exit("Error: unbind failed for %s - Cannot open %s" %
386 def bind_one(dev_id, driver, force):
387 '''Bind the device given by "dev_id" to the driver "driver". If the device
388 is already bound to a different driver, it will be unbound first'''
389 dev = devices[dev_id]
390 saved_driver = None # used to rollback any unbind in case of failure
392 # prevent disconnection of our ssh session
393 if dev["Ssh_if"] and not force:
394 print("Warning: routing table indicates that interface %s is active. "
395 "Not modifying" % dev_id, file=sys.stderr)
398 # unbind any existing drivers we don't want
399 if has_driver(dev_id):
400 if dev["Driver_str"] == driver:
401 print("Notice: %s already bound to driver %s, skipping" %
402 (dev_id, driver), file=sys.stderr)
405 saved_driver = dev["Driver_str"]
406 unbind_one(dev_id, force)
407 dev["Driver_str"] = "" # clear driver string
409 # For kernels >= 3.15 driver_override can be used to specify the driver
410 # for a device rather than relying on the driver to provide a positive
411 # match of the device. The existing process of looking up
412 # the vendor and device ID, adding them to the driver new_id,
413 # will erroneously bind other devices too which has the additional burden
414 # of unbinding those devices
415 if driver in dpdk_drivers:
416 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
417 if os.path.exists(filename):
419 f = open(filename, "w")
421 print("Error: bind failed for %s - Cannot open %s"
422 % (dev_id, filename), file=sys.stderr)
425 f.write("%s" % driver)
428 print("Error: bind failed for %s - Cannot write driver %s to "
429 "PCI ID " % (dev_id, driver), file=sys.stderr)
431 # For kernels < 3.15 use new_id to add PCI id's to the driver
433 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
435 f = open(filename, "w")
437 print("Error: bind failed for %s - Cannot open %s"
438 % (dev_id, filename), file=sys.stderr)
441 # Convert Device and Vendor Id to int to write to new_id
442 f.write("%04x %04x" % (int(dev["Vendor"],16),
443 int(dev["Device"], 16)))
446 print("Error: bind failed for %s - Cannot write new PCI ID to "
447 "driver %s" % (dev_id, driver), file=sys.stderr)
450 # do the bind by writing to /sys
451 filename = "/sys/bus/pci/drivers/%s/bind" % driver
453 f = open(filename, "a")
455 print("Error: bind failed for %s - Cannot open %s"
456 % (dev_id, filename), file=sys.stderr)
457 if saved_driver is not None: # restore any previous driver
458 bind_one(dev_id, saved_driver, force)
464 # for some reason, closing dev_id after adding a new PCI ID to new_id
465 # results in IOError. however, if the device was successfully bound,
466 # we don't care for any errors and can safely ignore IOError
467 tmp = get_pci_device_details(dev_id, True)
468 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
470 print("Error: bind failed for %s - Cannot bind to driver %s"
471 % (dev_id, driver), file=sys.stderr)
472 if saved_driver is not None: # restore any previous driver
473 bind_one(dev_id, saved_driver, force)
476 # For kernels > 3.15 driver_override is used to bind a device to a driver.
477 # Before unbinding it, overwrite driver_override with empty string so that
478 # the device can be bound to any other driver
479 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
480 if os.path.exists(filename):
482 f = open(filename, "w")
484 sys.exit("Error: unbind failed for %s - Cannot open %s"
485 % (dev_id, filename))
490 sys.exit("Error: unbind failed for %s - Cannot open %s"
491 % (dev_id, filename))
494 def unbind_all(dev_list, force=False):
495 """Unbind method, takes a list of device locations"""
497 if dev_list[0] == "dpdk":
498 for d in devices.keys():
499 if "Driver_str" in devices[d]:
500 if devices[d]["Driver_str"] in dpdk_drivers:
501 unbind_one(devices[d]["Slot"], force)
505 dev_list = map(dev_id_from_dev_name, dev_list)
506 except ValueError as ex:
514 def bind_all(dev_list, driver, force=False):
515 """Bind method, takes a list of device locations"""
518 # a common user error is to forget to specify the driver the devices need to
519 # be bound to. check if the driver is a valid device, and if it is, show
520 # a meaningful error.
522 dev_id_from_dev_name(driver)
523 # if we've made it this far, this means that the "driver" was a valid
524 # device string, so it's probably not a valid driver name.
525 sys.exit("Error: Driver '%s' does not look like a valid driver. " \
526 "Did you forget to specify the driver to bind devices to?" % driver)
528 # driver generated error - it's not a valid device ID, so all is well
531 # check if we're attempting to bind to a driver that isn't loaded
532 if not module_is_loaded(driver.replace('-','_')):
533 sys.exit("Error: Driver '%s' is not loaded." % driver)
536 dev_list = map(dev_id_from_dev_name, dev_list)
537 except ValueError as ex:
541 bind_one(d, driver, force)
543 # For kernels < 3.15 when binding devices to a generic driver
544 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
545 # that are not bound to any other driver could be bound even if no one has
546 # asked them to. hence, we check the list of drivers again, and see if
547 # some of the previously-unbound devices were erroneously bound.
548 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
549 for d in devices.keys():
550 # skip devices that were already bound or that we know should be bound
551 if "Driver_str" in devices[d] or d in dev_list:
554 # update information about this device
555 devices[d] = dict(devices[d].items() +
556 get_pci_device_details(d, True).items())
558 # check if updated information indicates that the device was bound
559 if "Driver_str" in devices[d]:
563 def display_devices(title, dev_list, extra_params=None):
564 '''Displays to the user the details of a list of devices given in
565 "dev_list". The "extra_params" parameter, if given, should contain a string
566 with %()s fields in it for replacement by the named fields in each
567 device's dictionary.'''
568 strings = [] # this holds the strings to print. We sort before printing
569 print("\n%s" % title)
570 print("="*len(title))
571 if len(dev_list) == 0:
572 strings.append("<none>")
575 if extra_params is not None:
576 strings.append("%s '%s %s' %s" % (dev["Slot"],
581 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
582 # sort before printing, so that the entries appear in PCI order
584 print("\n".join(strings)) # print one per line
586 def show_device_status(devices_type, device_name):
592 # split our list of network devices into the three categories above
593 for d in devices.keys():
594 if device_type_match(devices[d], devices_type):
595 if not has_driver(d):
596 no_drv.append(devices[d])
598 if devices[d]["Driver_str"] in dpdk_drivers:
599 dpdk_drv.append(devices[d])
601 kernel_drv.append(devices[d])
603 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
605 # don't bother displaying anything if there are no devices
607 msg = "No '%s' devices detected" % device_name
610 print("".join('=' * len(msg)))
613 # print each category separately, so we can clearly see what's used by DPDK
614 if len(dpdk_drv) != 0:
615 display_devices("%s devices using DPDK-compatible driver" % device_name,
616 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
617 if len(kernel_drv) != 0:
618 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
619 "if=%(Interface)s drv=%(Driver_str)s "
620 "unused=%(Module_str)s %(Active)s")
622 display_devices("Other %s devices" % device_name, no_drv,
623 "unused=%(Module_str)s")
626 '''Function called when the script is passed the "--status" option.
627 Displays to the user what devices are bound to the igb_uio driver, the
628 kernel driver or to no driver'''
630 if status_dev == "net" or status_dev == "all":
631 show_device_status(network_devices, "Network")
633 if status_dev == "baseband" or status_dev == "all":
634 show_device_status(baseband_devices, "Baseband")
636 if status_dev == "crypto" or status_dev == "all":
637 show_device_status(crypto_devices, "Crypto")
639 if status_dev == "event" or status_dev == "all":
640 show_device_status(eventdev_devices, "Eventdev")
642 if status_dev == "mempool" or status_dev == "all":
643 show_device_status(mempool_devices, "Mempool")
645 if status_dev == "compress" or status_dev == "all":
646 show_device_status(compress_devices , "Compress")
648 if status_dev == "misc" or status_dev == "all":
649 show_device_status(misc_devices, "Misc (rawdev)")
652 '''Parses the command-line arguments given by the user and takes the
653 appropriate action for each'''
659 if len(sys.argv) <= 1:
664 opts, args = getopt.getopt(sys.argv[1:], "b:us",
665 ["help", "usage", "status", "status-dev=",
666 "force", "bind=", "unbind", ])
667 except getopt.GetoptError as error:
669 print("Run '%s --usage' for further information" % sys.argv[0])
672 for opt, arg in opts:
673 if opt == "--help" or opt == "--usage":
676 if opt == "--status-dev":
679 if opt == "--status" or opt == "-s":
684 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
685 if b_flag is not None:
686 sys.exit("Error: binding and unbinding are mutually exclusive")
687 if opt == "-u" or opt == "--unbind":
693 def do_arg_actions():
694 '''do the actual action requested by the user'''
700 if b_flag is None and not status_flag:
701 print("Error: No action specified for devices. "
702 "Please give a -b or -u option", file=sys.stderr)
706 if b_flag is not None and len(args) == 0:
707 print("Error: No devices specified.", file=sys.stderr)
711 if b_flag == "none" or b_flag == "None":
712 unbind_all(args, force_flag)
713 elif b_flag is not None:
714 bind_all(args, b_flag, force_flag)
716 if b_flag is not None:
718 # refresh if we have changed anything
719 get_device_details(network_devices)
720 get_device_details(baseband_devices)
721 get_device_details(crypto_devices)
722 get_device_details(eventdev_devices)
723 get_device_details(mempool_devices)
724 get_device_details(compress_devices)
725 get_device_details(misc_devices)
730 '''program main function'''
731 # check if lspci is installed, suppress any output
732 with open(os.devnull, 'w') as devnull:
733 ret = subprocess.call(['which', 'lspci'],
734 stdout=devnull, stderr=devnull)
736 sys.exit("'lspci' not found - please install 'pciutils'")
740 get_device_details(network_devices)
741 get_device_details(baseband_devices)
742 get_device_details(crypto_devices)
743 get_device_details(eventdev_devices)
744 get_device_details(mempool_devices)
745 get_device_details(compress_devices)
746 get_device_details(misc_devices)
749 if __name__ == "__main__":