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}
48 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
49 baseband_devices = [acceleration_class]
50 crypto_devices = [encryption_class, intel_processor_class]
51 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
52 mempool_devices = [cavium_fpa, octeontx2_npa]
53 compress_devices = [cavium_zip]
54 misc_devices = [intel_ioat_bdw, intel_ioat_skx, octeontx2_dma]
56 # global dict ethernet devices present. Dictionary indexed by PCI address.
57 # Each device within this is itself a dictionary of device properties
59 # list of supported DPDK drivers
60 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
62 # command-line arg flags
70 '''Print usage information for the program'''
71 argv0 = basename(sys.argv[0])
76 %(argv0)s [options] DEVICE1 DEVICE2 ....
78 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
79 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
80 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
84 Display usage information and quit
87 Print the current status of all known network, crypto, event
89 For each device, it displays the PCI domain, bus, slot and function,
90 along with a text description of the device. Depending upon whether the
91 device is being used by a kernel driver, the igb_uio driver, or no
92 driver, other relevant information will be displayed:
93 * the Linux interface name e.g. if=eth0
94 * the driver being used e.g. drv=igb_uio
95 * any suitable drivers not currently using that device
97 NOTE: if this flag is passed along with a bind/unbind option, the
98 status display will always occur after the other operations have taken
102 Print the status of given device group. Supported device groups are:
103 "net", "baseband", "crypto", "event", "mempool" and "compress"
105 -b driver, --bind=driver:
106 Select the driver to use or \"none\" to unbind the device
109 Unbind a device (Equivalent to \"-b none\")
112 By default, network devices which are used by Linux - as indicated by
113 having routes in the routing table - cannot be modified. Using the
114 --force flag overrides this behavior, allowing active links to be
116 WARNING: This can lead to loss of network connection and should be used
122 To display current device status:
125 To display current network device status:
126 %(argv0)s --status-dev net
128 To bind eth1 from the current driver and move to use igb_uio
129 %(argv0)s --bind=igb_uio eth1
131 To unbind 0000:01:00.0 from using any driver
132 %(argv0)s -u 0000:01:00.0
134 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
135 %(argv0)s -b ixgbe 02:00.0 02:00.1
137 """ % locals()) # replace items from local variables
140 # This is roughly compatible with check_output function in subprocess module
141 # which is only available in python 2.7.
142 def check_output(args, stderr=None):
143 '''Run a command and capture its output'''
144 return subprocess.Popen(args, stdout=subprocess.PIPE,
145 stderr=stderr).communicate()[0]
149 '''Checks that igb_uio is loaded'''
152 # list of supported modules
153 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
155 # first check if module is loaded
157 # Get list of sysfs modules (both built-in and dynamically loaded)
158 sysfs_path = '/sys/module/'
160 # Get the list of directories in sysfs_path
161 sysfs_mods = [os.path.join(sysfs_path, o) for o
162 in os.listdir(sysfs_path)
163 if os.path.isdir(os.path.join(sysfs_path, o))]
165 # Extract the last element of '/sys/module/abc' in the array
166 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
168 # special case for vfio_pci (module is named vfio-pci,
169 # but its .ko is named vfio_pci)
170 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
173 if mod["Name"] in sysfs_mods:
178 # check if we have at least one loaded module
179 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
180 if b_flag in dpdk_drivers:
181 print("Error - no supported modules(DPDK driver) are loaded")
184 print("Warning - no supported modules(DPDK driver) are loaded")
186 # change DPDK driver list to only contain drivers that are loaded
187 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
190 def has_driver(dev_id):
191 '''return true if a device is assigned to a driver. False otherwise'''
192 return "Driver_str" in devices[dev_id]
195 def get_pci_device_details(dev_id, probe_lspci):
196 '''This function gets additional details for a PCI device'''
200 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
202 # parse lspci details
203 for line in extra_info:
206 name, value = line.decode().split("\t", 1)
207 name = name.strip(":") + "_str"
209 # check for a unix interface name
210 device["Interface"] = ""
211 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
213 device["Interface"] = \
214 ",".join(os.listdir(os.path.join(base, "net")))
216 # check if a port is used for ssh connection
217 device["Ssh_if"] = False
218 device["Active"] = ""
223 '''This function clears any old data'''
227 def get_device_details(devices_type):
228 '''This function populates the "devices" dictionary. The keys used are
229 the pci addresses (domain:bus:slot.func). The values are themselves
230 dictionaries - one for each NIC.'''
234 # first loop through and read details for all devices
235 # request machine readable format, with numeric IDs and String
237 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
238 for dev_line in dev_lines:
239 if len(dev_line) == 0:
240 if device_type_match(dev, devices_type):
241 # Replace "Driver" with "Driver_str" to have consistency of
242 # of dictionary key names
243 if "Driver" in dev.keys():
244 dev["Driver_str"] = dev.pop("Driver")
245 if "Module" in dev.keys():
246 dev["Module_str"] = dev.pop("Module")
247 # use dict to make copy of dev
248 devices[dev["Slot"]] = dict(dev)
249 # Clear previous device's data
252 name, value = dev_line.decode().split("\t", 1)
253 value_list = value.rsplit(' ', 1)
254 if len(value_list) > 1:
255 # String stored in <name>_str
256 dev[name.rstrip(":") + '_str'] = value_list[0]
258 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
259 .rstrip("]").lstrip("[")
261 if devices_type == network_devices:
262 # check what is the interface if any for an ssh connection if
263 # any to this host, so we can mark it later.
265 route = check_output(["ip", "-o", "route"])
266 # filter out all lines for 169.254 routes
267 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
268 route.decode().splitlines()))
269 rt_info = route.split()
270 for i in range(len(rt_info) - 1):
271 if rt_info[i] == "dev":
272 ssh_if.append(rt_info[i+1])
274 # based on the basic info, get extended text details
275 for d in devices.keys():
276 if not device_type_match(devices[d], devices_type):
279 # get additional info and add it to existing data
280 devices[d] = devices[d].copy()
281 # No need to probe lspci
282 devices[d].update(get_pci_device_details(d, False).items())
284 if devices_type == network_devices:
286 if _if in devices[d]["Interface"].split(","):
287 devices[d]["Ssh_if"] = True
288 devices[d]["Active"] = "*Active*"
291 # add igb_uio to list of supporting modules if needed
292 if "Module_str" in devices[d]:
293 for driver in dpdk_drivers:
294 if driver not in devices[d]["Module_str"]:
295 devices[d]["Module_str"] = \
296 devices[d]["Module_str"] + ",%s" % driver
298 devices[d]["Module_str"] = ",".join(dpdk_drivers)
300 # make sure the driver and module strings do not have any duplicates
302 modules = devices[d]["Module_str"].split(",")
303 if devices[d]["Driver_str"] in modules:
304 modules.remove(devices[d]["Driver_str"])
305 devices[d]["Module_str"] = ",".join(modules)
308 def device_type_match(dev, devices_type):
309 for i in range(len(devices_type)):
311 [x for x in devices_type[i].values() if x is not None])
313 if dev["Class"][0:2] == devices_type[i]["Class"]:
314 match_count = match_count + 1
315 for key in devices_type[i].keys():
316 if key != 'Class' and devices_type[i][key]:
317 value_list = devices_type[i][key].split(',')
318 for value in value_list:
319 if value.strip(' ') == dev[key]:
320 match_count = match_count + 1
321 # count must be the number of non None parameters to match
322 if match_count == param_count:
326 def dev_id_from_dev_name(dev_name):
327 '''Take a device "name" - a string passed in by user to identify a NIC
328 device, and determine the device id - i.e. the domain:bus:slot.func - for
329 it, which can then be used to index into the devices array'''
331 # check if it's already a suitable index
332 if dev_name in devices:
334 # check if it's an index just missing the domain part
335 elif "0000:" + dev_name in devices:
336 return "0000:" + dev_name
338 # check if it's an interface name, e.g. eth1
339 for d in devices.keys():
340 if dev_name in devices[d]["Interface"].split(","):
341 return devices[d]["Slot"]
342 # if nothing else matches - error
343 print("Unknown device: %s. "
344 "Please specify device in \"bus:slot.func\" format" % dev_name)
348 def unbind_one(dev_id, force):
349 '''Unbind the device identified by "dev_id" from its current driver'''
350 dev = devices[dev_id]
351 if not has_driver(dev_id):
352 print("%s %s %s is not currently managed by any driver\n" %
353 (dev["Slot"], dev["Device_str"], dev["Interface"]))
356 # prevent us disconnecting ourselves
357 if dev["Ssh_if"] and not force:
358 print("Routing table indicates that interface %s is active. "
359 "Skipping unbind" % (dev_id))
362 # write to /sys to unbind
363 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
365 f = open(filename, "a")
367 print("Error: unbind failed for %s - Cannot open %s"
368 % (dev_id, filename))
374 def bind_one(dev_id, driver, force):
375 '''Bind the device given by "dev_id" to the driver "driver". If the device
376 is already bound to a different driver, it will be unbound first'''
377 dev = devices[dev_id]
378 saved_driver = None # used to rollback any unbind in case of failure
380 # prevent disconnection of our ssh session
381 if dev["Ssh_if"] and not force:
382 print("Routing table indicates that interface %s is active. "
383 "Not modifying" % (dev_id))
386 # unbind any existing drivers we don't want
387 if has_driver(dev_id):
388 if dev["Driver_str"] == driver:
389 print("%s already bound to driver %s, skipping\n"
393 saved_driver = dev["Driver_str"]
394 unbind_one(dev_id, force)
395 dev["Driver_str"] = "" # clear driver string
397 # For kernels >= 3.15 driver_override can be used to specify the driver
398 # for a device rather than relying on the driver to provide a positive
399 # match of the device. The existing process of looking up
400 # the vendor and device ID, adding them to the driver new_id,
401 # will erroneously bind other devices too which has the additional burden
402 # of unbinding those devices
403 if driver in dpdk_drivers:
404 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
405 if os.path.exists(filename):
407 f = open(filename, "w")
409 print("Error: bind failed for %s - Cannot open %s"
410 % (dev_id, filename))
413 f.write("%s" % driver)
416 print("Error: bind failed for %s - Cannot write driver %s to "
417 "PCI ID " % (dev_id, driver))
419 # For kernels < 3.15 use new_id to add PCI id's to the driver
421 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
423 f = open(filename, "w")
425 print("Error: bind failed for %s - Cannot open %s"
426 % (dev_id, filename))
429 # Convert Device and Vendor Id to int to write to new_id
430 f.write("%04x %04x" % (int(dev["Vendor"],16),
431 int(dev["Device"], 16)))
434 print("Error: bind failed for %s - Cannot write new PCI ID to "
435 "driver %s" % (dev_id, driver))
438 # do the bind by writing to /sys
439 filename = "/sys/bus/pci/drivers/%s/bind" % driver
441 f = open(filename, "a")
443 print("Error: bind failed for %s - Cannot open %s"
444 % (dev_id, filename))
445 if saved_driver is not None: # restore any previous driver
446 bind_one(dev_id, saved_driver, force)
452 # for some reason, closing dev_id after adding a new PCI ID to new_id
453 # results in IOError. however, if the device was successfully bound,
454 # we don't care for any errors and can safely ignore IOError
455 tmp = get_pci_device_details(dev_id, True)
456 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
458 print("Error: bind failed for %s - Cannot bind to driver %s"
460 if saved_driver is not None: # restore any previous driver
461 bind_one(dev_id, saved_driver, force)
464 # For kernels > 3.15 driver_override is used to bind a device to a driver.
465 # Before unbinding it, overwrite driver_override with empty string so that
466 # the device can be bound to any other driver
467 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
468 if os.path.exists(filename):
470 f = open(filename, "w")
472 print("Error: unbind failed for %s - Cannot open %s"
473 % (dev_id, filename))
479 print("Error: unbind failed for %s - Cannot open %s"
480 % (dev_id, filename))
484 def unbind_all(dev_list, force=False):
485 """Unbind method, takes a list of device locations"""
487 if dev_list[0] == "dpdk":
488 for d in devices.keys():
489 if "Driver_str" in devices[d]:
490 if devices[d]["Driver_str"] in dpdk_drivers:
491 unbind_one(devices[d]["Slot"], force)
494 dev_list = map(dev_id_from_dev_name, dev_list)
499 def bind_all(dev_list, driver, force=False):
500 """Bind method, takes a list of device locations"""
503 dev_list = map(dev_id_from_dev_name, dev_list)
506 bind_one(d, driver, force)
508 # For kernels < 3.15 when binding devices to a generic driver
509 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
510 # that are not bound to any other driver could be bound even if no one has
511 # asked them to. hence, we check the list of drivers again, and see if
512 # some of the previously-unbound devices were erroneously bound.
513 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
514 for d in devices.keys():
515 # skip devices that were already bound or that we know should be bound
516 if "Driver_str" in devices[d] or d in dev_list:
519 # update information about this device
520 devices[d] = dict(devices[d].items() +
521 get_pci_device_details(d, True).items())
523 # check if updated information indicates that the device was bound
524 if "Driver_str" in devices[d]:
528 def display_devices(title, dev_list, extra_params=None):
529 '''Displays to the user the details of a list of devices given in
530 "dev_list". The "extra_params" parameter, if given, should contain a string
531 with %()s fields in it for replacement by the named fields in each
532 device's dictionary.'''
533 strings = [] # this holds the strings to print. We sort before printing
534 print("\n%s" % title)
535 print("="*len(title))
536 if len(dev_list) == 0:
537 strings.append("<none>")
540 if extra_params is not None:
541 strings.append("%s '%s %s' %s" % (dev["Slot"],
546 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
547 # sort before printing, so that the entries appear in PCI order
549 print("\n".join(strings)) # print one per line
551 def show_device_status(devices_type, device_name):
557 # split our list of network devices into the three categories above
558 for d in devices.keys():
559 if device_type_match(devices[d], devices_type):
560 if not has_driver(d):
561 no_drv.append(devices[d])
563 if devices[d]["Driver_str"] in dpdk_drivers:
564 dpdk_drv.append(devices[d])
566 kernel_drv.append(devices[d])
568 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
570 # don't bother displaying anything if there are no devices
572 msg = "No '%s' devices detected" % device_name
575 print("".join('=' * len(msg)))
578 # print each category separately, so we can clearly see what's used by DPDK
579 if len(dpdk_drv) != 0:
580 display_devices("%s devices using DPDK-compatible driver" % device_name,
581 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
582 if len(kernel_drv) != 0:
583 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
584 "if=%(Interface)s drv=%(Driver_str)s "
585 "unused=%(Module_str)s %(Active)s")
587 display_devices("Other %s devices" % device_name, no_drv,
588 "unused=%(Module_str)s")
591 '''Function called when the script is passed the "--status" option.
592 Displays to the user what devices are bound to the igb_uio driver, the
593 kernel driver or to no driver'''
595 if status_dev == "net" or status_dev == "all":
596 show_device_status(network_devices, "Network")
598 if status_dev == "baseband" or status_dev == "all":
599 show_device_status(baseband_devices, "Baseband")
601 if status_dev == "crypto" or status_dev == "all":
602 show_device_status(crypto_devices, "Crypto")
604 if status_dev == "event" or status_dev == "all":
605 show_device_status(eventdev_devices, "Eventdev")
607 if status_dev == "mempool" or status_dev == "all":
608 show_device_status(mempool_devices, "Mempool")
610 if status_dev == "compress" or status_dev == "all":
611 show_device_status(compress_devices , "Compress")
613 if status_dev == "misc" or status_dev == "all":
614 show_device_status(misc_devices, "Misc (rawdev)")
617 '''Parses the command-line arguments given by the user and takes the
618 appropriate action for each'''
624 if len(sys.argv) <= 1:
629 opts, args = getopt.getopt(sys.argv[1:], "b:us",
630 ["help", "usage", "status", "status-dev=",
631 "force", "bind=", "unbind", ])
632 except getopt.GetoptError as error:
634 print("Run '%s --usage' for further information" % sys.argv[0])
637 for opt, arg in opts:
638 if opt == "--help" or opt == "--usage":
641 if opt == "--status-dev":
644 if opt == "--status" or opt == "-s":
649 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
650 if b_flag is not None:
651 print("Error - Only one bind or unbind may be specified\n")
653 if opt == "-u" or opt == "--unbind":
659 def do_arg_actions():
660 '''do the actual action requested by the user'''
666 if b_flag is None and not status_flag:
667 print("Error: No action specified for devices."
668 "Please give a -b or -u option")
669 print("Run '%s --usage' for further information" % sys.argv[0])
672 if b_flag is not None and len(args) == 0:
673 print("Error: No devices specified.")
674 print("Run '%s --usage' for further information" % sys.argv[0])
677 if b_flag == "none" or b_flag == "None":
678 unbind_all(args, force_flag)
679 elif b_flag is not None:
680 bind_all(args, b_flag, force_flag)
682 if b_flag is not None:
684 # refresh if we have changed anything
685 get_device_details(network_devices)
686 get_device_details(baseband_devices)
687 get_device_details(crypto_devices)
688 get_device_details(eventdev_devices)
689 get_device_details(mempool_devices)
690 get_device_details(compress_devices)
691 get_device_details(misc_devices)
696 '''program main function'''
697 # check if lspci is installed, suppress any output
698 with open(os.devnull, 'w') as devnull:
699 ret = subprocess.call(['which', 'lspci'],
700 stdout=devnull, stderr=devnull)
702 print("'lspci' not found - please install 'pciutils'")
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)
716 if __name__ == "__main__":