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 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
16 'SVendor': None, 'SDevice': None}
17 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
18 'SVendor': None, 'SDevice': None}
19 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
20 'SVendor': None, 'SDevice': None}
21 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
22 'SVendor': None, 'SDevice': None}
23 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
24 'SVendor': None, 'SDevice': None}
25 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
26 'SVendor': None, 'SDevice': None}
27 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
28 'SVendor': None, 'SDevice': None}
29 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
30 'SVendor': None, 'SDevice': None}
32 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
33 'SVendor': None, 'SDevice': None}
34 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
35 'SVendor': None, 'SDevice': None}
37 network_devices = [network_class, cavium_pkx, avp_vnic]
38 crypto_devices = [encryption_class, intel_processor_class]
39 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
40 mempool_devices = [cavium_fpa, octeontx2_npa]
41 compress_devices = [cavium_zip]
43 # global dict ethernet devices present. Dictionary indexed by PCI address.
44 # Each device within this is itself a dictionary of device properties
46 # list of supported DPDK drivers
47 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
49 # command-line arg flags
57 '''Print usage information for the program'''
58 argv0 = basename(sys.argv[0])
63 %(argv0)s [options] DEVICE1 DEVICE2 ....
65 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
66 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
67 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
71 Display usage information and quit
74 Print the current status of all known network, crypto, event
76 For each device, it displays the PCI domain, bus, slot and function,
77 along with a text description of the device. Depending upon whether the
78 device is being used by a kernel driver, the igb_uio driver, or no
79 driver, other relevant information will be displayed:
80 * the Linux interface name e.g. if=eth0
81 * the driver being used e.g. drv=igb_uio
82 * any suitable drivers not currently using that device
84 NOTE: if this flag is passed along with a bind/unbind option, the
85 status display will always occur after the other operations have taken
89 Print the status of given device group. Supported device groups are:
90 "net", "crypto", "event", "mempool" and "compress"
92 -b driver, --bind=driver:
93 Select the driver to use or \"none\" to unbind the device
96 Unbind a device (Equivalent to \"-b none\")
99 By default, network devices which are used by Linux - as indicated by
100 having routes in the routing table - cannot be modified. Using the
101 --force flag overrides this behavior, allowing active links to be
103 WARNING: This can lead to loss of network connection and should be used
109 To display current device status:
112 To display current network device status:
113 %(argv0)s --status-dev net
115 To bind eth1 from the current driver and move to use igb_uio
116 %(argv0)s --bind=igb_uio eth1
118 To unbind 0000:01:00.0 from using any driver
119 %(argv0)s -u 0000:01:00.0
121 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
122 %(argv0)s -b ixgbe 02:00.0 02:00.1
124 """ % locals()) # replace items from local variables
127 # This is roughly compatible with check_output function in subprocess module
128 # which is only available in python 2.7.
129 def check_output(args, stderr=None):
130 '''Run a command and capture its output'''
131 return subprocess.Popen(args, stdout=subprocess.PIPE,
132 stderr=stderr).communicate()[0]
136 '''Checks that igb_uio is loaded'''
139 # list of supported modules
140 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
142 # first check if module is loaded
144 # Get list of sysfs modules (both built-in and dynamically loaded)
145 sysfs_path = '/sys/module/'
147 # Get the list of directories in sysfs_path
148 sysfs_mods = [os.path.join(sysfs_path, o) for o
149 in os.listdir(sysfs_path)
150 if os.path.isdir(os.path.join(sysfs_path, o))]
152 # Extract the last element of '/sys/module/abc' in the array
153 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
155 # special case for vfio_pci (module is named vfio-pci,
156 # but its .ko is named vfio_pci)
157 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
160 if mod["Name"] in sysfs_mods:
165 # check if we have at least one loaded module
166 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
167 if b_flag in dpdk_drivers:
168 print("Error - no supported modules(DPDK driver) are loaded")
171 print("Warning - no supported modules(DPDK driver) are loaded")
173 # change DPDK driver list to only contain drivers that are loaded
174 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
177 def has_driver(dev_id):
178 '''return true if a device is assigned to a driver. False otherwise'''
179 return "Driver_str" in devices[dev_id]
182 def get_pci_device_details(dev_id, probe_lspci):
183 '''This function gets additional details for a PCI device'''
187 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
189 # parse lspci details
190 for line in extra_info:
193 name, value = line.decode().split("\t", 1)
194 name = name.strip(":") + "_str"
196 # check for a unix interface name
197 device["Interface"] = ""
198 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
200 device["Interface"] = \
201 ",".join(os.listdir(os.path.join(base, "net")))
203 # check if a port is used for ssh connection
204 device["Ssh_if"] = False
205 device["Active"] = ""
210 '''This function clears any old data'''
213 def get_device_details(devices_type):
214 '''This function populates the "devices" dictionary. The keys used are
215 the pci addresses (domain:bus:slot.func). The values are themselves
216 dictionaries - one for each NIC.'''
220 # first loop through and read details for all devices
221 # request machine readable format, with numeric IDs and String
223 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
224 for dev_line in dev_lines:
225 if len(dev_line) == 0:
226 if device_type_match(dev, devices_type):
227 # Replace "Driver" with "Driver_str" to have consistency of
228 # of dictionary key names
229 if "Driver" in dev.keys():
230 dev["Driver_str"] = dev.pop("Driver")
231 if "Module" in dev.keys():
232 dev["Module_str"] = dev.pop("Module")
233 # use dict to make copy of dev
234 devices[dev["Slot"]] = dict(dev)
235 # Clear previous device's data
238 name, value = dev_line.decode().split("\t", 1)
239 value_list = value.rsplit(' ', 1)
240 if len(value_list) > 1:
241 # String stored in <name>_str
242 dev[name.rstrip(":") + '_str'] = value_list[0]
244 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
245 .rstrip("]").lstrip("[")
247 if devices_type == network_devices:
248 # check what is the interface if any for an ssh connection if
249 # any to this host, so we can mark it later.
251 route = check_output(["ip", "-o", "route"])
252 # filter out all lines for 169.254 routes
253 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
254 route.decode().splitlines()))
255 rt_info = route.split()
256 for i in range(len(rt_info) - 1):
257 if rt_info[i] == "dev":
258 ssh_if.append(rt_info[i+1])
260 # based on the basic info, get extended text details
261 for d in devices.keys():
262 if not device_type_match(devices[d], devices_type):
265 # get additional info and add it to existing data
266 devices[d] = devices[d].copy()
267 # No need to probe lspci
268 devices[d].update(get_pci_device_details(d, False).items())
270 if devices_type == network_devices:
272 if _if in devices[d]["Interface"].split(","):
273 devices[d]["Ssh_if"] = True
274 devices[d]["Active"] = "*Active*"
277 # add igb_uio to list of supporting modules if needed
278 if "Module_str" in devices[d]:
279 for driver in dpdk_drivers:
280 if driver not in devices[d]["Module_str"]:
281 devices[d]["Module_str"] = \
282 devices[d]["Module_str"] + ",%s" % driver
284 devices[d]["Module_str"] = ",".join(dpdk_drivers)
286 # make sure the driver and module strings do not have any duplicates
288 modules = devices[d]["Module_str"].split(",")
289 if devices[d]["Driver_str"] in modules:
290 modules.remove(devices[d]["Driver_str"])
291 devices[d]["Module_str"] = ",".join(modules)
294 def device_type_match(dev, devices_type):
295 for i in range(len(devices_type)):
297 [x for x in devices_type[i].values() if x is not None])
299 if dev["Class"][0:2] == devices_type[i]["Class"]:
300 match_count = match_count + 1
301 for key in devices_type[i].keys():
302 if key != 'Class' and devices_type[i][key]:
303 value_list = devices_type[i][key].split(',')
304 for value in value_list:
305 if value.strip(' ') == dev[key]:
306 match_count = match_count + 1
307 # count must be the number of non None parameters to match
308 if match_count == param_count:
312 def dev_id_from_dev_name(dev_name):
313 '''Take a device "name" - a string passed in by user to identify a NIC
314 device, and determine the device id - i.e. the domain:bus:slot.func - for
315 it, which can then be used to index into the devices array'''
317 # check if it's already a suitable index
318 if dev_name in devices:
320 # check if it's an index just missing the domain part
321 elif "0000:" + dev_name in devices:
322 return "0000:" + dev_name
324 # check if it's an interface name, e.g. eth1
325 for d in devices.keys():
326 if dev_name in devices[d]["Interface"].split(","):
327 return devices[d]["Slot"]
328 # if nothing else matches - error
329 print("Unknown device: %s. "
330 "Please specify device in \"bus:slot.func\" format" % dev_name)
334 def unbind_one(dev_id, force):
335 '''Unbind the device identified by "dev_id" from its current driver'''
336 dev = devices[dev_id]
337 if not has_driver(dev_id):
338 print("%s %s %s is not currently managed by any driver\n" %
339 (dev["Slot"], dev["Device_str"], dev["Interface"]))
342 # prevent us disconnecting ourselves
343 if dev["Ssh_if"] and not force:
344 print("Routing table indicates that interface %s is active. "
345 "Skipping unbind" % (dev_id))
348 # write to /sys to unbind
349 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
351 f = open(filename, "a")
353 print("Error: unbind failed for %s - Cannot open %s"
354 % (dev_id, filename))
360 def bind_one(dev_id, driver, force):
361 '''Bind the device given by "dev_id" to the driver "driver". If the device
362 is already bound to a different driver, it will be unbound first'''
363 dev = devices[dev_id]
364 saved_driver = None # used to rollback any unbind in case of failure
366 # prevent disconnection of our ssh session
367 if dev["Ssh_if"] and not force:
368 print("Routing table indicates that interface %s is active. "
369 "Not modifying" % (dev_id))
372 # unbind any existing drivers we don't want
373 if has_driver(dev_id):
374 if dev["Driver_str"] == driver:
375 print("%s already bound to driver %s, skipping\n"
379 saved_driver = dev["Driver_str"]
380 unbind_one(dev_id, force)
381 dev["Driver_str"] = "" # clear driver string
383 # For kernels >= 3.15 driver_override can be used to specify the driver
384 # for a device rather than relying on the driver to provide a positive
385 # match of the device. The existing process of looking up
386 # the vendor and device ID, adding them to the driver new_id,
387 # will erroneously bind other devices too which has the additional burden
388 # of unbinding those devices
389 if driver in dpdk_drivers:
390 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
391 if os.path.exists(filename):
393 f = open(filename, "w")
395 print("Error: bind failed for %s - Cannot open %s"
396 % (dev_id, filename))
399 f.write("%s" % driver)
402 print("Error: bind failed for %s - Cannot write driver %s to "
403 "PCI ID " % (dev_id, driver))
405 # For kernels < 3.15 use new_id to add PCI id's to the driver
407 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
409 f = open(filename, "w")
411 print("Error: bind failed for %s - Cannot open %s"
412 % (dev_id, filename))
415 # Convert Device and Vendor Id to int to write to new_id
416 f.write("%04x %04x" % (int(dev["Vendor"],16),
417 int(dev["Device"], 16)))
420 print("Error: bind failed for %s - Cannot write new PCI ID to "
421 "driver %s" % (dev_id, driver))
424 # do the bind by writing to /sys
425 filename = "/sys/bus/pci/drivers/%s/bind" % driver
427 f = open(filename, "a")
429 print("Error: bind failed for %s - Cannot open %s"
430 % (dev_id, filename))
431 if saved_driver is not None: # restore any previous driver
432 bind_one(dev_id, saved_driver, force)
438 # for some reason, closing dev_id after adding a new PCI ID to new_id
439 # results in IOError. however, if the device was successfully bound,
440 # we don't care for any errors and can safely ignore IOError
441 tmp = get_pci_device_details(dev_id, True)
442 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
444 print("Error: bind failed for %s - Cannot bind to driver %s"
446 if saved_driver is not None: # restore any previous driver
447 bind_one(dev_id, saved_driver, force)
450 # For kernels > 3.15 driver_override is used to bind a device to a driver.
451 # Before unbinding it, overwrite driver_override with empty string so that
452 # the device can be bound to any other driver
453 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
454 if os.path.exists(filename):
456 f = open(filename, "w")
458 print("Error: unbind failed for %s - Cannot open %s"
459 % (dev_id, filename))
465 print("Error: unbind failed for %s - Cannot open %s"
466 % (dev_id, filename))
470 def unbind_all(dev_list, force=False):
471 """Unbind method, takes a list of device locations"""
473 if dev_list[0] == "dpdk":
474 for d in devices.keys():
475 if "Driver_str" in devices[d]:
476 if devices[d]["Driver_str"] in dpdk_drivers:
477 unbind_one(devices[d]["Slot"], force)
480 dev_list = map(dev_id_from_dev_name, dev_list)
485 def bind_all(dev_list, driver, force=False):
486 """Bind method, takes a list of device locations"""
489 dev_list = map(dev_id_from_dev_name, dev_list)
492 bind_one(d, driver, force)
494 # For kernels < 3.15 when binding devices to a generic driver
495 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
496 # that are not bound to any other driver could be bound even if no one has
497 # asked them to. hence, we check the list of drivers again, and see if
498 # some of the previously-unbound devices were erroneously bound.
499 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
500 for d in devices.keys():
501 # skip devices that were already bound or that we know should be bound
502 if "Driver_str" in devices[d] or d in dev_list:
505 # update information about this device
506 devices[d] = dict(devices[d].items() +
507 get_pci_device_details(d, True).items())
509 # check if updated information indicates that the device was bound
510 if "Driver_str" in devices[d]:
514 def display_devices(title, dev_list, extra_params=None):
515 '''Displays to the user the details of a list of devices given in
516 "dev_list". The "extra_params" parameter, if given, should contain a string
517 with %()s fields in it for replacement by the named fields in each
518 device's dictionary.'''
519 strings = [] # this holds the strings to print. We sort before printing
520 print("\n%s" % title)
521 print("="*len(title))
522 if len(dev_list) == 0:
523 strings.append("<none>")
526 if extra_params is not None:
527 strings.append("%s '%s %s' %s" % (dev["Slot"],
532 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
533 # sort before printing, so that the entries appear in PCI order
535 print("\n".join(strings)) # print one per line
537 def show_device_status(devices_type, device_name):
543 # split our list of network devices into the three categories above
544 for d in devices.keys():
545 if device_type_match(devices[d], devices_type):
546 if not has_driver(d):
547 no_drv.append(devices[d])
549 if devices[d]["Driver_str"] in dpdk_drivers:
550 dpdk_drv.append(devices[d])
552 kernel_drv.append(devices[d])
554 n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
556 # don't bother displaying anything if there are no devices
558 msg = "No '%s' devices detected" % device_name
561 print("".join('=' * len(msg)))
564 # print each category separately, so we can clearly see what's used by DPDK
565 if len(dpdk_drv) != 0:
566 display_devices("%s devices using DPDK-compatible driver" % device_name,
567 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
568 if len(kernel_drv) != 0:
569 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
570 "if=%(Interface)s drv=%(Driver_str)s "
571 "unused=%(Module_str)s %(Active)s")
573 display_devices("Other %s devices" % device_name, no_drv,
574 "unused=%(Module_str)s")
577 '''Function called when the script is passed the "--status" option.
578 Displays to the user what devices are bound to the igb_uio driver, the
579 kernel driver or to no driver'''
581 if status_dev == "net" or status_dev == "all":
582 show_device_status(network_devices, "Network")
584 if status_dev == "crypto" or status_dev == "all":
585 show_device_status(crypto_devices, "Crypto")
587 if status_dev == "event" or status_dev == "all":
588 show_device_status(eventdev_devices, "Eventdev")
590 if status_dev == "mempool" or status_dev == "all":
591 show_device_status(mempool_devices, "Mempool")
593 if status_dev == "compress" or status_dev == "all":
594 show_device_status(compress_devices , "Compress")
598 '''Parses the command-line arguments given by the user and takes the
599 appropriate action for each'''
605 if len(sys.argv) <= 1:
610 opts, args = getopt.getopt(sys.argv[1:], "b:us",
611 ["help", "usage", "status", "status-dev=",
612 "force", "bind=", "unbind", ])
613 except getopt.GetoptError as error:
615 print("Run '%s --usage' for further information" % sys.argv[0])
618 for opt, arg in opts:
619 if opt == "--help" or opt == "--usage":
622 if opt == "--status-dev":
625 if opt == "--status" or opt == "-s":
630 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
631 if b_flag is not None:
632 print("Error - Only one bind or unbind may be specified\n")
634 if opt == "-u" or opt == "--unbind":
640 def do_arg_actions():
641 '''do the actual action requested by the user'''
647 if b_flag is None and not status_flag:
648 print("Error: No action specified for devices."
649 "Please give a -b or -u option")
650 print("Run '%s --usage' for further information" % sys.argv[0])
653 if b_flag is not None and len(args) == 0:
654 print("Error: No devices specified.")
655 print("Run '%s --usage' for further information" % sys.argv[0])
658 if b_flag == "none" or b_flag == "None":
659 unbind_all(args, force_flag)
660 elif b_flag is not None:
661 bind_all(args, b_flag, force_flag)
663 if b_flag is not None:
665 # refresh if we have changed anything
666 get_device_details(network_devices)
667 get_device_details(crypto_devices)
668 get_device_details(eventdev_devices)
669 get_device_details(mempool_devices)
670 get_device_details(compress_devices)
675 '''program main function'''
676 # check if lspci is installed, suppress any output
677 with open(os.devnull, 'w') as devnull:
678 ret = subprocess.call(['which', 'lspci'],
679 stdout=devnull, stderr=devnull)
681 print("'lspci' not found - please install 'pciutils'")
686 get_device_details(network_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)
693 if __name__ == "__main__":