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 print("Unknown device: %s. "
346 "Please specify device in \"bus:slot.func\" format" % dev_name)
350 def unbind_one(dev_id, force):
351 '''Unbind the device identified by "dev_id" from its current driver'''
352 dev = devices[dev_id]
353 if not has_driver(dev_id):
354 print("%s %s %s is not currently managed by any driver\n" %
355 (dev["Slot"], dev["Device_str"], dev["Interface"]))
358 # prevent us disconnecting ourselves
359 if dev["Ssh_if"] and not force:
360 print("Routing table indicates that interface %s is active. "
361 "Skipping unbind" % (dev_id))
364 # write to /sys to unbind
365 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
367 f = open(filename, "a")
369 print("Error: unbind failed for %s - Cannot open %s"
370 % (dev_id, filename))
376 def bind_one(dev_id, driver, force):
377 '''Bind the device given by "dev_id" to the driver "driver". If the device
378 is already bound to a different driver, it will be unbound first'''
379 dev = devices[dev_id]
380 saved_driver = None # used to rollback any unbind in case of failure
382 # prevent disconnection of our ssh session
383 if dev["Ssh_if"] and not force:
384 print("Routing table indicates that interface %s is active. "
385 "Not modifying" % (dev_id))
388 # unbind any existing drivers we don't want
389 if has_driver(dev_id):
390 if dev["Driver_str"] == driver:
391 print("%s already bound to driver %s, skipping\n"
395 saved_driver = dev["Driver_str"]
396 unbind_one(dev_id, force)
397 dev["Driver_str"] = "" # clear driver string
399 # For kernels >= 3.15 driver_override can be used to specify the driver
400 # for a device rather than relying on the driver to provide a positive
401 # match of the device. The existing process of looking up
402 # the vendor and device ID, adding them to the driver new_id,
403 # will erroneously bind other devices too which has the additional burden
404 # of unbinding those devices
405 if driver in dpdk_drivers:
406 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
407 if os.path.exists(filename):
409 f = open(filename, "w")
411 print("Error: bind failed for %s - Cannot open %s"
412 % (dev_id, filename))
415 f.write("%s" % driver)
418 print("Error: bind failed for %s - Cannot write driver %s to "
419 "PCI ID " % (dev_id, driver))
421 # For kernels < 3.15 use new_id to add PCI id's to the driver
423 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
425 f = open(filename, "w")
427 print("Error: bind failed for %s - Cannot open %s"
428 % (dev_id, filename))
431 # Convert Device and Vendor Id to int to write to new_id
432 f.write("%04x %04x" % (int(dev["Vendor"],16),
433 int(dev["Device"], 16)))
436 print("Error: bind failed for %s - Cannot write new PCI ID to "
437 "driver %s" % (dev_id, driver))
440 # do the bind by writing to /sys
441 filename = "/sys/bus/pci/drivers/%s/bind" % driver
443 f = open(filename, "a")
445 print("Error: bind failed for %s - Cannot open %s"
446 % (dev_id, filename))
447 if saved_driver is not None: # restore any previous driver
448 bind_one(dev_id, saved_driver, force)
454 # for some reason, closing dev_id after adding a new PCI ID to new_id
455 # results in IOError. however, if the device was successfully bound,
456 # we don't care for any errors and can safely ignore IOError
457 tmp = get_pci_device_details(dev_id, True)
458 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
460 print("Error: bind failed for %s - Cannot bind to driver %s"
462 if saved_driver is not None: # restore any previous driver
463 bind_one(dev_id, saved_driver, force)
466 # For kernels > 3.15 driver_override is used to bind a device to a driver.
467 # Before unbinding it, overwrite driver_override with empty string so that
468 # the device can be bound to any other driver
469 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
470 if os.path.exists(filename):
472 f = open(filename, "w")
474 print("Error: unbind failed for %s - Cannot open %s"
475 % (dev_id, filename))
481 print("Error: unbind failed for %s - Cannot open %s"
482 % (dev_id, filename))
486 def unbind_all(dev_list, force=False):
487 """Unbind method, takes a list of device locations"""
489 if dev_list[0] == "dpdk":
490 for d in devices.keys():
491 if "Driver_str" in devices[d]:
492 if devices[d]["Driver_str"] in dpdk_drivers:
493 unbind_one(devices[d]["Slot"], force)
496 dev_list = map(dev_id_from_dev_name, dev_list)
501 def bind_all(dev_list, driver, force=False):
502 """Bind method, takes a list of device locations"""
505 dev_list = map(dev_id_from_dev_name, dev_list)
508 bind_one(d, driver, force)
510 # For kernels < 3.15 when binding devices to a generic driver
511 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
512 # that are not bound to any other driver could be bound even if no one has
513 # asked them to. hence, we check the list of drivers again, and see if
514 # some of the previously-unbound devices were erroneously bound.
515 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
516 for d in devices.keys():
517 # skip devices that were already bound or that we know should be bound
518 if "Driver_str" in devices[d] or d in dev_list:
521 # update information about this device
522 devices[d] = dict(devices[d].items() +
523 get_pci_device_details(d, True).items())
525 # check if updated information indicates that the device was bound
526 if "Driver_str" in devices[d]:
530 def display_devices(title, dev_list, extra_params=None):
531 '''Displays to the user the details of a list of devices given in
532 "dev_list". The "extra_params" parameter, if given, should contain a string
533 with %()s fields in it for replacement by the named fields in each
534 device's dictionary.'''
535 strings = [] # this holds the strings to print. We sort before printing
536 print("\n%s" % title)
537 print("="*len(title))
538 if len(dev_list) == 0:
539 strings.append("<none>")
542 if extra_params is not None:
543 strings.append("%s '%s %s' %s" % (dev["Slot"],
548 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
549 # sort before printing, so that the entries appear in PCI order
551 print("\n".join(strings)) # print one per line
553 def show_device_status(devices_type, device_name):
559 # split our list of network devices into the three categories above
560 for d in devices.keys():
561 if device_type_match(devices[d], devices_type):
562 if not has_driver(d):
563 no_drv.append(devices[d])
565 if devices[d]["Driver_str"] in dpdk_drivers:
566 dpdk_drv.append(devices[d])
568 kernel_drv.append(devices[d])
570 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
572 # don't bother displaying anything if there are no devices
574 msg = "No '%s' devices detected" % device_name
577 print("".join('=' * len(msg)))
580 # print each category separately, so we can clearly see what's used by DPDK
581 if len(dpdk_drv) != 0:
582 display_devices("%s devices using DPDK-compatible driver" % device_name,
583 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
584 if len(kernel_drv) != 0:
585 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
586 "if=%(Interface)s drv=%(Driver_str)s "
587 "unused=%(Module_str)s %(Active)s")
589 display_devices("Other %s devices" % device_name, no_drv,
590 "unused=%(Module_str)s")
593 '''Function called when the script is passed the "--status" option.
594 Displays to the user what devices are bound to the igb_uio driver, the
595 kernel driver or to no driver'''
597 if status_dev == "net" or status_dev == "all":
598 show_device_status(network_devices, "Network")
600 if status_dev == "baseband" or status_dev == "all":
601 show_device_status(baseband_devices, "Baseband")
603 if status_dev == "crypto" or status_dev == "all":
604 show_device_status(crypto_devices, "Crypto")
606 if status_dev == "event" or status_dev == "all":
607 show_device_status(eventdev_devices, "Eventdev")
609 if status_dev == "mempool" or status_dev == "all":
610 show_device_status(mempool_devices, "Mempool")
612 if status_dev == "compress" or status_dev == "all":
613 show_device_status(compress_devices , "Compress")
615 if status_dev == "misc" or status_dev == "all":
616 show_device_status(misc_devices, "Misc (rawdev)")
619 '''Parses the command-line arguments given by the user and takes the
620 appropriate action for each'''
626 if len(sys.argv) <= 1:
631 opts, args = getopt.getopt(sys.argv[1:], "b:us",
632 ["help", "usage", "status", "status-dev=",
633 "force", "bind=", "unbind", ])
634 except getopt.GetoptError as error:
636 print("Run '%s --usage' for further information" % sys.argv[0])
639 for opt, arg in opts:
640 if opt == "--help" or opt == "--usage":
643 if opt == "--status-dev":
646 if opt == "--status" or opt == "-s":
651 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
652 if b_flag is not None:
653 print("Error - Only one bind or unbind may be specified\n")
655 if opt == "-u" or opt == "--unbind":
661 def do_arg_actions():
662 '''do the actual action requested by the user'''
668 if b_flag is None and not status_flag:
669 print("Error: No action specified for devices."
670 "Please give a -b or -u option")
671 print("Run '%s --usage' for further information" % sys.argv[0])
674 if b_flag is not None and len(args) == 0:
675 print("Error: No devices specified.")
676 print("Run '%s --usage' for further information" % sys.argv[0])
679 if b_flag == "none" or b_flag == "None":
680 unbind_all(args, force_flag)
681 elif b_flag is not None:
682 bind_all(args, b_flag, force_flag)
684 if b_flag is not None:
686 # refresh if we have changed anything
687 get_device_details(network_devices)
688 get_device_details(baseband_devices)
689 get_device_details(crypto_devices)
690 get_device_details(eventdev_devices)
691 get_device_details(mempool_devices)
692 get_device_details(compress_devices)
693 get_device_details(misc_devices)
698 '''program main function'''
699 # check if lspci is installed, suppress any output
700 with open(os.devnull, 'w') as devnull:
701 ret = subprocess.call(['which', 'lspci'],
702 stdout=devnull, stderr=devnull)
704 print("'lspci' not found - please install 'pciutils'")
709 get_device_details(network_devices)
710 get_device_details(baseband_devices)
711 get_device_details(crypto_devices)
712 get_device_details(eventdev_devices)
713 get_device_details(mempool_devices)
714 get_device_details(compress_devices)
715 get_device_details(misc_devices)
718 if __name__ == "__main__":