2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2010-2014 Intel Corporation
11 from os.path import exists, abspath, dirname, basename
12 from os.path import join as path_join
14 # The PCI base class for all devices
15 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
16 'SVendor': None, 'SDevice': None}
17 acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
18 'SVendor': None, 'SDevice': None}
19 ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
20 'SVendor': None, 'SDevice': None}
21 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
22 'SVendor': None, 'SDevice': None}
23 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
24 'SVendor': None, 'SDevice': None}
25 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
26 'SVendor': None, 'SDevice': None}
27 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
28 'SVendor': None, 'SDevice': None}
29 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
30 'SVendor': None, 'SDevice': None}
31 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
32 'SVendor': None, 'SDevice': None}
33 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
34 'SVendor': None, 'SDevice': None}
35 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
36 'SVendor': None, 'SDevice': None}
38 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
39 'SVendor': None, 'SDevice': None}
40 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
41 'SVendor': None, 'SDevice': None}
42 octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
43 'SVendor': None, 'SDevice': None}
45 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
46 'SVendor': None, 'SDevice': None}
47 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
48 'SVendor': None, 'SDevice': None}
49 intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00',
50 'SVendor': None, 'SDevice': None}
51 intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
52 'SVendor': None, 'SDevice': None}
53 intel_ntb_icx = {'Class': '06', 'Vendor': '8086', 'Device': '347e',
54 'SVendor': None, 'SDevice': None}
56 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
57 baseband_devices = [acceleration_class]
58 crypto_devices = [encryption_class, intel_processor_class]
59 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
60 mempool_devices = [cavium_fpa, octeontx2_npa]
61 compress_devices = [cavium_zip]
62 misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ioat_icx, intel_ntb_skx, intel_ntb_icx, octeontx2_dma]
64 # global dict ethernet devices present. Dictionary indexed by PCI address.
65 # Each device within this is itself a dictionary of device properties
67 # list of supported DPDK drivers
68 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
69 # list of currently loaded kernel modules
72 # command-line arg flags
80 '''Print usage information for the program'''
81 argv0 = basename(sys.argv[0])
86 %(argv0)s [options] DEVICE1 DEVICE2 ....
88 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
89 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
90 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
91 If devices are specified using PCI <domain:>bus:device:func format, then
92 shell wildcards and ranges may be used, e.g. 80:04.*, 80:04.[0-3]
96 Display usage information and quit
99 Print the current status of all known network, crypto, event
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
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
114 Print the status of given device group. Supported device groups are:
115 "net", "baseband", "crypto", "event", "mempool" and "compress"
117 -b driver, --bind=driver:
118 Select the driver to use or \"none\" to unbind the device
121 Unbind a device (Equivalent to \"-b none\")
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
128 WARNING: This can lead to loss of network connection and should be used
134 To display current device status:
137 To display current network device status:
138 %(argv0)s --status-dev net
140 To bind eth1 from the current driver and move to use igb_uio
141 %(argv0)s --bind=igb_uio eth1
143 To unbind 0000:01:00.0 from using any driver
144 %(argv0)s -u 0000:01:00.0
146 To 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
149 To bind all functions on device 0000:02:00 to ixgbe kernel driver
150 %(argv0)s -b ixgbe 02:00.*
152 """ % locals()) # replace items from local variables
154 # check if a specific kernel module is loaded
155 def module_is_loaded(module):
156 global loaded_modules
158 if module == 'vfio_pci':
162 return module in loaded_modules
164 # Get list of sysfs modules (both built-in and dynamically loaded)
165 sysfs_path = '/sys/module/'
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))]
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]
175 loaded_modules = sysfs_mods
177 return module in sysfs_mods
181 '''Checks that igb_uio is loaded'''
184 # list of supported modules
185 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
187 # first check if module is loaded
189 if module_is_loaded(mod["Name"]):
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)
196 # change DPDK driver list to only contain drivers that are loaded
197 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
200 def 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]
205 def get_pci_device_details(dev_id, probe_lspci):
206 '''This function gets additional details for a PCI device'''
210 extra_info = subprocess.check_output(["lspci", "-vmmks", dev_id]).splitlines()
211 # parse lspci details
212 for line in extra_info:
215 name, value = line.decode("utf8").split("\t", 1)
216 name = name.strip(":") + "_str"
218 # check for a unix interface name
219 device["Interface"] = ""
220 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
222 device["Interface"] = \
223 ",".join(os.listdir(os.path.join(base, "net")))
225 # check if a port is used for ssh connection
226 device["Ssh_if"] = False
227 device["Active"] = ""
232 '''This function clears any old data'''
236 def 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.'''
243 # first loop through and read details for all devices
244 # request machine readable format, with numeric IDs and String
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
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]
267 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
268 .rstrip("]").lstrip("[")
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.
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])
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):
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())
293 if devices_type == network_devices:
295 if _if in devices[d]["Interface"].split(","):
296 devices[d]["Ssh_if"] = True
297 devices[d]["Active"] = "*Active*"
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
307 devices[d]["Module_str"] = ",".join(dpdk_drivers)
309 # make sure the driver and module strings do not have any duplicates
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)
317 def device_type_match(dev, devices_type):
318 for i in range(len(devices_type)):
320 [x for x in devices_type[i].values() if x is not None])
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:
335 def 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'''
340 # check if it's already a suitable index
341 if dev_name in devices:
343 # check if it's an index just missing the domain part
344 elif "0000:" + dev_name in devices:
345 return "0000:" + dev_name
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)
356 def 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)
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)
370 # write to /sys to unbind
371 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
373 f = open(filename, "a")
375 sys.exit("Error: unbind failed for %s - Cannot open %s" %
381 def 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
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)
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)
400 saved_driver = dev["Driver_str"]
401 unbind_one(dev_id, force)
402 dev["Driver_str"] = "" # clear driver string
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):
414 f = open(filename, "w")
416 print("Error: bind failed for %s - Cannot open %s"
417 % (dev_id, filename), file=sys.stderr)
420 f.write("%s" % driver)
423 print("Error: bind failed for %s - Cannot write driver %s to "
424 "PCI ID " % (dev_id, driver), file=sys.stderr)
426 # For kernels < 3.15 use new_id to add PCI id's to the driver
428 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
430 f = open(filename, "w")
432 print("Error: bind failed for %s - Cannot open %s"
433 % (dev_id, filename), file=sys.stderr)
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)))
441 print("Error: bind failed for %s - Cannot write new PCI ID to "
442 "driver %s" % (dev_id, driver), file=sys.stderr)
445 # do the bind by writing to /sys
446 filename = "/sys/bus/pci/drivers/%s/bind" % driver
448 f = open(filename, "a")
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)
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:
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)
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):
477 f = open(filename, "w")
479 sys.exit("Error: unbind failed for %s - Cannot open %s"
480 % (dev_id, filename))
485 sys.exit("Error: unbind failed for %s - Cannot open %s"
486 % (dev_id, filename))
489 def unbind_all(dev_list, force=False):
490 """Unbind method, takes a list of device locations"""
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)
500 dev_list = map(dev_id_from_dev_name, dev_list)
501 except ValueError as ex:
509 def bind_all(dev_list, driver, force=False):
510 """Bind method, takes a list of device locations"""
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.
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)
523 # driver generated error - it's not a valid device ID, so all is well
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)
531 dev_list = map(dev_id_from_dev_name, dev_list)
532 except ValueError as ex:
536 bind_one(d, driver, force)
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:
549 # update information about this device
550 devices[d] = dict(devices[d].items() +
551 get_pci_device_details(d, True).items())
553 # check if updated information indicates that the device was bound
554 if "Driver_str" in devices[d]:
558 def 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>")
570 if extra_params is not None:
571 strings.append("%s '%s %s' %s" % (dev["Slot"],
576 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
577 # sort before printing, so that the entries appear in PCI order
579 print("\n".join(strings)) # print one per line
581 def show_device_status(devices_type, device_name, if_field=False):
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])
593 if devices[d]["Driver_str"] in dpdk_drivers:
594 dpdk_drv.append(devices[d])
596 kernel_drv.append(devices[d])
598 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
600 # don't bother displaying anything if there are no devices
602 msg = "No '%s' devices detected" % device_name
605 print("".join('=' * len(msg)))
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:
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")
620 display_devices("Other %s devices" % device_name, no_drv,
621 "unused=%(Module_str)s")
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'''
628 if status_dev == "net" or status_dev == "all":
629 show_device_status(network_devices, "Network", if_field=True)
631 if status_dev == "baseband" or status_dev == "all":
632 show_device_status(baseband_devices, "Baseband")
634 if status_dev == "crypto" or status_dev == "all":
635 show_device_status(crypto_devices, "Crypto")
637 if status_dev == "event" or status_dev == "all":
638 show_device_status(eventdev_devices, "Eventdev")
640 if status_dev == "mempool" or status_dev == "all":
641 show_device_status(mempool_devices, "Mempool")
643 if status_dev == "compress" or status_dev == "all":
644 show_device_status(compress_devices , "Compress")
646 if status_dev == "misc" or status_dev == "all":
647 show_device_status(misc_devices, "Misc (rawdev)")
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))]
663 '''Parses the command-line arguments given by the user and takes the
664 appropriate action for each'''
670 if len(sys.argv) <= 1:
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:
680 print("Run '%s --usage' for further information" % sys.argv[0])
683 for opt, arg in opts:
684 if opt == "--help" or opt == "--usage":
687 if opt == "--status-dev":
690 if opt == "--status" or opt == "-s":
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":
703 # resolve any PCI globs in the args
706 new_args.extend(pci_glob(arg))
709 def do_arg_actions():
710 '''do the actual action requested by the user'''
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)
722 if b_flag is not None and len(args) == 0:
723 print("Error: No devices specified.", file=sys.stderr)
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)
732 if b_flag is not None:
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)
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)
752 sys.exit("'lspci' not found - please install 'pciutils'")
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)
765 if __name__ == "__main__":