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}
26 network_devices = [network_class, cavium_pkx]
27 crypto_devices = [encryption_class, intel_processor_class]
28 eventdev_devices = [cavium_sso]
29 mempool_devices = [cavium_fpa]
31 # global dict ethernet devices present. Dictionary indexed by PCI address.
32 # Each device within this is itself a dictionary of device properties
34 # list of supported DPDK drivers
35 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
37 # command-line arg flags
45 '''Print usage information for the program'''
46 argv0 = basename(sys.argv[0])
51 %(argv0)s [options] DEVICE1 DEVICE2 ....
53 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
54 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
55 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
59 Display usage information and quit
62 Print the current status of all known network, crypto, event
64 For each device, it displays the PCI domain, bus, slot and function,
65 along with a text description of the device. Depending upon whether the
66 device is being used by a kernel driver, the igb_uio driver, or no
67 driver, other relevant information will be displayed:
68 * the Linux interface name e.g. if=eth0
69 * the driver being used e.g. drv=igb_uio
70 * any suitable drivers not currently using that device
72 NOTE: if this flag is passed along with a bind/unbind option, the
73 status display will always occur after the other operations have taken
77 Print the status of given device group. Supported device groups are:
78 "net", "crypto", "event" and "mempool"
80 -b driver, --bind=driver:
81 Select the driver to use or \"none\" to unbind the device
84 Unbind a device (Equivalent to \"-b none\")
87 By default, network devices which are used by Linux - as indicated by
88 having routes in the routing table - cannot be modified. Using the
89 --force flag overrides this behavior, allowing active links to be
91 WARNING: This can lead to loss of network connection and should be used
97 To display current device status:
100 To display current network device status:
101 %(argv0)s --status-dev net
103 To bind eth1 from the current driver and move to use igb_uio
104 %(argv0)s --bind=igb_uio eth1
106 To unbind 0000:01:00.0 from using any driver
107 %(argv0)s -u 0000:01:00.0
109 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
110 %(argv0)s -b ixgbe 02:00.0 02:00.1
112 """ % locals()) # replace items from local variables
115 # This is roughly compatible with check_output function in subprocess module
116 # which is only available in python 2.7.
117 def check_output(args, stderr=None):
118 '''Run a command and capture its output'''
119 return subprocess.Popen(args, stdout=subprocess.PIPE,
120 stderr=stderr).communicate()[0]
124 '''Checks that igb_uio is loaded'''
127 # list of supported modules
128 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
130 # first check if module is loaded
132 # Get list of sysfs modules (both built-in and dynamically loaded)
133 sysfs_path = '/sys/module/'
135 # Get the list of directories in sysfs_path
136 sysfs_mods = [os.path.join(sysfs_path, o) for o
137 in os.listdir(sysfs_path)
138 if os.path.isdir(os.path.join(sysfs_path, o))]
140 # Extract the last element of '/sys/module/abc' in the array
141 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
143 # special case for vfio_pci (module is named vfio-pci,
144 # but its .ko is named vfio_pci)
145 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
148 if mod["Name"] in sysfs_mods:
153 # check if we have at least one loaded module
154 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
155 if b_flag in dpdk_drivers:
156 print("Error - no supported modules(DPDK driver) are loaded")
159 print("Warning - no supported modules(DPDK driver) are loaded")
161 # change DPDK driver list to only contain drivers that are loaded
162 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
165 def has_driver(dev_id):
166 '''return true if a device is assigned to a driver. False otherwise'''
167 return "Driver_str" in devices[dev_id]
170 def get_pci_device_details(dev_id, probe_lspci):
171 '''This function gets additional details for a PCI device'''
175 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
177 # parse lspci details
178 for line in extra_info:
181 name, value = line.decode().split("\t", 1)
182 name = name.strip(":") + "_str"
184 # check for a unix interface name
185 device["Interface"] = ""
186 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
188 device["Interface"] = \
189 ",".join(os.listdir(os.path.join(base, "net")))
191 # check if a port is used for ssh connection
192 device["Ssh_if"] = False
193 device["Active"] = ""
198 '''This function clears any old data'''
201 def get_device_details(devices_type):
202 '''This function populates the "devices" dictionary. The keys used are
203 the pci addresses (domain:bus:slot.func). The values are themselves
204 dictionaries - one for each NIC.'''
208 # first loop through and read details for all devices
209 # request machine readable format, with numeric IDs and String
211 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
212 for dev_line in dev_lines:
213 if len(dev_line) == 0:
214 if device_type_match(dev, devices_type):
215 # Replace "Driver" with "Driver_str" to have consistency of
216 # of dictionary key names
217 if "Driver" in dev.keys():
218 dev["Driver_str"] = dev.pop("Driver")
219 if "Module" in dev.keys():
220 dev["Module_str"] = dev.pop("Module")
221 # use dict to make copy of dev
222 devices[dev["Slot"]] = dict(dev)
223 # Clear previous device's data
226 name, value = dev_line.decode().split("\t", 1)
227 value_list = value.rsplit(' ', 1)
228 if len(value_list) > 1:
229 # String stored in <name>_str
230 dev[name.rstrip(":") + '_str'] = value_list[0]
232 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
233 .rstrip("]").lstrip("[")
235 if devices_type == network_devices:
236 # check what is the interface if any for an ssh connection if
237 # any to this host, so we can mark it later.
239 route = check_output(["ip", "-o", "route"])
240 # filter out all lines for 169.254 routes
241 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
242 route.decode().splitlines()))
243 rt_info = route.split()
244 for i in range(len(rt_info) - 1):
245 if rt_info[i] == "dev":
246 ssh_if.append(rt_info[i+1])
248 # based on the basic info, get extended text details
249 for d in devices.keys():
250 if not device_type_match(devices[d], devices_type):
253 # get additional info and add it to existing data
254 devices[d] = devices[d].copy()
255 # No need to probe lspci
256 devices[d].update(get_pci_device_details(d, False).items())
258 if devices_type == network_devices:
260 if _if in devices[d]["Interface"].split(","):
261 devices[d]["Ssh_if"] = True
262 devices[d]["Active"] = "*Active*"
265 # add igb_uio to list of supporting modules if needed
266 if "Module_str" in devices[d]:
267 for driver in dpdk_drivers:
268 if driver not in devices[d]["Module_str"]:
269 devices[d]["Module_str"] = \
270 devices[d]["Module_str"] + ",%s" % driver
272 devices[d]["Module_str"] = ",".join(dpdk_drivers)
274 # make sure the driver and module strings do not have any duplicates
276 modules = devices[d]["Module_str"].split(",")
277 if devices[d]["Driver_str"] in modules:
278 modules.remove(devices[d]["Driver_str"])
279 devices[d]["Module_str"] = ",".join(modules)
282 def device_type_match(dev, devices_type):
283 for i in range(len(devices_type)):
285 [x for x in devices_type[i].values() if x is not None])
287 if dev["Class"][0:2] == devices_type[i]["Class"]:
288 match_count = match_count + 1
289 for key in devices_type[i].keys():
290 if key != 'Class' and devices_type[i][key]:
291 value_list = devices_type[i][key].split(',')
292 for value in value_list:
293 if value.strip(' ') == dev[key]:
294 match_count = match_count + 1
295 # count must be the number of non None parameters to match
296 if match_count == param_count:
300 def dev_id_from_dev_name(dev_name):
301 '''Take a device "name" - a string passed in by user to identify a NIC
302 device, and determine the device id - i.e. the domain:bus:slot.func - for
303 it, which can then be used to index into the devices array'''
305 # check if it's already a suitable index
306 if dev_name in devices:
308 # check if it's an index just missing the domain part
309 elif "0000:" + dev_name in devices:
310 return "0000:" + dev_name
312 # check if it's an interface name, e.g. eth1
313 for d in devices.keys():
314 if dev_name in devices[d]["Interface"].split(","):
315 return devices[d]["Slot"]
316 # if nothing else matches - error
317 print("Unknown device: %s. "
318 "Please specify device in \"bus:slot.func\" format" % dev_name)
322 def unbind_one(dev_id, force):
323 '''Unbind the device identified by "dev_id" from its current driver'''
324 dev = devices[dev_id]
325 if not has_driver(dev_id):
326 print("%s %s %s is not currently managed by any driver\n" %
327 (dev["Slot"], dev["Device_str"], dev["Interface"]))
330 # prevent us disconnecting ourselves
331 if dev["Ssh_if"] and not force:
332 print("Routing table indicates that interface %s is active. "
333 "Skipping unbind" % (dev_id))
336 # write to /sys to unbind
337 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
339 f = open(filename, "a")
341 print("Error: unbind failed for %s - Cannot open %s"
342 % (dev_id, filename))
348 def bind_one(dev_id, driver, force):
349 '''Bind the device given by "dev_id" to the driver "driver". If the device
350 is already bound to a different driver, it will be unbound first'''
351 dev = devices[dev_id]
352 saved_driver = None # used to rollback any unbind in case of failure
354 # prevent disconnection of our ssh session
355 if dev["Ssh_if"] and not force:
356 print("Routing table indicates that interface %s is active. "
357 "Not modifying" % (dev_id))
360 # unbind any existing drivers we don't want
361 if has_driver(dev_id):
362 if dev["Driver_str"] == driver:
363 print("%s already bound to driver %s, skipping\n"
367 saved_driver = dev["Driver_str"]
368 unbind_one(dev_id, force)
369 dev["Driver_str"] = "" # clear driver string
371 # For kernels >= 3.15 driver_override can be used to specify the driver
372 # for a device rather than relying on the driver to provide a positive
373 # match of the device. The existing process of looking up
374 # the vendor and device ID, adding them to the driver new_id,
375 # will erroneously bind other devices too which has the additional burden
376 # of unbinding those devices
377 if driver in dpdk_drivers:
378 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
379 if os.path.exists(filename):
381 f = open(filename, "w")
383 print("Error: bind failed for %s - Cannot open %s"
384 % (dev_id, filename))
387 f.write("%s" % driver)
390 print("Error: bind failed for %s - Cannot write driver %s to "
391 "PCI ID " % (dev_id, driver))
393 # For kernels < 3.15 use new_id to add PCI id's to the driver
395 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
397 f = open(filename, "w")
399 print("Error: bind failed for %s - Cannot open %s"
400 % (dev_id, filename))
403 # Convert Device and Vendor Id to int to write to new_id
404 f.write("%04x %04x" % (int(dev["Vendor"],16),
405 int(dev["Device"], 16)))
408 print("Error: bind failed for %s - Cannot write new PCI ID to "
409 "driver %s" % (dev_id, driver))
412 # do the bind by writing to /sys
413 filename = "/sys/bus/pci/drivers/%s/bind" % driver
415 f = open(filename, "a")
417 print("Error: bind failed for %s - Cannot open %s"
418 % (dev_id, filename))
419 if saved_driver is not None: # restore any previous driver
420 bind_one(dev_id, saved_driver, force)
426 # for some reason, closing dev_id after adding a new PCI ID to new_id
427 # results in IOError. however, if the device was successfully bound,
428 # we don't care for any errors and can safely ignore IOError
429 tmp = get_pci_device_details(dev_id, True)
430 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
432 print("Error: bind failed for %s - Cannot bind to driver %s"
434 if saved_driver is not None: # restore any previous driver
435 bind_one(dev_id, saved_driver, force)
438 # For kernels > 3.15 driver_override is used to bind a device to a driver.
439 # Before unbinding it, overwrite driver_override with empty string so that
440 # the device can be bound to any other driver
441 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
442 if os.path.exists(filename):
444 f = open(filename, "w")
446 print("Error: unbind failed for %s - Cannot open %s"
447 % (dev_id, filename))
453 print("Error: unbind failed for %s - Cannot open %s"
454 % (dev_id, filename))
458 def unbind_all(dev_list, force=False):
459 """Unbind method, takes a list of device locations"""
461 if dev_list[0] == "dpdk":
462 for d in devices.keys():
463 if "Driver_str" in devices[d]:
464 if devices[d]["Driver_str"] in dpdk_drivers:
465 unbind_one(devices[d]["Slot"], force)
468 dev_list = map(dev_id_from_dev_name, dev_list)
473 def bind_all(dev_list, driver, force=False):
474 """Bind method, takes a list of device locations"""
477 dev_list = map(dev_id_from_dev_name, dev_list)
480 bind_one(d, driver, force)
482 # For kernels < 3.15 when binding devices to a generic driver
483 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
484 # that are not bound to any other driver could be bound even if no one has
485 # asked them to. hence, we check the list of drivers again, and see if
486 # some of the previously-unbound devices were erroneously bound.
487 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
488 for d in devices.keys():
489 # skip devices that were already bound or that we know should be bound
490 if "Driver_str" in devices[d] or d in dev_list:
493 # update information about this device
494 devices[d] = dict(devices[d].items() +
495 get_pci_device_details(d, True).items())
497 # check if updated information indicates that the device was bound
498 if "Driver_str" in devices[d]:
502 def display_devices(title, dev_list, extra_params=None):
503 '''Displays to the user the details of a list of devices given in
504 "dev_list". The "extra_params" parameter, if given, should contain a string
505 with %()s fields in it for replacement by the named fields in each
506 device's dictionary.'''
507 strings = [] # this holds the strings to print. We sort before printing
508 print("\n%s" % title)
509 print("="*len(title))
510 if len(dev_list) == 0:
511 strings.append("<none>")
514 if extra_params is not None:
515 strings.append("%s '%s %s' %s" % (dev["Slot"],
520 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
521 # sort before printing, so that the entries appear in PCI order
523 print("\n".join(strings)) # print one per line
525 def show_device_status(devices_type, device_name):
531 # split our list of network devices into the three categories above
532 for d in devices.keys():
533 if device_type_match(devices[d], devices_type):
534 if not has_driver(d):
535 no_drv.append(devices[d])
537 if devices[d]["Driver_str"] in dpdk_drivers:
538 dpdk_drv.append(devices[d])
540 kernel_drv.append(devices[d])
542 # print each category separately, so we can clearly see what's used by DPDK
543 display_devices("%s devices using DPDK-compatible driver" % device_name,
544 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
545 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
546 "if=%(Interface)s drv=%(Driver_str)s "
547 "unused=%(Module_str)s %(Active)s")
548 display_devices("Other %s devices" % device_name, no_drv,
549 "unused=%(Module_str)s")
552 '''Function called when the script is passed the "--status" option.
553 Displays to the user what devices are bound to the igb_uio driver, the
554 kernel driver or to no driver'''
556 if status_dev == "net" or status_dev == "all":
557 show_device_status(network_devices, "Network")
559 if status_dev == "crypto" or status_dev == "all":
560 show_device_status(crypto_devices, "Crypto")
562 if status_dev == "event" or status_dev == "all":
563 show_device_status(eventdev_devices, "Eventdev")
565 if status_dev == "mempool" or status_dev == "all":
566 show_device_status(mempool_devices, "Mempool")
569 '''Parses the command-line arguments given by the user and takes the
570 appropriate action for each'''
576 if len(sys.argv) <= 1:
581 opts, args = getopt.getopt(sys.argv[1:], "b:us",
582 ["help", "usage", "status", "status-dev=",
583 "force", "bind=", "unbind", ])
584 except getopt.GetoptError as error:
586 print("Run '%s --usage' for further information" % sys.argv[0])
589 for opt, arg in opts:
590 if opt == "--help" or opt == "--usage":
593 if opt == "--status-dev":
596 if opt == "--status" or opt == "-s":
601 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
602 if b_flag is not None:
603 print("Error - Only one bind or unbind may be specified\n")
605 if opt == "-u" or opt == "--unbind":
611 def do_arg_actions():
612 '''do the actual action requested by the user'''
618 if b_flag is None and not status_flag:
619 print("Error: No action specified for devices."
620 "Please give a -b or -u option")
621 print("Run '%s --usage' for further information" % sys.argv[0])
624 if b_flag is not None and len(args) == 0:
625 print("Error: No devices specified.")
626 print("Run '%s --usage' for further information" % sys.argv[0])
629 if b_flag == "none" or b_flag == "None":
630 unbind_all(args, force_flag)
631 elif b_flag is not None:
632 bind_all(args, b_flag, force_flag)
634 if b_flag is not None:
636 # refresh if we have changed anything
637 get_device_details(network_devices)
638 get_device_details(crypto_devices)
639 get_device_details(eventdev_devices)
640 get_device_details(mempool_devices)
645 '''program main function'''
649 get_device_details(network_devices)
650 get_device_details(crypto_devices)
651 get_device_details(eventdev_devices)
652 get_device_details(mempool_devices)
655 if __name__ == "__main__":