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}
41 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
42 'SVendor': None, 'SDevice': None}
43 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
44 'SVendor': None, 'SDevice': None}
46 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
47 baseband_devices = [acceleration_class]
48 crypto_devices = [encryption_class, intel_processor_class]
49 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
50 mempool_devices = [cavium_fpa, octeontx2_npa]
51 compress_devices = [cavium_zip]
52 misc_devices = [intel_ioat_bdw, intel_ioat_skx]
54 # global dict ethernet devices present. Dictionary indexed by PCI address.
55 # Each device within this is itself a dictionary of device properties
57 # list of supported DPDK drivers
58 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
60 # command-line arg flags
68 '''Print usage information for the program'''
69 argv0 = basename(sys.argv[0])
74 %(argv0)s [options] DEVICE1 DEVICE2 ....
76 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
77 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
78 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
82 Display usage information and quit
85 Print the current status of all known network, crypto, event
87 For each device, it displays the PCI domain, bus, slot and function,
88 along with a text description of the device. Depending upon whether the
89 device is being used by a kernel driver, the igb_uio driver, or no
90 driver, other relevant information will be displayed:
91 * the Linux interface name e.g. if=eth0
92 * the driver being used e.g. drv=igb_uio
93 * any suitable drivers not currently using that device
95 NOTE: if this flag is passed along with a bind/unbind option, the
96 status display will always occur after the other operations have taken
100 Print the status of given device group. Supported device groups are:
101 "net", "baseband", "crypto", "event", "mempool" and "compress"
103 -b driver, --bind=driver:
104 Select the driver to use or \"none\" to unbind the device
107 Unbind a device (Equivalent to \"-b none\")
110 By default, network devices which are used by Linux - as indicated by
111 having routes in the routing table - cannot be modified. Using the
112 --force flag overrides this behavior, allowing active links to be
114 WARNING: This can lead to loss of network connection and should be used
120 To display current device status:
123 To display current network device status:
124 %(argv0)s --status-dev net
126 To bind eth1 from the current driver and move to use igb_uio
127 %(argv0)s --bind=igb_uio eth1
129 To unbind 0000:01:00.0 from using any driver
130 %(argv0)s -u 0000:01:00.0
132 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
133 %(argv0)s -b ixgbe 02:00.0 02:00.1
135 """ % locals()) # replace items from local variables
138 # This is roughly compatible with check_output function in subprocess module
139 # which is only available in python 2.7.
140 def check_output(args, stderr=None):
141 '''Run a command and capture its output'''
142 return subprocess.Popen(args, stdout=subprocess.PIPE,
143 stderr=stderr).communicate()[0]
147 '''Checks that igb_uio is loaded'''
150 # list of supported modules
151 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
153 # first check if module is loaded
155 # Get list of sysfs modules (both built-in and dynamically loaded)
156 sysfs_path = '/sys/module/'
158 # Get the list of directories in sysfs_path
159 sysfs_mods = [os.path.join(sysfs_path, o) for o
160 in os.listdir(sysfs_path)
161 if os.path.isdir(os.path.join(sysfs_path, o))]
163 # Extract the last element of '/sys/module/abc' in the array
164 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
166 # special case for vfio_pci (module is named vfio-pci,
167 # but its .ko is named vfio_pci)
168 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
171 if mod["Name"] in sysfs_mods:
176 # check if we have at least one loaded module
177 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
178 if b_flag in dpdk_drivers:
179 print("Error - no supported modules(DPDK driver) are loaded")
182 print("Warning - no supported modules(DPDK driver) are loaded")
184 # change DPDK driver list to only contain drivers that are loaded
185 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
188 def has_driver(dev_id):
189 '''return true if a device is assigned to a driver. False otherwise'''
190 return "Driver_str" in devices[dev_id]
193 def get_pci_device_details(dev_id, probe_lspci):
194 '''This function gets additional details for a PCI device'''
198 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
200 # parse lspci details
201 for line in extra_info:
204 name, value = line.decode().split("\t", 1)
205 name = name.strip(":") + "_str"
207 # check for a unix interface name
208 device["Interface"] = ""
209 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
211 device["Interface"] = \
212 ",".join(os.listdir(os.path.join(base, "net")))
214 # check if a port is used for ssh connection
215 device["Ssh_if"] = False
216 device["Active"] = ""
221 '''This function clears any old data'''
225 def get_device_details(devices_type):
226 '''This function populates the "devices" dictionary. The keys used are
227 the pci addresses (domain:bus:slot.func). The values are themselves
228 dictionaries - one for each NIC.'''
232 # first loop through and read details for all devices
233 # request machine readable format, with numeric IDs and String
235 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
236 for dev_line in dev_lines:
237 if len(dev_line) == 0:
238 if device_type_match(dev, devices_type):
239 # Replace "Driver" with "Driver_str" to have consistency of
240 # of dictionary key names
241 if "Driver" in dev.keys():
242 dev["Driver_str"] = dev.pop("Driver")
243 if "Module" in dev.keys():
244 dev["Module_str"] = dev.pop("Module")
245 # use dict to make copy of dev
246 devices[dev["Slot"]] = dict(dev)
247 # Clear previous device's data
250 name, value = dev_line.decode().split("\t", 1)
251 value_list = value.rsplit(' ', 1)
252 if len(value_list) > 1:
253 # String stored in <name>_str
254 dev[name.rstrip(":") + '_str'] = value_list[0]
256 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
257 .rstrip("]").lstrip("[")
259 if devices_type == network_devices:
260 # check what is the interface if any for an ssh connection if
261 # any to this host, so we can mark it later.
263 route = check_output(["ip", "-o", "route"])
264 # filter out all lines for 169.254 routes
265 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
266 route.decode().splitlines()))
267 rt_info = route.split()
268 for i in range(len(rt_info) - 1):
269 if rt_info[i] == "dev":
270 ssh_if.append(rt_info[i+1])
272 # based on the basic info, get extended text details
273 for d in devices.keys():
274 if not device_type_match(devices[d], devices_type):
277 # get additional info and add it to existing data
278 devices[d] = devices[d].copy()
279 # No need to probe lspci
280 devices[d].update(get_pci_device_details(d, False).items())
282 if devices_type == network_devices:
284 if _if in devices[d]["Interface"].split(","):
285 devices[d]["Ssh_if"] = True
286 devices[d]["Active"] = "*Active*"
289 # add igb_uio to list of supporting modules if needed
290 if "Module_str" in devices[d]:
291 for driver in dpdk_drivers:
292 if driver not in devices[d]["Module_str"]:
293 devices[d]["Module_str"] = \
294 devices[d]["Module_str"] + ",%s" % driver
296 devices[d]["Module_str"] = ",".join(dpdk_drivers)
298 # make sure the driver and module strings do not have any duplicates
300 modules = devices[d]["Module_str"].split(",")
301 if devices[d]["Driver_str"] in modules:
302 modules.remove(devices[d]["Driver_str"])
303 devices[d]["Module_str"] = ",".join(modules)
306 def device_type_match(dev, devices_type):
307 for i in range(len(devices_type)):
309 [x for x in devices_type[i].values() if x is not None])
311 if dev["Class"][0:2] == devices_type[i]["Class"]:
312 match_count = match_count + 1
313 for key in devices_type[i].keys():
314 if key != 'Class' and devices_type[i][key]:
315 value_list = devices_type[i][key].split(',')
316 for value in value_list:
317 if value.strip(' ') == dev[key]:
318 match_count = match_count + 1
319 # count must be the number of non None parameters to match
320 if match_count == param_count:
324 def dev_id_from_dev_name(dev_name):
325 '''Take a device "name" - a string passed in by user to identify a NIC
326 device, and determine the device id - i.e. the domain:bus:slot.func - for
327 it, which can then be used to index into the devices array'''
329 # check if it's already a suitable index
330 if dev_name in devices:
332 # check if it's an index just missing the domain part
333 elif "0000:" + dev_name in devices:
334 return "0000:" + dev_name
336 # check if it's an interface name, e.g. eth1
337 for d in devices.keys():
338 if dev_name in devices[d]["Interface"].split(","):
339 return devices[d]["Slot"]
340 # if nothing else matches - error
341 print("Unknown device: %s. "
342 "Please specify device in \"bus:slot.func\" format" % dev_name)
346 def unbind_one(dev_id, force):
347 '''Unbind the device identified by "dev_id" from its current driver'''
348 dev = devices[dev_id]
349 if not has_driver(dev_id):
350 print("%s %s %s is not currently managed by any driver\n" %
351 (dev["Slot"], dev["Device_str"], dev["Interface"]))
354 # prevent us disconnecting ourselves
355 if dev["Ssh_if"] and not force:
356 print("Routing table indicates that interface %s is active. "
357 "Skipping unbind" % (dev_id))
360 # write to /sys to unbind
361 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
363 f = open(filename, "a")
365 print("Error: unbind failed for %s - Cannot open %s"
366 % (dev_id, filename))
372 def bind_one(dev_id, driver, force):
373 '''Bind the device given by "dev_id" to the driver "driver". If the device
374 is already bound to a different driver, it will be unbound first'''
375 dev = devices[dev_id]
376 saved_driver = None # used to rollback any unbind in case of failure
378 # prevent disconnection of our ssh session
379 if dev["Ssh_if"] and not force:
380 print("Routing table indicates that interface %s is active. "
381 "Not modifying" % (dev_id))
384 # unbind any existing drivers we don't want
385 if has_driver(dev_id):
386 if dev["Driver_str"] == driver:
387 print("%s already bound to driver %s, skipping\n"
391 saved_driver = dev["Driver_str"]
392 unbind_one(dev_id, force)
393 dev["Driver_str"] = "" # clear driver string
395 # For kernels >= 3.15 driver_override can be used to specify the driver
396 # for a device rather than relying on the driver to provide a positive
397 # match of the device. The existing process of looking up
398 # the vendor and device ID, adding them to the driver new_id,
399 # will erroneously bind other devices too which has the additional burden
400 # of unbinding those devices
401 if driver in dpdk_drivers:
402 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
403 if os.path.exists(filename):
405 f = open(filename, "w")
407 print("Error: bind failed for %s - Cannot open %s"
408 % (dev_id, filename))
411 f.write("%s" % driver)
414 print("Error: bind failed for %s - Cannot write driver %s to "
415 "PCI ID " % (dev_id, driver))
417 # For kernels < 3.15 use new_id to add PCI id's to the driver
419 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
421 f = open(filename, "w")
423 print("Error: bind failed for %s - Cannot open %s"
424 % (dev_id, filename))
427 # Convert Device and Vendor Id to int to write to new_id
428 f.write("%04x %04x" % (int(dev["Vendor"],16),
429 int(dev["Device"], 16)))
432 print("Error: bind failed for %s - Cannot write new PCI ID to "
433 "driver %s" % (dev_id, driver))
436 # do the bind by writing to /sys
437 filename = "/sys/bus/pci/drivers/%s/bind" % driver
439 f = open(filename, "a")
441 print("Error: bind failed for %s - Cannot open %s"
442 % (dev_id, filename))
443 if saved_driver is not None: # restore any previous driver
444 bind_one(dev_id, saved_driver, force)
450 # for some reason, closing dev_id after adding a new PCI ID to new_id
451 # results in IOError. however, if the device was successfully bound,
452 # we don't care for any errors and can safely ignore IOError
453 tmp = get_pci_device_details(dev_id, True)
454 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
456 print("Error: bind failed for %s - Cannot bind to driver %s"
458 if saved_driver is not None: # restore any previous driver
459 bind_one(dev_id, saved_driver, force)
462 # For kernels > 3.15 driver_override is used to bind a device to a driver.
463 # Before unbinding it, overwrite driver_override with empty string so that
464 # the device can be bound to any other driver
465 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
466 if os.path.exists(filename):
468 f = open(filename, "w")
470 print("Error: unbind failed for %s - Cannot open %s"
471 % (dev_id, filename))
477 print("Error: unbind failed for %s - Cannot open %s"
478 % (dev_id, filename))
482 def unbind_all(dev_list, force=False):
483 """Unbind method, takes a list of device locations"""
485 if dev_list[0] == "dpdk":
486 for d in devices.keys():
487 if "Driver_str" in devices[d]:
488 if devices[d]["Driver_str"] in dpdk_drivers:
489 unbind_one(devices[d]["Slot"], force)
492 dev_list = map(dev_id_from_dev_name, dev_list)
497 def bind_all(dev_list, driver, force=False):
498 """Bind method, takes a list of device locations"""
501 dev_list = map(dev_id_from_dev_name, dev_list)
504 bind_one(d, driver, force)
506 # For kernels < 3.15 when binding devices to a generic driver
507 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
508 # that are not bound to any other driver could be bound even if no one has
509 # asked them to. hence, we check the list of drivers again, and see if
510 # some of the previously-unbound devices were erroneously bound.
511 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
512 for d in devices.keys():
513 # skip devices that were already bound or that we know should be bound
514 if "Driver_str" in devices[d] or d in dev_list:
517 # update information about this device
518 devices[d] = dict(devices[d].items() +
519 get_pci_device_details(d, True).items())
521 # check if updated information indicates that the device was bound
522 if "Driver_str" in devices[d]:
526 def display_devices(title, dev_list, extra_params=None):
527 '''Displays to the user the details of a list of devices given in
528 "dev_list". The "extra_params" parameter, if given, should contain a string
529 with %()s fields in it for replacement by the named fields in each
530 device's dictionary.'''
531 strings = [] # this holds the strings to print. We sort before printing
532 print("\n%s" % title)
533 print("="*len(title))
534 if len(dev_list) == 0:
535 strings.append("<none>")
538 if extra_params is not None:
539 strings.append("%s '%s %s' %s" % (dev["Slot"],
544 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
545 # sort before printing, so that the entries appear in PCI order
547 print("\n".join(strings)) # print one per line
549 def show_device_status(devices_type, device_name):
555 # split our list of network devices into the three categories above
556 for d in devices.keys():
557 if device_type_match(devices[d], devices_type):
558 if not has_driver(d):
559 no_drv.append(devices[d])
561 if devices[d]["Driver_str"] in dpdk_drivers:
562 dpdk_drv.append(devices[d])
564 kernel_drv.append(devices[d])
566 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
568 # don't bother displaying anything if there are no devices
570 msg = "No '%s' devices detected" % device_name
573 print("".join('=' * len(msg)))
576 # print each category separately, so we can clearly see what's used by DPDK
577 if len(dpdk_drv) != 0:
578 display_devices("%s devices using DPDK-compatible driver" % device_name,
579 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
580 if len(kernel_drv) != 0:
581 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
582 "if=%(Interface)s drv=%(Driver_str)s "
583 "unused=%(Module_str)s %(Active)s")
585 display_devices("Other %s devices" % device_name, no_drv,
586 "unused=%(Module_str)s")
589 '''Function called when the script is passed the "--status" option.
590 Displays to the user what devices are bound to the igb_uio driver, the
591 kernel driver or to no driver'''
593 if status_dev == "net" or status_dev == "all":
594 show_device_status(network_devices, "Network")
596 if status_dev == "baseband" or status_dev == "all":
597 show_device_status(baseband_devices, "Baseband")
599 if status_dev == "crypto" or status_dev == "all":
600 show_device_status(crypto_devices, "Crypto")
602 if status_dev == "event" or status_dev == "all":
603 show_device_status(eventdev_devices, "Eventdev")
605 if status_dev == "mempool" or status_dev == "all":
606 show_device_status(mempool_devices, "Mempool")
608 if status_dev == "compress" or status_dev == "all":
609 show_device_status(compress_devices , "Compress")
611 if status_dev == "misc" or status_dev == "all":
612 show_device_status(misc_devices, "Misc (rawdev)")
615 '''Parses the command-line arguments given by the user and takes the
616 appropriate action for each'''
622 if len(sys.argv) <= 1:
627 opts, args = getopt.getopt(sys.argv[1:], "b:us",
628 ["help", "usage", "status", "status-dev=",
629 "force", "bind=", "unbind", ])
630 except getopt.GetoptError as error:
632 print("Run '%s --usage' for further information" % sys.argv[0])
635 for opt, arg in opts:
636 if opt == "--help" or opt == "--usage":
639 if opt == "--status-dev":
642 if opt == "--status" or opt == "-s":
647 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
648 if b_flag is not None:
649 print("Error - Only one bind or unbind may be specified\n")
651 if opt == "-u" or opt == "--unbind":
657 def do_arg_actions():
658 '''do the actual action requested by the user'''
664 if b_flag is None and not status_flag:
665 print("Error: No action specified for devices."
666 "Please give a -b or -u option")
667 print("Run '%s --usage' for further information" % sys.argv[0])
670 if b_flag is not None and len(args) == 0:
671 print("Error: No devices specified.")
672 print("Run '%s --usage' for further information" % sys.argv[0])
675 if b_flag == "none" or b_flag == "None":
676 unbind_all(args, force_flag)
677 elif b_flag is not None:
678 bind_all(args, b_flag, force_flag)
680 if b_flag is not None:
682 # refresh if we have changed anything
683 get_device_details(network_devices)
684 get_device_details(baseband_devices)
685 get_device_details(crypto_devices)
686 get_device_details(eventdev_devices)
687 get_device_details(mempool_devices)
688 get_device_details(compress_devices)
689 get_device_details(misc_devices)
694 '''program main function'''
695 # check if lspci is installed, suppress any output
696 with open(os.devnull, 'w') as devnull:
697 ret = subprocess.call(['which', 'lspci'],
698 stdout=devnull, stderr=devnull)
700 print("'lspci' not found - please install 'pciutils'")
705 get_device_details(network_devices)
706 get_device_details(baseband_devices)
707 get_device_details(crypto_devices)
708 get_device_details(eventdev_devices)
709 get_device_details(mempool_devices)
710 get_device_details(compress_devices)
711 get_device_details(misc_devices)
714 if __name__ == "__main__":