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 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
40 crypto_devices = [encryption_class, intel_processor_class]
41 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
42 mempool_devices = [cavium_fpa, octeontx2_npa]
43 compress_devices = [cavium_zip]
45 # global dict ethernet devices present. Dictionary indexed by PCI address.
46 # Each device within this is itself a dictionary of device properties
48 # list of supported DPDK drivers
49 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
51 # command-line arg flags
59 '''Print usage information for the program'''
60 argv0 = basename(sys.argv[0])
65 %(argv0)s [options] DEVICE1 DEVICE2 ....
67 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
68 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
69 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
73 Display usage information and quit
76 Print the current status of all known network, crypto, event
78 For each device, it displays the PCI domain, bus, slot and function,
79 along with a text description of the device. Depending upon whether the
80 device is being used by a kernel driver, the igb_uio driver, or no
81 driver, other relevant information will be displayed:
82 * the Linux interface name e.g. if=eth0
83 * the driver being used e.g. drv=igb_uio
84 * any suitable drivers not currently using that device
86 NOTE: if this flag is passed along with a bind/unbind option, the
87 status display will always occur after the other operations have taken
91 Print the status of given device group. Supported device groups are:
92 "net", "crypto", "event", "mempool" and "compress"
94 -b driver, --bind=driver:
95 Select the driver to use or \"none\" to unbind the device
98 Unbind a device (Equivalent to \"-b none\")
101 By default, network devices which are used by Linux - as indicated by
102 having routes in the routing table - cannot be modified. Using the
103 --force flag overrides this behavior, allowing active links to be
105 WARNING: This can lead to loss of network connection and should be used
111 To display current device status:
114 To display current network device status:
115 %(argv0)s --status-dev net
117 To bind eth1 from the current driver and move to use igb_uio
118 %(argv0)s --bind=igb_uio eth1
120 To unbind 0000:01:00.0 from using any driver
121 %(argv0)s -u 0000:01:00.0
123 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
124 %(argv0)s -b ixgbe 02:00.0 02:00.1
126 """ % locals()) # replace items from local variables
129 # This is roughly compatible with check_output function in subprocess module
130 # which is only available in python 2.7.
131 def check_output(args, stderr=None):
132 '''Run a command and capture its output'''
133 return subprocess.Popen(args, stdout=subprocess.PIPE,
134 stderr=stderr).communicate()[0]
138 '''Checks that igb_uio is loaded'''
141 # list of supported modules
142 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
144 # first check if module is loaded
146 # Get list of sysfs modules (both built-in and dynamically loaded)
147 sysfs_path = '/sys/module/'
149 # Get the list of directories in sysfs_path
150 sysfs_mods = [os.path.join(sysfs_path, o) for o
151 in os.listdir(sysfs_path)
152 if os.path.isdir(os.path.join(sysfs_path, o))]
154 # Extract the last element of '/sys/module/abc' in the array
155 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
157 # special case for vfio_pci (module is named vfio-pci,
158 # but its .ko is named vfio_pci)
159 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
162 if mod["Name"] in sysfs_mods:
167 # check if we have at least one loaded module
168 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
169 if b_flag in dpdk_drivers:
170 print("Error - no supported modules(DPDK driver) are loaded")
173 print("Warning - no supported modules(DPDK driver) are loaded")
175 # change DPDK driver list to only contain drivers that are loaded
176 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
179 def has_driver(dev_id):
180 '''return true if a device is assigned to a driver. False otherwise'''
181 return "Driver_str" in devices[dev_id]
184 def get_pci_device_details(dev_id, probe_lspci):
185 '''This function gets additional details for a PCI device'''
189 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
191 # parse lspci details
192 for line in extra_info:
195 name, value = line.decode().split("\t", 1)
196 name = name.strip(":") + "_str"
198 # check for a unix interface name
199 device["Interface"] = ""
200 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
202 device["Interface"] = \
203 ",".join(os.listdir(os.path.join(base, "net")))
205 # check if a port is used for ssh connection
206 device["Ssh_if"] = False
207 device["Active"] = ""
212 '''This function clears any old data'''
215 def get_device_details(devices_type):
216 '''This function populates the "devices" dictionary. The keys used are
217 the pci addresses (domain:bus:slot.func). The values are themselves
218 dictionaries - one for each NIC.'''
222 # first loop through and read details for all devices
223 # request machine readable format, with numeric IDs and String
225 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
226 for dev_line in dev_lines:
227 if len(dev_line) == 0:
228 if device_type_match(dev, devices_type):
229 # Replace "Driver" with "Driver_str" to have consistency of
230 # of dictionary key names
231 if "Driver" in dev.keys():
232 dev["Driver_str"] = dev.pop("Driver")
233 if "Module" in dev.keys():
234 dev["Module_str"] = dev.pop("Module")
235 # use dict to make copy of dev
236 devices[dev["Slot"]] = dict(dev)
237 # Clear previous device's data
240 name, value = dev_line.decode().split("\t", 1)
241 value_list = value.rsplit(' ', 1)
242 if len(value_list) > 1:
243 # String stored in <name>_str
244 dev[name.rstrip(":") + '_str'] = value_list[0]
246 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
247 .rstrip("]").lstrip("[")
249 if devices_type == network_devices:
250 # check what is the interface if any for an ssh connection if
251 # any to this host, so we can mark it later.
253 route = check_output(["ip", "-o", "route"])
254 # filter out all lines for 169.254 routes
255 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
256 route.decode().splitlines()))
257 rt_info = route.split()
258 for i in range(len(rt_info) - 1):
259 if rt_info[i] == "dev":
260 ssh_if.append(rt_info[i+1])
262 # based on the basic info, get extended text details
263 for d in devices.keys():
264 if not device_type_match(devices[d], devices_type):
267 # get additional info and add it to existing data
268 devices[d] = devices[d].copy()
269 # No need to probe lspci
270 devices[d].update(get_pci_device_details(d, False).items())
272 if devices_type == network_devices:
274 if _if in devices[d]["Interface"].split(","):
275 devices[d]["Ssh_if"] = True
276 devices[d]["Active"] = "*Active*"
279 # add igb_uio to list of supporting modules if needed
280 if "Module_str" in devices[d]:
281 for driver in dpdk_drivers:
282 if driver not in devices[d]["Module_str"]:
283 devices[d]["Module_str"] = \
284 devices[d]["Module_str"] + ",%s" % driver
286 devices[d]["Module_str"] = ",".join(dpdk_drivers)
288 # make sure the driver and module strings do not have any duplicates
290 modules = devices[d]["Module_str"].split(",")
291 if devices[d]["Driver_str"] in modules:
292 modules.remove(devices[d]["Driver_str"])
293 devices[d]["Module_str"] = ",".join(modules)
296 def device_type_match(dev, devices_type):
297 for i in range(len(devices_type)):
299 [x for x in devices_type[i].values() if x is not None])
301 if dev["Class"][0:2] == devices_type[i]["Class"]:
302 match_count = match_count + 1
303 for key in devices_type[i].keys():
304 if key != 'Class' and devices_type[i][key]:
305 value_list = devices_type[i][key].split(',')
306 for value in value_list:
307 if value.strip(' ') == dev[key]:
308 match_count = match_count + 1
309 # count must be the number of non None parameters to match
310 if match_count == param_count:
314 def dev_id_from_dev_name(dev_name):
315 '''Take a device "name" - a string passed in by user to identify a NIC
316 device, and determine the device id - i.e. the domain:bus:slot.func - for
317 it, which can then be used to index into the devices array'''
319 # check if it's already a suitable index
320 if dev_name in devices:
322 # check if it's an index just missing the domain part
323 elif "0000:" + dev_name in devices:
324 return "0000:" + dev_name
326 # check if it's an interface name, e.g. eth1
327 for d in devices.keys():
328 if dev_name in devices[d]["Interface"].split(","):
329 return devices[d]["Slot"]
330 # if nothing else matches - error
331 print("Unknown device: %s. "
332 "Please specify device in \"bus:slot.func\" format" % dev_name)
336 def unbind_one(dev_id, force):
337 '''Unbind the device identified by "dev_id" from its current driver'''
338 dev = devices[dev_id]
339 if not has_driver(dev_id):
340 print("%s %s %s is not currently managed by any driver\n" %
341 (dev["Slot"], dev["Device_str"], dev["Interface"]))
344 # prevent us disconnecting ourselves
345 if dev["Ssh_if"] and not force:
346 print("Routing table indicates that interface %s is active. "
347 "Skipping unbind" % (dev_id))
350 # write to /sys to unbind
351 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
353 f = open(filename, "a")
355 print("Error: unbind failed for %s - Cannot open %s"
356 % (dev_id, filename))
362 def bind_one(dev_id, driver, force):
363 '''Bind the device given by "dev_id" to the driver "driver". If the device
364 is already bound to a different driver, it will be unbound first'''
365 dev = devices[dev_id]
366 saved_driver = None # used to rollback any unbind in case of failure
368 # prevent disconnection of our ssh session
369 if dev["Ssh_if"] and not force:
370 print("Routing table indicates that interface %s is active. "
371 "Not modifying" % (dev_id))
374 # unbind any existing drivers we don't want
375 if has_driver(dev_id):
376 if dev["Driver_str"] == driver:
377 print("%s already bound to driver %s, skipping\n"
381 saved_driver = dev["Driver_str"]
382 unbind_one(dev_id, force)
383 dev["Driver_str"] = "" # clear driver string
385 # For kernels >= 3.15 driver_override can be used to specify the driver
386 # for a device rather than relying on the driver to provide a positive
387 # match of the device. The existing process of looking up
388 # the vendor and device ID, adding them to the driver new_id,
389 # will erroneously bind other devices too which has the additional burden
390 # of unbinding those devices
391 if driver in dpdk_drivers:
392 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
393 if os.path.exists(filename):
395 f = open(filename, "w")
397 print("Error: bind failed for %s - Cannot open %s"
398 % (dev_id, filename))
401 f.write("%s" % driver)
404 print("Error: bind failed for %s - Cannot write driver %s to "
405 "PCI ID " % (dev_id, driver))
407 # For kernels < 3.15 use new_id to add PCI id's to the driver
409 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
411 f = open(filename, "w")
413 print("Error: bind failed for %s - Cannot open %s"
414 % (dev_id, filename))
417 # Convert Device and Vendor Id to int to write to new_id
418 f.write("%04x %04x" % (int(dev["Vendor"],16),
419 int(dev["Device"], 16)))
422 print("Error: bind failed for %s - Cannot write new PCI ID to "
423 "driver %s" % (dev_id, driver))
426 # do the bind by writing to /sys
427 filename = "/sys/bus/pci/drivers/%s/bind" % driver
429 f = open(filename, "a")
431 print("Error: bind failed for %s - Cannot open %s"
432 % (dev_id, filename))
433 if saved_driver is not None: # restore any previous driver
434 bind_one(dev_id, saved_driver, force)
440 # for some reason, closing dev_id after adding a new PCI ID to new_id
441 # results in IOError. however, if the device was successfully bound,
442 # we don't care for any errors and can safely ignore IOError
443 tmp = get_pci_device_details(dev_id, True)
444 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
446 print("Error: bind failed for %s - Cannot bind to driver %s"
448 if saved_driver is not None: # restore any previous driver
449 bind_one(dev_id, saved_driver, force)
452 # For kernels > 3.15 driver_override is used to bind a device to a driver.
453 # Before unbinding it, overwrite driver_override with empty string so that
454 # the device can be bound to any other driver
455 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
456 if os.path.exists(filename):
458 f = open(filename, "w")
460 print("Error: unbind failed for %s - Cannot open %s"
461 % (dev_id, filename))
467 print("Error: unbind failed for %s - Cannot open %s"
468 % (dev_id, filename))
472 def unbind_all(dev_list, force=False):
473 """Unbind method, takes a list of device locations"""
475 if dev_list[0] == "dpdk":
476 for d in devices.keys():
477 if "Driver_str" in devices[d]:
478 if devices[d]["Driver_str"] in dpdk_drivers:
479 unbind_one(devices[d]["Slot"], force)
482 dev_list = map(dev_id_from_dev_name, dev_list)
487 def bind_all(dev_list, driver, force=False):
488 """Bind method, takes a list of device locations"""
491 dev_list = map(dev_id_from_dev_name, dev_list)
494 bind_one(d, driver, force)
496 # For kernels < 3.15 when binding devices to a generic driver
497 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
498 # that are not bound to any other driver could be bound even if no one has
499 # asked them to. hence, we check the list of drivers again, and see if
500 # some of the previously-unbound devices were erroneously bound.
501 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
502 for d in devices.keys():
503 # skip devices that were already bound or that we know should be bound
504 if "Driver_str" in devices[d] or d in dev_list:
507 # update information about this device
508 devices[d] = dict(devices[d].items() +
509 get_pci_device_details(d, True).items())
511 # check if updated information indicates that the device was bound
512 if "Driver_str" in devices[d]:
516 def display_devices(title, dev_list, extra_params=None):
517 '''Displays to the user the details of a list of devices given in
518 "dev_list". The "extra_params" parameter, if given, should contain a string
519 with %()s fields in it for replacement by the named fields in each
520 device's dictionary.'''
521 strings = [] # this holds the strings to print. We sort before printing
522 print("\n%s" % title)
523 print("="*len(title))
524 if len(dev_list) == 0:
525 strings.append("<none>")
528 if extra_params is not None:
529 strings.append("%s '%s %s' %s" % (dev["Slot"],
534 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
535 # sort before printing, so that the entries appear in PCI order
537 print("\n".join(strings)) # print one per line
539 def show_device_status(devices_type, device_name):
545 # split our list of network devices into the three categories above
546 for d in devices.keys():
547 if device_type_match(devices[d], devices_type):
548 if not has_driver(d):
549 no_drv.append(devices[d])
551 if devices[d]["Driver_str"] in dpdk_drivers:
552 dpdk_drv.append(devices[d])
554 kernel_drv.append(devices[d])
556 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
558 # don't bother displaying anything if there are no devices
560 msg = "No '%s' devices detected" % device_name
563 print("".join('=' * len(msg)))
566 # print each category separately, so we can clearly see what's used by DPDK
567 if len(dpdk_drv) != 0:
568 display_devices("%s devices using DPDK-compatible driver" % device_name,
569 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
570 if len(kernel_drv) != 0:
571 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
572 "if=%(Interface)s drv=%(Driver_str)s "
573 "unused=%(Module_str)s %(Active)s")
575 display_devices("Other %s devices" % device_name, no_drv,
576 "unused=%(Module_str)s")
579 '''Function called when the script is passed the "--status" option.
580 Displays to the user what devices are bound to the igb_uio driver, the
581 kernel driver or to no driver'''
583 if status_dev == "net" or status_dev == "all":
584 show_device_status(network_devices, "Network")
586 if status_dev == "crypto" or status_dev == "all":
587 show_device_status(crypto_devices, "Crypto")
589 if status_dev == "event" or status_dev == "all":
590 show_device_status(eventdev_devices, "Eventdev")
592 if status_dev == "mempool" or status_dev == "all":
593 show_device_status(mempool_devices, "Mempool")
595 if status_dev == "compress" or status_dev == "all":
596 show_device_status(compress_devices , "Compress")
600 '''Parses the command-line arguments given by the user and takes the
601 appropriate action for each'''
607 if len(sys.argv) <= 1:
612 opts, args = getopt.getopt(sys.argv[1:], "b:us",
613 ["help", "usage", "status", "status-dev=",
614 "force", "bind=", "unbind", ])
615 except getopt.GetoptError as error:
617 print("Run '%s --usage' for further information" % sys.argv[0])
620 for opt, arg in opts:
621 if opt == "--help" or opt == "--usage":
624 if opt == "--status-dev":
627 if opt == "--status" or opt == "-s":
632 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
633 if b_flag is not None:
634 print("Error - Only one bind or unbind may be specified\n")
636 if opt == "-u" or opt == "--unbind":
642 def do_arg_actions():
643 '''do the actual action requested by the user'''
649 if b_flag is None and not status_flag:
650 print("Error: No action specified for devices."
651 "Please give a -b or -u option")
652 print("Run '%s --usage' for further information" % sys.argv[0])
655 if b_flag is not None and len(args) == 0:
656 print("Error: No devices specified.")
657 print("Run '%s --usage' for further information" % sys.argv[0])
660 if b_flag == "none" or b_flag == "None":
661 unbind_all(args, force_flag)
662 elif b_flag is not None:
663 bind_all(args, b_flag, force_flag)
665 if b_flag is not None:
667 # refresh if we have changed anything
668 get_device_details(network_devices)
669 get_device_details(crypto_devices)
670 get_device_details(eventdev_devices)
671 get_device_details(mempool_devices)
672 get_device_details(compress_devices)
677 '''program main function'''
678 # check if lspci is installed, suppress any output
679 with open(os.devnull, 'w') as devnull:
680 ret = subprocess.call(['which', 'lspci'],
681 stdout=devnull, stderr=devnull)
683 print("'lspci' not found - please install 'pciutils'")
688 get_device_details(network_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)
695 if __name__ == "__main__":