2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2010-2014 Intel Corporation
10 from os.path import exists, abspath, dirname, basename
12 # The PCI base class for all devices
13 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
14 'SVendor': None, 'SDevice': None}
15 acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
16 'SVendor': None, 'SDevice': None}
17 ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
18 'SVendor': None, 'SDevice': None}
19 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
20 'SVendor': None, 'SDevice': None}
21 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
22 'SVendor': None, 'SDevice': None}
23 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
24 'SVendor': None, 'SDevice': None}
25 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
26 'SVendor': None, 'SDevice': None}
27 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
28 'SVendor': None, 'SDevice': None}
29 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
30 'SVendor': None, 'SDevice': None}
31 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
32 'SVendor': None, 'SDevice': None}
33 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
34 'SVendor': None, 'SDevice': None}
36 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
37 'SVendor': None, 'SDevice': None}
38 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
39 'SVendor': None, 'SDevice': None}
40 octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
41 'SVendor': None, 'SDevice': None}
43 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
44 'SVendor': None, 'SDevice': None}
45 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
46 'SVendor': None, 'SDevice': None}
47 intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
48 'SVendor': None, 'SDevice': None}
50 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
51 baseband_devices = [acceleration_class]
52 crypto_devices = [encryption_class, intel_processor_class]
53 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
54 mempool_devices = [cavium_fpa, octeontx2_npa]
55 compress_devices = [cavium_zip]
56 misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ntb_skx, octeontx2_dma]
58 # global dict ethernet devices present. Dictionary indexed by PCI address.
59 # Each device within this is itself a dictionary of device properties
61 # list of supported DPDK drivers
62 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
64 # command-line arg flags
72 '''Print usage information for the program'''
73 argv0 = basename(sys.argv[0])
78 %(argv0)s [options] DEVICE1 DEVICE2 ....
80 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
81 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
82 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
86 Display usage information and quit
89 Print the current status of all known network, crypto, event
91 For each device, it displays the PCI domain, bus, slot and function,
92 along with a text description of the device. Depending upon whether the
93 device is being used by a kernel driver, the igb_uio driver, or no
94 driver, other relevant information will be displayed:
95 * the Linux interface name e.g. if=eth0
96 * the driver being used e.g. drv=igb_uio
97 * any suitable drivers not currently using that device
99 NOTE: if this flag is passed along with a bind/unbind option, the
100 status display will always occur after the other operations have taken
104 Print the status of given device group. Supported device groups are:
105 "net", "baseband", "crypto", "event", "mempool" and "compress"
107 -b driver, --bind=driver:
108 Select the driver to use or \"none\" to unbind the device
111 Unbind a device (Equivalent to \"-b none\")
114 By default, network devices which are used by Linux - as indicated by
115 having routes in the routing table - cannot be modified. Using the
116 --force flag overrides this behavior, allowing active links to be
118 WARNING: This can lead to loss of network connection and should be used
124 To display current device status:
127 To display current network device status:
128 %(argv0)s --status-dev net
130 To bind eth1 from the current driver and move to use igb_uio
131 %(argv0)s --bind=igb_uio eth1
133 To unbind 0000:01:00.0 from using any driver
134 %(argv0)s -u 0000:01:00.0
136 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
137 %(argv0)s -b ixgbe 02:00.0 02:00.1
139 """ % locals()) # replace items from local variables
142 # This is roughly compatible with check_output function in subprocess module
143 # which is only available in python 2.7.
144 def check_output(args, stderr=None):
145 '''Run a command and capture its output'''
146 return subprocess.Popen(args, stdout=subprocess.PIPE,
147 stderr=stderr).communicate()[0]
151 '''Checks that igb_uio is loaded'''
154 # list of supported modules
155 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
157 # first check if module is loaded
159 # Get list of sysfs modules (both built-in and dynamically loaded)
160 sysfs_path = '/sys/module/'
162 # Get the list of directories in sysfs_path
163 sysfs_mods = [os.path.join(sysfs_path, o) for o
164 in os.listdir(sysfs_path)
165 if os.path.isdir(os.path.join(sysfs_path, o))]
167 # Extract the last element of '/sys/module/abc' in the array
168 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
170 # special case for vfio_pci (module is named vfio-pci,
171 # but its .ko is named vfio_pci)
172 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
175 if mod["Name"] in sysfs_mods:
180 # check if we have at least one loaded module
181 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
182 if b_flag in dpdk_drivers:
183 print("Error - no supported modules(DPDK driver) are loaded")
186 print("Warning - no supported modules(DPDK driver) are loaded")
188 # change DPDK driver list to only contain drivers that are loaded
189 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
192 def has_driver(dev_id):
193 '''return true if a device is assigned to a driver. False otherwise'''
194 return "Driver_str" in devices[dev_id]
197 def get_pci_device_details(dev_id, probe_lspci):
198 '''This function gets additional details for a PCI device'''
202 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
204 # parse lspci details
205 for line in extra_info:
208 name, value = line.decode().split("\t", 1)
209 name = name.strip(":") + "_str"
211 # check for a unix interface name
212 device["Interface"] = ""
213 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
215 device["Interface"] = \
216 ",".join(os.listdir(os.path.join(base, "net")))
218 # check if a port is used for ssh connection
219 device["Ssh_if"] = False
220 device["Active"] = ""
225 '''This function clears any old data'''
229 def get_device_details(devices_type):
230 '''This function populates the "devices" dictionary. The keys used are
231 the pci addresses (domain:bus:slot.func). The values are themselves
232 dictionaries - one for each NIC.'''
236 # first loop through and read details for all devices
237 # request machine readable format, with numeric IDs and String
239 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
240 for dev_line in dev_lines:
241 if len(dev_line) == 0:
242 if device_type_match(dev, devices_type):
243 # Replace "Driver" with "Driver_str" to have consistency of
244 # of dictionary key names
245 if "Driver" in dev.keys():
246 dev["Driver_str"] = dev.pop("Driver")
247 if "Module" in dev.keys():
248 dev["Module_str"] = dev.pop("Module")
249 # use dict to make copy of dev
250 devices[dev["Slot"]] = dict(dev)
251 # Clear previous device's data
254 name, value = dev_line.decode().split("\t", 1)
255 value_list = value.rsplit(' ', 1)
256 if len(value_list) > 1:
257 # String stored in <name>_str
258 dev[name.rstrip(":") + '_str'] = value_list[0]
260 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
261 .rstrip("]").lstrip("[")
263 if devices_type == network_devices:
264 # check what is the interface if any for an ssh connection if
265 # any to this host, so we can mark it later.
267 route = check_output(["ip", "-o", "route"])
268 # filter out all lines for 169.254 routes
269 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
270 route.decode().splitlines()))
271 rt_info = route.split()
272 for i in range(len(rt_info) - 1):
273 if rt_info[i] == "dev":
274 ssh_if.append(rt_info[i+1])
276 # based on the basic info, get extended text details
277 for d in devices.keys():
278 if not device_type_match(devices[d], devices_type):
281 # get additional info and add it to existing data
282 devices[d] = devices[d].copy()
283 # No need to probe lspci
284 devices[d].update(get_pci_device_details(d, False).items())
286 if devices_type == network_devices:
288 if _if in devices[d]["Interface"].split(","):
289 devices[d]["Ssh_if"] = True
290 devices[d]["Active"] = "*Active*"
293 # add igb_uio to list of supporting modules if needed
294 if "Module_str" in devices[d]:
295 for driver in dpdk_drivers:
296 if driver not in devices[d]["Module_str"]:
297 devices[d]["Module_str"] = \
298 devices[d]["Module_str"] + ",%s" % driver
300 devices[d]["Module_str"] = ",".join(dpdk_drivers)
302 # make sure the driver and module strings do not have any duplicates
304 modules = devices[d]["Module_str"].split(",")
305 if devices[d]["Driver_str"] in modules:
306 modules.remove(devices[d]["Driver_str"])
307 devices[d]["Module_str"] = ",".join(modules)
310 def device_type_match(dev, devices_type):
311 for i in range(len(devices_type)):
313 [x for x in devices_type[i].values() if x is not None])
315 if dev["Class"][0:2] == devices_type[i]["Class"]:
316 match_count = match_count + 1
317 for key in devices_type[i].keys():
318 if key != 'Class' and devices_type[i][key]:
319 value_list = devices_type[i][key].split(',')
320 for value in value_list:
321 if value.strip(' ') == dev[key]:
322 match_count = match_count + 1
323 # count must be the number of non None parameters to match
324 if match_count == param_count:
328 def dev_id_from_dev_name(dev_name):
329 '''Take a device "name" - a string passed in by user to identify a NIC
330 device, and determine the device id - i.e. the domain:bus:slot.func - for
331 it, which can then be used to index into the devices array'''
333 # check if it's already a suitable index
334 if dev_name in devices:
336 # check if it's an index just missing the domain part
337 elif "0000:" + dev_name in devices:
338 return "0000:" + dev_name
340 # check if it's an interface name, e.g. eth1
341 for d in devices.keys():
342 if dev_name in devices[d]["Interface"].split(","):
343 return devices[d]["Slot"]
344 # if nothing else matches - error
345 raise ValueError("Unknown device: %s. "
346 "Please specify device in \"bus:slot.func\" format" % dev_name)
349 def unbind_one(dev_id, force):
350 '''Unbind the device identified by "dev_id" from its current driver'''
351 dev = devices[dev_id]
352 if not has_driver(dev_id):
353 print("%s %s %s is not currently managed by any driver\n" %
354 (dev["Slot"], dev["Device_str"], dev["Interface"]))
357 # prevent us disconnecting ourselves
358 if dev["Ssh_if"] and not force:
359 print("Routing table indicates that interface %s is active. "
360 "Skipping unbind" % (dev_id))
363 # write to /sys to unbind
364 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
366 f = open(filename, "a")
368 print("Error: unbind failed for %s - Cannot open %s"
369 % (dev_id, filename))
375 def bind_one(dev_id, driver, force):
376 '''Bind the device given by "dev_id" to the driver "driver". If the device
377 is already bound to a different driver, it will be unbound first'''
378 dev = devices[dev_id]
379 saved_driver = None # used to rollback any unbind in case of failure
381 # prevent disconnection of our ssh session
382 if dev["Ssh_if"] and not force:
383 print("Routing table indicates that interface %s is active. "
384 "Not modifying" % (dev_id))
387 # unbind any existing drivers we don't want
388 if has_driver(dev_id):
389 if dev["Driver_str"] == driver:
390 print("%s already bound to driver %s, skipping\n"
394 saved_driver = dev["Driver_str"]
395 unbind_one(dev_id, force)
396 dev["Driver_str"] = "" # clear driver string
398 # For kernels >= 3.15 driver_override can be used to specify the driver
399 # for a device rather than relying on the driver to provide a positive
400 # match of the device. The existing process of looking up
401 # the vendor and device ID, adding them to the driver new_id,
402 # will erroneously bind other devices too which has the additional burden
403 # of unbinding those devices
404 if driver in dpdk_drivers:
405 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
406 if os.path.exists(filename):
408 f = open(filename, "w")
410 print("Error: bind failed for %s - Cannot open %s"
411 % (dev_id, filename))
414 f.write("%s" % driver)
417 print("Error: bind failed for %s - Cannot write driver %s to "
418 "PCI ID " % (dev_id, driver))
420 # For kernels < 3.15 use new_id to add PCI id's to the driver
422 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
424 f = open(filename, "w")
426 print("Error: bind failed for %s - Cannot open %s"
427 % (dev_id, filename))
430 # Convert Device and Vendor Id to int to write to new_id
431 f.write("%04x %04x" % (int(dev["Vendor"],16),
432 int(dev["Device"], 16)))
435 print("Error: bind failed for %s - Cannot write new PCI ID to "
436 "driver %s" % (dev_id, driver))
439 # do the bind by writing to /sys
440 filename = "/sys/bus/pci/drivers/%s/bind" % driver
442 f = open(filename, "a")
444 print("Error: bind failed for %s - Cannot open %s"
445 % (dev_id, filename))
446 if saved_driver is not None: # restore any previous driver
447 bind_one(dev_id, saved_driver, force)
453 # for some reason, closing dev_id after adding a new PCI ID to new_id
454 # results in IOError. however, if the device was successfully bound,
455 # we don't care for any errors and can safely ignore IOError
456 tmp = get_pci_device_details(dev_id, True)
457 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
459 print("Error: bind failed for %s - Cannot bind to driver %s"
461 if saved_driver is not None: # restore any previous driver
462 bind_one(dev_id, saved_driver, force)
465 # For kernels > 3.15 driver_override is used to bind a device to a driver.
466 # Before unbinding it, overwrite driver_override with empty string so that
467 # the device can be bound to any other driver
468 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
469 if os.path.exists(filename):
471 f = open(filename, "w")
473 print("Error: unbind failed for %s - Cannot open %s"
474 % (dev_id, filename))
480 print("Error: unbind failed for %s - Cannot open %s"
481 % (dev_id, filename))
485 def unbind_all(dev_list, force=False):
486 """Unbind method, takes a list of device locations"""
488 if dev_list[0] == "dpdk":
489 for d in devices.keys():
490 if "Driver_str" in devices[d]:
491 if devices[d]["Driver_str"] in dpdk_drivers:
492 unbind_one(devices[d]["Slot"], force)
496 dev_list = map(dev_id_from_dev_name, dev_list)
497 except ValueError as ex:
505 def bind_all(dev_list, driver, force=False):
506 """Bind method, takes a list of device locations"""
509 # a common user error is to forget to specify the driver the devices need to
510 # be bound to. check if the driver is a valid device, and if it is, show
511 # a meaningful error.
513 dev_id_from_dev_name(driver)
514 # if we've made it this far, this means that the "driver" was a valid
515 # device string, so it's probably not a valid driver name.
516 sys.exit("Error: Driver '%s' does not look like a valid driver. " \
517 "Did you forget to specify the driver to bind devices to?" % driver)
519 # driver generated error - it's not a valid device ID, so all is well
523 dev_list = map(dev_id_from_dev_name, dev_list)
524 except ValueError as ex:
528 bind_one(d, driver, force)
530 # For kernels < 3.15 when binding devices to a generic driver
531 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
532 # that are not bound to any other driver could be bound even if no one has
533 # asked them to. hence, we check the list of drivers again, and see if
534 # some of the previously-unbound devices were erroneously bound.
535 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
536 for d in devices.keys():
537 # skip devices that were already bound or that we know should be bound
538 if "Driver_str" in devices[d] or d in dev_list:
541 # update information about this device
542 devices[d] = dict(devices[d].items() +
543 get_pci_device_details(d, True).items())
545 # check if updated information indicates that the device was bound
546 if "Driver_str" in devices[d]:
550 def display_devices(title, dev_list, extra_params=None):
551 '''Displays to the user the details of a list of devices given in
552 "dev_list". The "extra_params" parameter, if given, should contain a string
553 with %()s fields in it for replacement by the named fields in each
554 device's dictionary.'''
555 strings = [] # this holds the strings to print. We sort before printing
556 print("\n%s" % title)
557 print("="*len(title))
558 if len(dev_list) == 0:
559 strings.append("<none>")
562 if extra_params is not None:
563 strings.append("%s '%s %s' %s" % (dev["Slot"],
568 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
569 # sort before printing, so that the entries appear in PCI order
571 print("\n".join(strings)) # print one per line
573 def show_device_status(devices_type, device_name):
579 # split our list of network devices into the three categories above
580 for d in devices.keys():
581 if device_type_match(devices[d], devices_type):
582 if not has_driver(d):
583 no_drv.append(devices[d])
585 if devices[d]["Driver_str"] in dpdk_drivers:
586 dpdk_drv.append(devices[d])
588 kernel_drv.append(devices[d])
590 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
592 # don't bother displaying anything if there are no devices
594 msg = "No '%s' devices detected" % device_name
597 print("".join('=' * len(msg)))
600 # print each category separately, so we can clearly see what's used by DPDK
601 if len(dpdk_drv) != 0:
602 display_devices("%s devices using DPDK-compatible driver" % device_name,
603 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
604 if len(kernel_drv) != 0:
605 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
606 "if=%(Interface)s drv=%(Driver_str)s "
607 "unused=%(Module_str)s %(Active)s")
609 display_devices("Other %s devices" % device_name, no_drv,
610 "unused=%(Module_str)s")
613 '''Function called when the script is passed the "--status" option.
614 Displays to the user what devices are bound to the igb_uio driver, the
615 kernel driver or to no driver'''
617 if status_dev == "net" or status_dev == "all":
618 show_device_status(network_devices, "Network")
620 if status_dev == "baseband" or status_dev == "all":
621 show_device_status(baseband_devices, "Baseband")
623 if status_dev == "crypto" or status_dev == "all":
624 show_device_status(crypto_devices, "Crypto")
626 if status_dev == "event" or status_dev == "all":
627 show_device_status(eventdev_devices, "Eventdev")
629 if status_dev == "mempool" or status_dev == "all":
630 show_device_status(mempool_devices, "Mempool")
632 if status_dev == "compress" or status_dev == "all":
633 show_device_status(compress_devices , "Compress")
635 if status_dev == "misc" or status_dev == "all":
636 show_device_status(misc_devices, "Misc (rawdev)")
639 '''Parses the command-line arguments given by the user and takes the
640 appropriate action for each'''
646 if len(sys.argv) <= 1:
651 opts, args = getopt.getopt(sys.argv[1:], "b:us",
652 ["help", "usage", "status", "status-dev=",
653 "force", "bind=", "unbind", ])
654 except getopt.GetoptError as error:
656 print("Run '%s --usage' for further information" % sys.argv[0])
659 for opt, arg in opts:
660 if opt == "--help" or opt == "--usage":
663 if opt == "--status-dev":
666 if opt == "--status" or opt == "-s":
671 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
672 if b_flag is not None:
673 print("Error - Only one bind or unbind may be specified\n")
675 if opt == "-u" or opt == "--unbind":
681 def do_arg_actions():
682 '''do the actual action requested by the user'''
688 if b_flag is None and not status_flag:
689 print("Error: No action specified for devices."
690 "Please give a -b or -u option")
691 print("Run '%s --usage' for further information" % sys.argv[0])
694 if b_flag is not None and len(args) == 0:
695 print("Error: No devices specified.")
696 print("Run '%s --usage' for further information" % sys.argv[0])
699 if b_flag == "none" or b_flag == "None":
700 unbind_all(args, force_flag)
701 elif b_flag is not None:
702 bind_all(args, b_flag, force_flag)
704 if b_flag is not None:
706 # refresh if we have changed anything
707 get_device_details(network_devices)
708 get_device_details(baseband_devices)
709 get_device_details(crypto_devices)
710 get_device_details(eventdev_devices)
711 get_device_details(mempool_devices)
712 get_device_details(compress_devices)
713 get_device_details(misc_devices)
718 '''program main function'''
719 # check if lspci is installed, suppress any output
720 with open(os.devnull, 'w') as devnull:
721 ret = subprocess.call(['which', 'lspci'],
722 stdout=devnull, stderr=devnull)
724 print("'lspci' not found - please install 'pciutils'")
729 get_device_details(network_devices)
730 get_device_details(baseband_devices)
731 get_device_details(crypto_devices)
732 get_device_details(eventdev_devices)
733 get_device_details(mempool_devices)
734 get_device_details(compress_devices)
735 get_device_details(misc_devices)
738 if __name__ == "__main__":