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 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
26 'SVendor': None, 'SDevice': None}
28 network_devices = [network_class, cavium_pkx, avp_vnic]
29 crypto_devices = [encryption_class, intel_processor_class]
30 eventdev_devices = [cavium_sso]
31 mempool_devices = [cavium_fpa]
33 # global dict ethernet devices present. Dictionary indexed by PCI address.
34 # Each device within this is itself a dictionary of device properties
36 # list of supported DPDK drivers
37 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
39 # command-line arg flags
47 '''Print usage information for the program'''
48 argv0 = basename(sys.argv[0])
53 %(argv0)s [options] DEVICE1 DEVICE2 ....
55 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
56 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
57 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
61 Display usage information and quit
64 Print the current status of all known network, crypto, event
66 For each device, it displays the PCI domain, bus, slot and function,
67 along with a text description of the device. Depending upon whether the
68 device is being used by a kernel driver, the igb_uio driver, or no
69 driver, other relevant information will be displayed:
70 * the Linux interface name e.g. if=eth0
71 * the driver being used e.g. drv=igb_uio
72 * any suitable drivers not currently using that device
74 NOTE: if this flag is passed along with a bind/unbind option, the
75 status display will always occur after the other operations have taken
79 Print the status of given device group. Supported device groups are:
80 "net", "crypto", "event" and "mempool"
82 -b driver, --bind=driver:
83 Select the driver to use or \"none\" to unbind the device
86 Unbind a device (Equivalent to \"-b none\")
89 By default, network devices which are used by Linux - as indicated by
90 having routes in the routing table - cannot be modified. Using the
91 --force flag overrides this behavior, allowing active links to be
93 WARNING: This can lead to loss of network connection and should be used
99 To display current device status:
102 To display current network device status:
103 %(argv0)s --status-dev net
105 To bind eth1 from the current driver and move to use igb_uio
106 %(argv0)s --bind=igb_uio eth1
108 To unbind 0000:01:00.0 from using any driver
109 %(argv0)s -u 0000:01:00.0
111 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
112 %(argv0)s -b ixgbe 02:00.0 02:00.1
114 """ % locals()) # replace items from local variables
117 # This is roughly compatible with check_output function in subprocess module
118 # which is only available in python 2.7.
119 def check_output(args, stderr=None):
120 '''Run a command and capture its output'''
121 return subprocess.Popen(args, stdout=subprocess.PIPE,
122 stderr=stderr).communicate()[0]
126 '''Checks that igb_uio is loaded'''
129 # list of supported modules
130 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
132 # first check if module is loaded
134 # Get list of sysfs modules (both built-in and dynamically loaded)
135 sysfs_path = '/sys/module/'
137 # Get the list of directories in sysfs_path
138 sysfs_mods = [os.path.join(sysfs_path, o) for o
139 in os.listdir(sysfs_path)
140 if os.path.isdir(os.path.join(sysfs_path, o))]
142 # Extract the last element of '/sys/module/abc' in the array
143 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
145 # special case for vfio_pci (module is named vfio-pci,
146 # but its .ko is named vfio_pci)
147 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
150 if mod["Name"] in sysfs_mods:
155 # check if we have at least one loaded module
156 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
157 if b_flag in dpdk_drivers:
158 print("Error - no supported modules(DPDK driver) are loaded")
161 print("Warning - no supported modules(DPDK driver) are loaded")
163 # change DPDK driver list to only contain drivers that are loaded
164 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
167 def has_driver(dev_id):
168 '''return true if a device is assigned to a driver. False otherwise'''
169 return "Driver_str" in devices[dev_id]
172 def get_pci_device_details(dev_id, probe_lspci):
173 '''This function gets additional details for a PCI device'''
177 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
179 # parse lspci details
180 for line in extra_info:
183 name, value = line.decode().split("\t", 1)
184 name = name.strip(":") + "_str"
186 # check for a unix interface name
187 device["Interface"] = ""
188 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
190 device["Interface"] = \
191 ",".join(os.listdir(os.path.join(base, "net")))
193 # check if a port is used for ssh connection
194 device["Ssh_if"] = False
195 device["Active"] = ""
200 '''This function clears any old data'''
203 def get_device_details(devices_type):
204 '''This function populates the "devices" dictionary. The keys used are
205 the pci addresses (domain:bus:slot.func). The values are themselves
206 dictionaries - one for each NIC.'''
210 # first loop through and read details for all devices
211 # request machine readable format, with numeric IDs and String
213 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
214 for dev_line in dev_lines:
215 if len(dev_line) == 0:
216 if device_type_match(dev, devices_type):
217 # Replace "Driver" with "Driver_str" to have consistency of
218 # of dictionary key names
219 if "Driver" in dev.keys():
220 dev["Driver_str"] = dev.pop("Driver")
221 if "Module" in dev.keys():
222 dev["Module_str"] = dev.pop("Module")
223 # use dict to make copy of dev
224 devices[dev["Slot"]] = dict(dev)
225 # Clear previous device's data
228 name, value = dev_line.decode().split("\t", 1)
229 value_list = value.rsplit(' ', 1)
230 if len(value_list) > 1:
231 # String stored in <name>_str
232 dev[name.rstrip(":") + '_str'] = value_list[0]
234 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
235 .rstrip("]").lstrip("[")
237 if devices_type == network_devices:
238 # check what is the interface if any for an ssh connection if
239 # any to this host, so we can mark it later.
241 route = check_output(["ip", "-o", "route"])
242 # filter out all lines for 169.254 routes
243 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
244 route.decode().splitlines()))
245 rt_info = route.split()
246 for i in range(len(rt_info) - 1):
247 if rt_info[i] == "dev":
248 ssh_if.append(rt_info[i+1])
250 # based on the basic info, get extended text details
251 for d in devices.keys():
252 if not device_type_match(devices[d], devices_type):
255 # get additional info and add it to existing data
256 devices[d] = devices[d].copy()
257 # No need to probe lspci
258 devices[d].update(get_pci_device_details(d, False).items())
260 if devices_type == network_devices:
262 if _if in devices[d]["Interface"].split(","):
263 devices[d]["Ssh_if"] = True
264 devices[d]["Active"] = "*Active*"
267 # add igb_uio to list of supporting modules if needed
268 if "Module_str" in devices[d]:
269 for driver in dpdk_drivers:
270 if driver not in devices[d]["Module_str"]:
271 devices[d]["Module_str"] = \
272 devices[d]["Module_str"] + ",%s" % driver
274 devices[d]["Module_str"] = ",".join(dpdk_drivers)
276 # make sure the driver and module strings do not have any duplicates
278 modules = devices[d]["Module_str"].split(",")
279 if devices[d]["Driver_str"] in modules:
280 modules.remove(devices[d]["Driver_str"])
281 devices[d]["Module_str"] = ",".join(modules)
284 def device_type_match(dev, devices_type):
285 for i in range(len(devices_type)):
287 [x for x in devices_type[i].values() if x is not None])
289 if dev["Class"][0:2] == devices_type[i]["Class"]:
290 match_count = match_count + 1
291 for key in devices_type[i].keys():
292 if key != 'Class' and devices_type[i][key]:
293 value_list = devices_type[i][key].split(',')
294 for value in value_list:
295 if value.strip(' ') == dev[key]:
296 match_count = match_count + 1
297 # count must be the number of non None parameters to match
298 if match_count == param_count:
302 def dev_id_from_dev_name(dev_name):
303 '''Take a device "name" - a string passed in by user to identify a NIC
304 device, and determine the device id - i.e. the domain:bus:slot.func - for
305 it, which can then be used to index into the devices array'''
307 # check if it's already a suitable index
308 if dev_name in devices:
310 # check if it's an index just missing the domain part
311 elif "0000:" + dev_name in devices:
312 return "0000:" + dev_name
314 # check if it's an interface name, e.g. eth1
315 for d in devices.keys():
316 if dev_name in devices[d]["Interface"].split(","):
317 return devices[d]["Slot"]
318 # if nothing else matches - error
319 print("Unknown device: %s. "
320 "Please specify device in \"bus:slot.func\" format" % dev_name)
324 def unbind_one(dev_id, force):
325 '''Unbind the device identified by "dev_id" from its current driver'''
326 dev = devices[dev_id]
327 if not has_driver(dev_id):
328 print("%s %s %s is not currently managed by any driver\n" %
329 (dev["Slot"], dev["Device_str"], dev["Interface"]))
332 # prevent us disconnecting ourselves
333 if dev["Ssh_if"] and not force:
334 print("Routing table indicates that interface %s is active. "
335 "Skipping unbind" % (dev_id))
338 # write to /sys to unbind
339 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
341 f = open(filename, "a")
343 print("Error: unbind failed for %s - Cannot open %s"
344 % (dev_id, filename))
350 def bind_one(dev_id, driver, force):
351 '''Bind the device given by "dev_id" to the driver "driver". If the device
352 is already bound to a different driver, it will be unbound first'''
353 dev = devices[dev_id]
354 saved_driver = None # used to rollback any unbind in case of failure
356 # prevent disconnection of our ssh session
357 if dev["Ssh_if"] and not force:
358 print("Routing table indicates that interface %s is active. "
359 "Not modifying" % (dev_id))
362 # unbind any existing drivers we don't want
363 if has_driver(dev_id):
364 if dev["Driver_str"] == driver:
365 print("%s already bound to driver %s, skipping\n"
369 saved_driver = dev["Driver_str"]
370 unbind_one(dev_id, force)
371 dev["Driver_str"] = "" # clear driver string
373 # For kernels >= 3.15 driver_override can be used to specify the driver
374 # for a device rather than relying on the driver to provide a positive
375 # match of the device. The existing process of looking up
376 # the vendor and device ID, adding them to the driver new_id,
377 # will erroneously bind other devices too which has the additional burden
378 # of unbinding those devices
379 if driver in dpdk_drivers:
380 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
381 if os.path.exists(filename):
383 f = open(filename, "w")
385 print("Error: bind failed for %s - Cannot open %s"
386 % (dev_id, filename))
389 f.write("%s" % driver)
392 print("Error: bind failed for %s - Cannot write driver %s to "
393 "PCI ID " % (dev_id, driver))
395 # For kernels < 3.15 use new_id to add PCI id's to the driver
397 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
399 f = open(filename, "w")
401 print("Error: bind failed for %s - Cannot open %s"
402 % (dev_id, filename))
405 # Convert Device and Vendor Id to int to write to new_id
406 f.write("%04x %04x" % (int(dev["Vendor"],16),
407 int(dev["Device"], 16)))
410 print("Error: bind failed for %s - Cannot write new PCI ID to "
411 "driver %s" % (dev_id, driver))
414 # do the bind by writing to /sys
415 filename = "/sys/bus/pci/drivers/%s/bind" % driver
417 f = open(filename, "a")
419 print("Error: bind failed for %s - Cannot open %s"
420 % (dev_id, filename))
421 if saved_driver is not None: # restore any previous driver
422 bind_one(dev_id, saved_driver, force)
428 # for some reason, closing dev_id after adding a new PCI ID to new_id
429 # results in IOError. however, if the device was successfully bound,
430 # we don't care for any errors and can safely ignore IOError
431 tmp = get_pci_device_details(dev_id, True)
432 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
434 print("Error: bind failed for %s - Cannot bind to driver %s"
436 if saved_driver is not None: # restore any previous driver
437 bind_one(dev_id, saved_driver, force)
440 # For kernels > 3.15 driver_override is used to bind a device to a driver.
441 # Before unbinding it, overwrite driver_override with empty string so that
442 # the device can be bound to any other driver
443 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
444 if os.path.exists(filename):
446 f = open(filename, "w")
448 print("Error: unbind failed for %s - Cannot open %s"
449 % (dev_id, filename))
455 print("Error: unbind failed for %s - Cannot open %s"
456 % (dev_id, filename))
460 def unbind_all(dev_list, force=False):
461 """Unbind method, takes a list of device locations"""
463 if dev_list[0] == "dpdk":
464 for d in devices.keys():
465 if "Driver_str" in devices[d]:
466 if devices[d]["Driver_str"] in dpdk_drivers:
467 unbind_one(devices[d]["Slot"], force)
470 dev_list = map(dev_id_from_dev_name, dev_list)
475 def bind_all(dev_list, driver, force=False):
476 """Bind method, takes a list of device locations"""
479 dev_list = map(dev_id_from_dev_name, dev_list)
482 bind_one(d, driver, force)
484 # For kernels < 3.15 when binding devices to a generic driver
485 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
486 # that are not bound to any other driver could be bound even if no one has
487 # asked them to. hence, we check the list of drivers again, and see if
488 # some of the previously-unbound devices were erroneously bound.
489 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
490 for d in devices.keys():
491 # skip devices that were already bound or that we know should be bound
492 if "Driver_str" in devices[d] or d in dev_list:
495 # update information about this device
496 devices[d] = dict(devices[d].items() +
497 get_pci_device_details(d, True).items())
499 # check if updated information indicates that the device was bound
500 if "Driver_str" in devices[d]:
504 def display_devices(title, dev_list, extra_params=None):
505 '''Displays to the user the details of a list of devices given in
506 "dev_list". The "extra_params" parameter, if given, should contain a string
507 with %()s fields in it for replacement by the named fields in each
508 device's dictionary.'''
509 strings = [] # this holds the strings to print. We sort before printing
510 print("\n%s" % title)
511 print("="*len(title))
512 if len(dev_list) == 0:
513 strings.append("<none>")
516 if extra_params is not None:
517 strings.append("%s '%s %s' %s" % (dev["Slot"],
522 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
523 # sort before printing, so that the entries appear in PCI order
525 print("\n".join(strings)) # print one per line
527 def show_device_status(devices_type, device_name):
533 # split our list of network devices into the three categories above
534 for d in devices.keys():
535 if device_type_match(devices[d], devices_type):
536 if not has_driver(d):
537 no_drv.append(devices[d])
539 if devices[d]["Driver_str"] in dpdk_drivers:
540 dpdk_drv.append(devices[d])
542 kernel_drv.append(devices[d])
544 # print each category separately, so we can clearly see what's used by DPDK
545 display_devices("%s devices using DPDK-compatible driver" % device_name,
546 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
547 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
548 "if=%(Interface)s drv=%(Driver_str)s "
549 "unused=%(Module_str)s %(Active)s")
550 display_devices("Other %s devices" % device_name, no_drv,
551 "unused=%(Module_str)s")
554 '''Function called when the script is passed the "--status" option.
555 Displays to the user what devices are bound to the igb_uio driver, the
556 kernel driver or to no driver'''
558 if status_dev == "net" or status_dev == "all":
559 show_device_status(network_devices, "Network")
561 if status_dev == "crypto" or status_dev == "all":
562 show_device_status(crypto_devices, "Crypto")
564 if status_dev == "event" or status_dev == "all":
565 show_device_status(eventdev_devices, "Eventdev")
567 if status_dev == "mempool" or status_dev == "all":
568 show_device_status(mempool_devices, "Mempool")
571 '''Parses the command-line arguments given by the user and takes the
572 appropriate action for each'''
578 if len(sys.argv) <= 1:
583 opts, args = getopt.getopt(sys.argv[1:], "b:us",
584 ["help", "usage", "status", "status-dev=",
585 "force", "bind=", "unbind", ])
586 except getopt.GetoptError as error:
588 print("Run '%s --usage' for further information" % sys.argv[0])
591 for opt, arg in opts:
592 if opt == "--help" or opt == "--usage":
595 if opt == "--status-dev":
598 if opt == "--status" or opt == "-s":
603 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
604 if b_flag is not None:
605 print("Error - Only one bind or unbind may be specified\n")
607 if opt == "-u" or opt == "--unbind":
613 def do_arg_actions():
614 '''do the actual action requested by the user'''
620 if b_flag is None and not status_flag:
621 print("Error: No action specified for devices."
622 "Please give a -b or -u option")
623 print("Run '%s --usage' for further information" % sys.argv[0])
626 if b_flag is not None and len(args) == 0:
627 print("Error: No devices specified.")
628 print("Run '%s --usage' for further information" % sys.argv[0])
631 if b_flag == "none" or b_flag == "None":
632 unbind_all(args, force_flag)
633 elif b_flag is not None:
634 bind_all(args, b_flag, force_flag)
636 if b_flag is not None:
638 # refresh if we have changed anything
639 get_device_details(network_devices)
640 get_device_details(crypto_devices)
641 get_device_details(eventdev_devices)
642 get_device_details(mempool_devices)
647 '''program main function'''
651 get_device_details(network_devices)
652 get_device_details(crypto_devices)
653 get_device_details(eventdev_devices)
654 get_device_details(mempool_devices)
657 if __name__ == "__main__":