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 # The PCI base class for all devices
14 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
15 'SVendor': None, 'SDevice': None}
16 acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
17 'SVendor': None, 'SDevice': None}
18 ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
19 'SVendor': None, 'SDevice': None}
20 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
21 'SVendor': None, 'SDevice': None}
22 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
23 'SVendor': None, 'SDevice': None}
24 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
25 'SVendor': None, 'SDevice': None}
26 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
27 'SVendor': None, 'SDevice': None}
28 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
29 'SVendor': None, 'SDevice': None}
30 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
31 'SVendor': None, 'SDevice': None}
32 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
33 'SVendor': None, 'SDevice': None}
34 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
35 'SVendor': None, 'SDevice': None}
37 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
38 'SVendor': None, 'SDevice': None}
39 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
40 'SVendor': None, 'SDevice': None}
41 octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
42 'SVendor': None, 'SDevice': None}
44 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
45 'SVendor': None, 'SDevice': None}
46 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
47 'SVendor': None, 'SDevice': None}
48 intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
49 'SVendor': None, 'SDevice': None}
51 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
52 baseband_devices = [acceleration_class]
53 crypto_devices = [encryption_class, intel_processor_class]
54 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
55 mempool_devices = [cavium_fpa, octeontx2_npa]
56 compress_devices = [cavium_zip]
57 misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ntb_skx, octeontx2_dma]
59 # global dict ethernet devices present. Dictionary indexed by PCI address.
60 # Each device within this is itself a dictionary of device properties
62 # list of supported DPDK drivers
63 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
64 # list of currently loaded kernel modules
67 # command-line arg flags
75 '''Print usage information for the program'''
76 argv0 = basename(sys.argv[0])
81 %(argv0)s [options] DEVICE1 DEVICE2 ....
83 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
84 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
85 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
89 Display usage information and quit
92 Print the current status of all known network, crypto, event
94 For each device, it displays the PCI domain, bus, slot and function,
95 along with a text description of the device. Depending upon whether the
96 device is being used by a kernel driver, the igb_uio driver, or no
97 driver, other relevant information will be displayed:
98 * the Linux interface name e.g. if=eth0
99 * the driver being used e.g. drv=igb_uio
100 * any suitable drivers not currently using that device
102 NOTE: if this flag is passed along with a bind/unbind option, the
103 status display will always occur after the other operations have taken
107 Print the status of given device group. Supported device groups are:
108 "net", "baseband", "crypto", "event", "mempool" and "compress"
110 -b driver, --bind=driver:
111 Select the driver to use or \"none\" to unbind the device
114 Unbind a device (Equivalent to \"-b none\")
117 By default, network devices which are used by Linux - as indicated by
118 having routes in the routing table - cannot be modified. Using the
119 --force flag overrides this behavior, allowing active links to be
121 WARNING: This can lead to loss of network connection and should be used
127 To display current device status:
130 To display current network device status:
131 %(argv0)s --status-dev net
133 To bind eth1 from the current driver and move to use igb_uio
134 %(argv0)s --bind=igb_uio eth1
136 To unbind 0000:01:00.0 from using any driver
137 %(argv0)s -u 0000:01:00.0
139 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
140 %(argv0)s -b ixgbe 02:00.0 02:00.1
142 """ % locals()) # replace items from local variables
145 # This is roughly compatible with check_output function in subprocess module
146 # which is only available in python 2.7.
147 def check_output(args, stderr=None):
148 '''Run a command and capture its output'''
149 return subprocess.Popen(args, stdout=subprocess.PIPE,
150 stderr=stderr).communicate()[0]
152 # check if a specific kernel module is loaded
153 def module_is_loaded(module):
154 global loaded_modules
156 if module == 'vfio_pci':
160 return module in loaded_modules
162 # Get list of sysfs modules (both built-in and dynamically loaded)
163 sysfs_path = '/sys/module/'
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))]
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]
173 loaded_modules = sysfs_mods
175 return module in sysfs_mods
179 '''Checks that igb_uio is loaded'''
182 # list of supported modules
183 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
185 # first check if module is loaded
187 if module_is_loaded(mod["Name"]):
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)
194 # change DPDK driver list to only contain drivers that are loaded
195 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
198 def 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]
203 def get_pci_device_details(dev_id, probe_lspci):
204 '''This function gets additional details for a PCI device'''
208 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
210 # parse lspci details
211 for line in extra_info:
214 name, value = line.decode().split("\t", 1)
215 name = name.strip(":") + "_str"
217 # check for a unix interface name
218 device["Interface"] = ""
219 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
221 device["Interface"] = \
222 ",".join(os.listdir(os.path.join(base, "net")))
224 # check if a port is used for ssh connection
225 device["Ssh_if"] = False
226 device["Active"] = ""
231 '''This function clears any old data'''
235 def get_device_details(devices_type):
236 '''This function populates the "devices" dictionary. The keys used are
237 the pci addresses (domain:bus:slot.func). The values are themselves
238 dictionaries - one for each NIC.'''
242 # first loop through and read details for all devices
243 # request machine readable format, with numeric IDs and String
245 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
246 for dev_line in dev_lines:
247 if len(dev_line) == 0:
248 if device_type_match(dev, devices_type):
249 # Replace "Driver" with "Driver_str" to have consistency of
250 # of dictionary key names
251 if "Driver" in dev.keys():
252 dev["Driver_str"] = dev.pop("Driver")
253 if "Module" in dev.keys():
254 dev["Module_str"] = dev.pop("Module")
255 # use dict to make copy of dev
256 devices[dev["Slot"]] = dict(dev)
257 # Clear previous device's data
260 name, value = dev_line.decode().split("\t", 1)
261 value_list = value.rsplit(' ', 1)
262 if len(value_list) > 1:
263 # String stored in <name>_str
264 dev[name.rstrip(":") + '_str'] = value_list[0]
266 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
267 .rstrip("]").lstrip("[")
269 if devices_type == network_devices:
270 # check what is the interface if any for an ssh connection if
271 # any to this host, so we can mark it later.
273 route = check_output(["ip", "-o", "route"])
274 # filter out all lines for 169.254 routes
275 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
276 route.decode().splitlines()))
277 rt_info = route.split()
278 for i in range(len(rt_info) - 1):
279 if rt_info[i] == "dev":
280 ssh_if.append(rt_info[i+1])
282 # based on the basic info, get extended text details
283 for d in devices.keys():
284 if not device_type_match(devices[d], devices_type):
287 # get additional info and add it to existing data
288 devices[d] = devices[d].copy()
289 # No need to probe lspci
290 devices[d].update(get_pci_device_details(d, False).items())
292 if devices_type == network_devices:
294 if _if in devices[d]["Interface"].split(","):
295 devices[d]["Ssh_if"] = True
296 devices[d]["Active"] = "*Active*"
299 # add igb_uio to list of supporting modules if needed
300 if "Module_str" in devices[d]:
301 for driver in dpdk_drivers:
302 if driver not in devices[d]["Module_str"]:
303 devices[d]["Module_str"] = \
304 devices[d]["Module_str"] + ",%s" % driver
306 devices[d]["Module_str"] = ",".join(dpdk_drivers)
308 # make sure the driver and module strings do not have any duplicates
310 modules = devices[d]["Module_str"].split(",")
311 if devices[d]["Driver_str"] in modules:
312 modules.remove(devices[d]["Driver_str"])
313 devices[d]["Module_str"] = ",".join(modules)
316 def device_type_match(dev, devices_type):
317 for i in range(len(devices_type)):
319 [x for x in devices_type[i].values() if x is not None])
321 if dev["Class"][0:2] == devices_type[i]["Class"]:
322 match_count = match_count + 1
323 for key in devices_type[i].keys():
324 if key != 'Class' and devices_type[i][key]:
325 value_list = devices_type[i][key].split(',')
326 for value in value_list:
327 if value.strip(' ') == dev[key]:
328 match_count = match_count + 1
329 # count must be the number of non None parameters to match
330 if match_count == param_count:
334 def dev_id_from_dev_name(dev_name):
335 '''Take a device "name" - a string passed in by user to identify a NIC
336 device, and determine the device id - i.e. the domain:bus:slot.func - for
337 it, which can then be used to index into the devices array'''
339 # check if it's already a suitable index
340 if dev_name in devices:
342 # check if it's an index just missing the domain part
343 elif "0000:" + dev_name in devices:
344 return "0000:" + dev_name
346 # check if it's an interface name, e.g. eth1
347 for d in devices.keys():
348 if dev_name in devices[d]["Interface"].split(","):
349 return devices[d]["Slot"]
350 # if nothing else matches - error
351 raise ValueError("Unknown device: %s. "
352 "Please specify device in \"bus:slot.func\" format" % dev_name)
355 def unbind_one(dev_id, force):
356 '''Unbind the device identified by "dev_id" from its current driver'''
357 dev = devices[dev_id]
358 if not has_driver(dev_id):
359 print("Notice: %s %s %s is not currently managed by any driver" %
360 (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr)
363 # prevent us disconnecting ourselves
364 if dev["Ssh_if"] and not force:
365 print("Warning: routing table indicates that interface %s is active. "
366 "Skipping unbind" % dev_id, file=sys.stderr)
369 # write to /sys to unbind
370 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
372 f = open(filename, "a")
374 sys.exit("Error: unbind failed for %s - Cannot open %s" %
380 def bind_one(dev_id, driver, force):
381 '''Bind the device given by "dev_id" to the driver "driver". If the device
382 is already bound to a different driver, it will be unbound first'''
383 dev = devices[dev_id]
384 saved_driver = None # used to rollback any unbind in case of failure
386 # prevent disconnection of our ssh session
387 if dev["Ssh_if"] and not force:
388 print("Warning: routing table indicates that interface %s is active. "
389 "Not modifying" % dev_id, file=sys.stderr)
392 # unbind any existing drivers we don't want
393 if has_driver(dev_id):
394 if dev["Driver_str"] == driver:
395 print("Notice: %s already bound to driver %s, skipping" %
396 (dev_id, driver), file=sys.stderr)
399 saved_driver = dev["Driver_str"]
400 unbind_one(dev_id, force)
401 dev["Driver_str"] = "" # clear driver string
403 # For kernels >= 3.15 driver_override can be used to specify the driver
404 # for a device rather than relying on the driver to provide a positive
405 # match of the device. The existing process of looking up
406 # the vendor and device ID, adding them to the driver new_id,
407 # will erroneously bind other devices too which has the additional burden
408 # of unbinding those devices
409 if driver in dpdk_drivers:
410 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
411 if os.path.exists(filename):
413 f = open(filename, "w")
415 print("Error: bind failed for %s - Cannot open %s"
416 % (dev_id, filename), file=sys.stderr)
419 f.write("%s" % driver)
422 print("Error: bind failed for %s - Cannot write driver %s to "
423 "PCI ID " % (dev_id, driver), file=sys.stderr)
425 # For kernels < 3.15 use new_id to add PCI id's to the driver
427 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
429 f = open(filename, "w")
431 print("Error: bind failed for %s - Cannot open %s"
432 % (dev_id, filename), file=sys.stderr)
435 # Convert Device and Vendor Id to int to write to new_id
436 f.write("%04x %04x" % (int(dev["Vendor"],16),
437 int(dev["Device"], 16)))
440 print("Error: bind failed for %s - Cannot write new PCI ID to "
441 "driver %s" % (dev_id, driver), file=sys.stderr)
444 # do the bind by writing to /sys
445 filename = "/sys/bus/pci/drivers/%s/bind" % driver
447 f = open(filename, "a")
449 print("Error: bind failed for %s - Cannot open %s"
450 % (dev_id, filename), file=sys.stderr)
451 if saved_driver is not None: # restore any previous driver
452 bind_one(dev_id, saved_driver, force)
458 # for some reason, closing dev_id after adding a new PCI ID to new_id
459 # results in IOError. however, if the device was successfully bound,
460 # we don't care for any errors and can safely ignore IOError
461 tmp = get_pci_device_details(dev_id, True)
462 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
464 print("Error: bind failed for %s - Cannot bind to driver %s"
465 % (dev_id, driver), file=sys.stderr)
466 if saved_driver is not None: # restore any previous driver
467 bind_one(dev_id, saved_driver, force)
470 # For kernels > 3.15 driver_override is used to bind a device to a driver.
471 # Before unbinding it, overwrite driver_override with empty string so that
472 # the device can be bound to any other driver
473 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
474 if os.path.exists(filename):
476 f = open(filename, "w")
478 sys.exit("Error: unbind failed for %s - Cannot open %s"
479 % (dev_id, filename))
484 sys.exit("Error: unbind failed for %s - Cannot open %s"
485 % (dev_id, filename))
488 def unbind_all(dev_list, force=False):
489 """Unbind method, takes a list of device locations"""
491 if dev_list[0] == "dpdk":
492 for d in devices.keys():
493 if "Driver_str" in devices[d]:
494 if devices[d]["Driver_str"] in dpdk_drivers:
495 unbind_one(devices[d]["Slot"], force)
499 dev_list = map(dev_id_from_dev_name, dev_list)
500 except ValueError as ex:
508 def bind_all(dev_list, driver, force=False):
509 """Bind method, takes a list of device locations"""
512 # a common user error is to forget to specify the driver the devices need to
513 # be bound to. check if the driver is a valid device, and if it is, show
514 # a meaningful error.
516 dev_id_from_dev_name(driver)
517 # if we've made it this far, this means that the "driver" was a valid
518 # device string, so it's probably not a valid driver name.
519 sys.exit("Error: Driver '%s' does not look like a valid driver. " \
520 "Did you forget to specify the driver to bind devices to?" % driver)
522 # driver generated error - it's not a valid device ID, so all is well
525 # check if we're attempting to bind to a driver that isn't loaded
526 if not module_is_loaded(driver.replace('-','_')):
527 sys.exit("Error: Driver '%s' is not loaded." % driver)
530 dev_list = map(dev_id_from_dev_name, dev_list)
531 except ValueError as ex:
535 bind_one(d, driver, force)
537 # For kernels < 3.15 when binding devices to a generic driver
538 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
539 # that are not bound to any other driver could be bound even if no one has
540 # asked them to. hence, we check the list of drivers again, and see if
541 # some of the previously-unbound devices were erroneously bound.
542 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
543 for d in devices.keys():
544 # skip devices that were already bound or that we know should be bound
545 if "Driver_str" in devices[d] or d in dev_list:
548 # update information about this device
549 devices[d] = dict(devices[d].items() +
550 get_pci_device_details(d, True).items())
552 # check if updated information indicates that the device was bound
553 if "Driver_str" in devices[d]:
557 def display_devices(title, dev_list, extra_params=None):
558 '''Displays to the user the details of a list of devices given in
559 "dev_list". The "extra_params" parameter, if given, should contain a string
560 with %()s fields in it for replacement by the named fields in each
561 device's dictionary.'''
562 strings = [] # this holds the strings to print. We sort before printing
563 print("\n%s" % title)
564 print("="*len(title))
565 if len(dev_list) == 0:
566 strings.append("<none>")
569 if extra_params is not None:
570 strings.append("%s '%s %s' %s" % (dev["Slot"],
575 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
576 # sort before printing, so that the entries appear in PCI order
578 print("\n".join(strings)) # print one per line
580 def show_device_status(devices_type, device_name):
586 # split our list of network devices into the three categories above
587 for d in devices.keys():
588 if device_type_match(devices[d], devices_type):
589 if not has_driver(d):
590 no_drv.append(devices[d])
592 if devices[d]["Driver_str"] in dpdk_drivers:
593 dpdk_drv.append(devices[d])
595 kernel_drv.append(devices[d])
597 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
599 # don't bother displaying anything if there are no devices
601 msg = "No '%s' devices detected" % device_name
604 print("".join('=' * len(msg)))
607 # print each category separately, so we can clearly see what's used by DPDK
608 if len(dpdk_drv) != 0:
609 display_devices("%s devices using DPDK-compatible driver" % device_name,
610 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
611 if len(kernel_drv) != 0:
612 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
613 "if=%(Interface)s drv=%(Driver_str)s "
614 "unused=%(Module_str)s %(Active)s")
616 display_devices("Other %s devices" % device_name, no_drv,
617 "unused=%(Module_str)s")
620 '''Function called when the script is passed the "--status" option.
621 Displays to the user what devices are bound to the igb_uio driver, the
622 kernel driver or to no driver'''
624 if status_dev == "net" or status_dev == "all":
625 show_device_status(network_devices, "Network")
627 if status_dev == "baseband" or status_dev == "all":
628 show_device_status(baseband_devices, "Baseband")
630 if status_dev == "crypto" or status_dev == "all":
631 show_device_status(crypto_devices, "Crypto")
633 if status_dev == "event" or status_dev == "all":
634 show_device_status(eventdev_devices, "Eventdev")
636 if status_dev == "mempool" or status_dev == "all":
637 show_device_status(mempool_devices, "Mempool")
639 if status_dev == "compress" or status_dev == "all":
640 show_device_status(compress_devices , "Compress")
642 if status_dev == "misc" or status_dev == "all":
643 show_device_status(misc_devices, "Misc (rawdev)")
646 '''Parses the command-line arguments given by the user and takes the
647 appropriate action for each'''
653 if len(sys.argv) <= 1:
658 opts, args = getopt.getopt(sys.argv[1:], "b:us",
659 ["help", "usage", "status", "status-dev=",
660 "force", "bind=", "unbind", ])
661 except getopt.GetoptError as error:
663 print("Run '%s --usage' for further information" % sys.argv[0])
666 for opt, arg in opts:
667 if opt == "--help" or opt == "--usage":
670 if opt == "--status-dev":
673 if opt == "--status" or opt == "-s":
678 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
679 if b_flag is not None:
680 sys.exit("Error: binding and unbinding are mutually exclusive")
681 if opt == "-u" or opt == "--unbind":
687 def do_arg_actions():
688 '''do the actual action requested by the user'''
694 if b_flag is None and not status_flag:
695 print("Error: No action specified for devices. "
696 "Please give a -b or -u option", file=sys.stderr)
700 if b_flag is not None and len(args) == 0:
701 print("Error: No devices specified.", file=sys.stderr)
705 if b_flag == "none" or b_flag == "None":
706 unbind_all(args, force_flag)
707 elif b_flag is not None:
708 bind_all(args, b_flag, force_flag)
710 if b_flag is not None:
712 # refresh if we have changed anything
713 get_device_details(network_devices)
714 get_device_details(baseband_devices)
715 get_device_details(crypto_devices)
716 get_device_details(eventdev_devices)
717 get_device_details(mempool_devices)
718 get_device_details(compress_devices)
719 get_device_details(misc_devices)
724 '''program main function'''
725 # check if lspci is installed, suppress any output
726 with open(os.devnull, 'w') as devnull:
727 ret = subprocess.call(['which', 'lspci'],
728 stdout=devnull, stderr=devnull)
730 sys.exit("'lspci' not found - please install 'pciutils'")
734 get_device_details(network_devices)
735 get_device_details(baseband_devices)
736 get_device_details(crypto_devices)
737 get_device_details(eventdev_devices)
738 get_device_details(mempool_devices)
739 get_device_details(compress_devices)
740 get_device_details(misc_devices)
743 if __name__ == "__main__":