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 ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
16 'SVendor': None, 'SDevice': None}
17 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
18 'SVendor': None, 'SDevice': None}
19 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
20 'SVendor': None, 'SDevice': None}
21 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
22 'SVendor': None, 'SDevice': None}
23 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
24 'SVendor': None, 'SDevice': None}
25 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
26 'SVendor': None, 'SDevice': None}
27 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
28 'SVendor': None, 'SDevice': None}
29 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
30 'SVendor': None, 'SDevice': None}
31 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
32 'SVendor': None, 'SDevice': None}
34 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
35 'SVendor': None, 'SDevice': None}
36 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
37 'SVendor': None, 'SDevice': None}
39 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
40 'SVendor': None, 'SDevice': None}
41 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
42 'SVendor': None, 'SDevice': None}
44 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
45 crypto_devices = [encryption_class, intel_processor_class]
46 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
47 mempool_devices = [cavium_fpa, octeontx2_npa]
48 compress_devices = [cavium_zip]
49 misc_devices = [intel_ioat_bdw, intel_ioat_skx]
51 # global dict ethernet devices present. Dictionary indexed by PCI address.
52 # Each device within this is itself a dictionary of device properties
54 # list of supported DPDK drivers
55 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
57 # command-line arg flags
65 '''Print usage information for the program'''
66 argv0 = basename(sys.argv[0])
71 %(argv0)s [options] DEVICE1 DEVICE2 ....
73 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
74 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
75 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
79 Display usage information and quit
82 Print the current status of all known network, crypto, event
84 For each device, it displays the PCI domain, bus, slot and function,
85 along with a text description of the device. Depending upon whether the
86 device is being used by a kernel driver, the igb_uio driver, or no
87 driver, other relevant information will be displayed:
88 * the Linux interface name e.g. if=eth0
89 * the driver being used e.g. drv=igb_uio
90 * any suitable drivers not currently using that device
92 NOTE: if this flag is passed along with a bind/unbind option, the
93 status display will always occur after the other operations have taken
97 Print the status of given device group. Supported device groups are:
98 "net", "crypto", "event", "mempool" and "compress"
100 -b driver, --bind=driver:
101 Select the driver to use or \"none\" to unbind the device
104 Unbind a device (Equivalent to \"-b none\")
107 By default, network devices which are used by Linux - as indicated by
108 having routes in the routing table - cannot be modified. Using the
109 --force flag overrides this behavior, allowing active links to be
111 WARNING: This can lead to loss of network connection and should be used
117 To display current device status:
120 To display current network device status:
121 %(argv0)s --status-dev net
123 To bind eth1 from the current driver and move to use igb_uio
124 %(argv0)s --bind=igb_uio eth1
126 To unbind 0000:01:00.0 from using any driver
127 %(argv0)s -u 0000:01:00.0
129 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
130 %(argv0)s -b ixgbe 02:00.0 02:00.1
132 """ % locals()) # replace items from local variables
135 # This is roughly compatible with check_output function in subprocess module
136 # which is only available in python 2.7.
137 def check_output(args, stderr=None):
138 '''Run a command and capture its output'''
139 return subprocess.Popen(args, stdout=subprocess.PIPE,
140 stderr=stderr).communicate()[0]
144 '''Checks that igb_uio is loaded'''
147 # list of supported modules
148 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
150 # first check if module is loaded
152 # Get list of sysfs modules (both built-in and dynamically loaded)
153 sysfs_path = '/sys/module/'
155 # Get the list of directories in sysfs_path
156 sysfs_mods = [os.path.join(sysfs_path, o) for o
157 in os.listdir(sysfs_path)
158 if os.path.isdir(os.path.join(sysfs_path, o))]
160 # Extract the last element of '/sys/module/abc' in the array
161 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
163 # special case for vfio_pci (module is named vfio-pci,
164 # but its .ko is named vfio_pci)
165 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
168 if mod["Name"] in sysfs_mods:
173 # check if we have at least one loaded module
174 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
175 if b_flag in dpdk_drivers:
176 print("Error - no supported modules(DPDK driver) are loaded")
179 print("Warning - no supported modules(DPDK driver) are loaded")
181 # change DPDK driver list to only contain drivers that are loaded
182 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
185 def has_driver(dev_id):
186 '''return true if a device is assigned to a driver. False otherwise'''
187 return "Driver_str" in devices[dev_id]
190 def get_pci_device_details(dev_id, probe_lspci):
191 '''This function gets additional details for a PCI device'''
195 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
197 # parse lspci details
198 for line in extra_info:
201 name, value = line.decode().split("\t", 1)
202 name = name.strip(":") + "_str"
204 # check for a unix interface name
205 device["Interface"] = ""
206 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
208 device["Interface"] = \
209 ",".join(os.listdir(os.path.join(base, "net")))
211 # check if a port is used for ssh connection
212 device["Ssh_if"] = False
213 device["Active"] = ""
218 '''This function clears any old data'''
222 def get_device_details(devices_type):
223 '''This function populates the "devices" dictionary. The keys used are
224 the pci addresses (domain:bus:slot.func). The values are themselves
225 dictionaries - one for each NIC.'''
229 # first loop through and read details for all devices
230 # request machine readable format, with numeric IDs and String
232 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
233 for dev_line in dev_lines:
234 if len(dev_line) == 0:
235 if device_type_match(dev, devices_type):
236 # Replace "Driver" with "Driver_str" to have consistency of
237 # of dictionary key names
238 if "Driver" in dev.keys():
239 dev["Driver_str"] = dev.pop("Driver")
240 if "Module" in dev.keys():
241 dev["Module_str"] = dev.pop("Module")
242 # use dict to make copy of dev
243 devices[dev["Slot"]] = dict(dev)
244 # Clear previous device's data
247 name, value = dev_line.decode().split("\t", 1)
248 value_list = value.rsplit(' ', 1)
249 if len(value_list) > 1:
250 # String stored in <name>_str
251 dev[name.rstrip(":") + '_str'] = value_list[0]
253 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
254 .rstrip("]").lstrip("[")
256 if devices_type == network_devices:
257 # check what is the interface if any for an ssh connection if
258 # any to this host, so we can mark it later.
260 route = check_output(["ip", "-o", "route"])
261 # filter out all lines for 169.254 routes
262 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
263 route.decode().splitlines()))
264 rt_info = route.split()
265 for i in range(len(rt_info) - 1):
266 if rt_info[i] == "dev":
267 ssh_if.append(rt_info[i+1])
269 # based on the basic info, get extended text details
270 for d in devices.keys():
271 if not device_type_match(devices[d], devices_type):
274 # get additional info and add it to existing data
275 devices[d] = devices[d].copy()
276 # No need to probe lspci
277 devices[d].update(get_pci_device_details(d, False).items())
279 if devices_type == network_devices:
281 if _if in devices[d]["Interface"].split(","):
282 devices[d]["Ssh_if"] = True
283 devices[d]["Active"] = "*Active*"
286 # add igb_uio to list of supporting modules if needed
287 if "Module_str" in devices[d]:
288 for driver in dpdk_drivers:
289 if driver not in devices[d]["Module_str"]:
290 devices[d]["Module_str"] = \
291 devices[d]["Module_str"] + ",%s" % driver
293 devices[d]["Module_str"] = ",".join(dpdk_drivers)
295 # make sure the driver and module strings do not have any duplicates
297 modules = devices[d]["Module_str"].split(",")
298 if devices[d]["Driver_str"] in modules:
299 modules.remove(devices[d]["Driver_str"])
300 devices[d]["Module_str"] = ",".join(modules)
303 def device_type_match(dev, devices_type):
304 for i in range(len(devices_type)):
306 [x for x in devices_type[i].values() if x is not None])
308 if dev["Class"][0:2] == devices_type[i]["Class"]:
309 match_count = match_count + 1
310 for key in devices_type[i].keys():
311 if key != 'Class' and devices_type[i][key]:
312 value_list = devices_type[i][key].split(',')
313 for value in value_list:
314 if value.strip(' ') == dev[key]:
315 match_count = match_count + 1
316 # count must be the number of non None parameters to match
317 if match_count == param_count:
321 def dev_id_from_dev_name(dev_name):
322 '''Take a device "name" - a string passed in by user to identify a NIC
323 device, and determine the device id - i.e. the domain:bus:slot.func - for
324 it, which can then be used to index into the devices array'''
326 # check if it's already a suitable index
327 if dev_name in devices:
329 # check if it's an index just missing the domain part
330 elif "0000:" + dev_name in devices:
331 return "0000:" + dev_name
333 # check if it's an interface name, e.g. eth1
334 for d in devices.keys():
335 if dev_name in devices[d]["Interface"].split(","):
336 return devices[d]["Slot"]
337 # if nothing else matches - error
338 print("Unknown device: %s. "
339 "Please specify device in \"bus:slot.func\" format" % dev_name)
343 def unbind_one(dev_id, force):
344 '''Unbind the device identified by "dev_id" from its current driver'''
345 dev = devices[dev_id]
346 if not has_driver(dev_id):
347 print("%s %s %s is not currently managed by any driver\n" %
348 (dev["Slot"], dev["Device_str"], dev["Interface"]))
351 # prevent us disconnecting ourselves
352 if dev["Ssh_if"] and not force:
353 print("Routing table indicates that interface %s is active. "
354 "Skipping unbind" % (dev_id))
357 # write to /sys to unbind
358 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
360 f = open(filename, "a")
362 print("Error: unbind failed for %s - Cannot open %s"
363 % (dev_id, filename))
369 def bind_one(dev_id, driver, force):
370 '''Bind the device given by "dev_id" to the driver "driver". If the device
371 is already bound to a different driver, it will be unbound first'''
372 dev = devices[dev_id]
373 saved_driver = None # used to rollback any unbind in case of failure
375 # prevent disconnection of our ssh session
376 if dev["Ssh_if"] and not force:
377 print("Routing table indicates that interface %s is active. "
378 "Not modifying" % (dev_id))
381 # unbind any existing drivers we don't want
382 if has_driver(dev_id):
383 if dev["Driver_str"] == driver:
384 print("%s already bound to driver %s, skipping\n"
388 saved_driver = dev["Driver_str"]
389 unbind_one(dev_id, force)
390 dev["Driver_str"] = "" # clear driver string
392 # For kernels >= 3.15 driver_override can be used to specify the driver
393 # for a device rather than relying on the driver to provide a positive
394 # match of the device. The existing process of looking up
395 # the vendor and device ID, adding them to the driver new_id,
396 # will erroneously bind other devices too which has the additional burden
397 # of unbinding those devices
398 if driver in dpdk_drivers:
399 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
400 if os.path.exists(filename):
402 f = open(filename, "w")
404 print("Error: bind failed for %s - Cannot open %s"
405 % (dev_id, filename))
408 f.write("%s" % driver)
411 print("Error: bind failed for %s - Cannot write driver %s to "
412 "PCI ID " % (dev_id, driver))
414 # For kernels < 3.15 use new_id to add PCI id's to the driver
416 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
418 f = open(filename, "w")
420 print("Error: bind failed for %s - Cannot open %s"
421 % (dev_id, filename))
424 # Convert Device and Vendor Id to int to write to new_id
425 f.write("%04x %04x" % (int(dev["Vendor"],16),
426 int(dev["Device"], 16)))
429 print("Error: bind failed for %s - Cannot write new PCI ID to "
430 "driver %s" % (dev_id, driver))
433 # do the bind by writing to /sys
434 filename = "/sys/bus/pci/drivers/%s/bind" % driver
436 f = open(filename, "a")
438 print("Error: bind failed for %s - Cannot open %s"
439 % (dev_id, filename))
440 if saved_driver is not None: # restore any previous driver
441 bind_one(dev_id, saved_driver, force)
447 # for some reason, closing dev_id after adding a new PCI ID to new_id
448 # results in IOError. however, if the device was successfully bound,
449 # we don't care for any errors and can safely ignore IOError
450 tmp = get_pci_device_details(dev_id, True)
451 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
453 print("Error: bind failed for %s - Cannot bind to driver %s"
455 if saved_driver is not None: # restore any previous driver
456 bind_one(dev_id, saved_driver, force)
459 # For kernels > 3.15 driver_override is used to bind a device to a driver.
460 # Before unbinding it, overwrite driver_override with empty string so that
461 # the device can be bound to any other driver
462 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
463 if os.path.exists(filename):
465 f = open(filename, "w")
467 print("Error: unbind failed for %s - Cannot open %s"
468 % (dev_id, filename))
474 print("Error: unbind failed for %s - Cannot open %s"
475 % (dev_id, filename))
479 def unbind_all(dev_list, force=False):
480 """Unbind method, takes a list of device locations"""
482 if dev_list[0] == "dpdk":
483 for d in devices.keys():
484 if "Driver_str" in devices[d]:
485 if devices[d]["Driver_str"] in dpdk_drivers:
486 unbind_one(devices[d]["Slot"], force)
489 dev_list = map(dev_id_from_dev_name, dev_list)
494 def bind_all(dev_list, driver, force=False):
495 """Bind method, takes a list of device locations"""
498 dev_list = map(dev_id_from_dev_name, dev_list)
501 bind_one(d, driver, force)
503 # For kernels < 3.15 when binding devices to a generic driver
504 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
505 # that are not bound to any other driver could be bound even if no one has
506 # asked them to. hence, we check the list of drivers again, and see if
507 # some of the previously-unbound devices were erroneously bound.
508 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
509 for d in devices.keys():
510 # skip devices that were already bound or that we know should be bound
511 if "Driver_str" in devices[d] or d in dev_list:
514 # update information about this device
515 devices[d] = dict(devices[d].items() +
516 get_pci_device_details(d, True).items())
518 # check if updated information indicates that the device was bound
519 if "Driver_str" in devices[d]:
523 def display_devices(title, dev_list, extra_params=None):
524 '''Displays to the user the details of a list of devices given in
525 "dev_list". The "extra_params" parameter, if given, should contain a string
526 with %()s fields in it for replacement by the named fields in each
527 device's dictionary.'''
528 strings = [] # this holds the strings to print. We sort before printing
529 print("\n%s" % title)
530 print("="*len(title))
531 if len(dev_list) == 0:
532 strings.append("<none>")
535 if extra_params is not None:
536 strings.append("%s '%s %s' %s" % (dev["Slot"],
541 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
542 # sort before printing, so that the entries appear in PCI order
544 print("\n".join(strings)) # print one per line
546 def show_device_status(devices_type, device_name):
552 # split our list of network devices into the three categories above
553 for d in devices.keys():
554 if device_type_match(devices[d], devices_type):
555 if not has_driver(d):
556 no_drv.append(devices[d])
558 if devices[d]["Driver_str"] in dpdk_drivers:
559 dpdk_drv.append(devices[d])
561 kernel_drv.append(devices[d])
563 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
565 # don't bother displaying anything if there are no devices
567 msg = "No '%s' devices detected" % device_name
570 print("".join('=' * len(msg)))
573 # print each category separately, so we can clearly see what's used by DPDK
574 if len(dpdk_drv) != 0:
575 display_devices("%s devices using DPDK-compatible driver" % device_name,
576 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
577 if len(kernel_drv) != 0:
578 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
579 "if=%(Interface)s drv=%(Driver_str)s "
580 "unused=%(Module_str)s %(Active)s")
582 display_devices("Other %s devices" % device_name, no_drv,
583 "unused=%(Module_str)s")
586 '''Function called when the script is passed the "--status" option.
587 Displays to the user what devices are bound to the igb_uio driver, the
588 kernel driver or to no driver'''
590 if status_dev == "net" or status_dev == "all":
591 show_device_status(network_devices, "Network")
593 if status_dev == "crypto" or status_dev == "all":
594 show_device_status(crypto_devices, "Crypto")
596 if status_dev == "event" or status_dev == "all":
597 show_device_status(eventdev_devices, "Eventdev")
599 if status_dev == "mempool" or status_dev == "all":
600 show_device_status(mempool_devices, "Mempool")
602 if status_dev == "compress" or status_dev == "all":
603 show_device_status(compress_devices , "Compress")
605 if status_dev == "misc" or status_dev == "all":
606 show_device_status(misc_devices, "Misc (rawdev)")
609 '''Parses the command-line arguments given by the user and takes the
610 appropriate action for each'''
616 if len(sys.argv) <= 1:
621 opts, args = getopt.getopt(sys.argv[1:], "b:us",
622 ["help", "usage", "status", "status-dev=",
623 "force", "bind=", "unbind", ])
624 except getopt.GetoptError as error:
626 print("Run '%s --usage' for further information" % sys.argv[0])
629 for opt, arg in opts:
630 if opt == "--help" or opt == "--usage":
633 if opt == "--status-dev":
636 if opt == "--status" or opt == "-s":
641 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
642 if b_flag is not None:
643 print("Error - Only one bind or unbind may be specified\n")
645 if opt == "-u" or opt == "--unbind":
651 def do_arg_actions():
652 '''do the actual action requested by the user'''
658 if b_flag is None and not status_flag:
659 print("Error: No action specified for devices."
660 "Please give a -b or -u option")
661 print("Run '%s --usage' for further information" % sys.argv[0])
664 if b_flag is not None and len(args) == 0:
665 print("Error: No devices specified.")
666 print("Run '%s --usage' for further information" % sys.argv[0])
669 if b_flag == "none" or b_flag == "None":
670 unbind_all(args, force_flag)
671 elif b_flag is not None:
672 bind_all(args, b_flag, force_flag)
674 if b_flag is not None:
676 # refresh if we have changed anything
677 get_device_details(network_devices)
678 get_device_details(crypto_devices)
679 get_device_details(eventdev_devices)
680 get_device_details(mempool_devices)
681 get_device_details(compress_devices)
682 get_device_details(misc_devices)
687 '''program main function'''
688 # check if lspci is installed, suppress any output
689 with open(os.devnull, 'w') as devnull:
690 ret = subprocess.call(['which', 'lspci'],
691 stdout=devnull, stderr=devnull)
693 print("'lspci' not found - please install 'pciutils'")
698 get_device_details(network_devices)
699 get_device_details(crypto_devices)
700 get_device_details(eventdev_devices)
701 get_device_details(mempool_devices)
702 get_device_details(compress_devices)
703 get_device_details(misc_devices)
706 if __name__ == "__main__":