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 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
28 'SVendor': None, 'SDevice': None}
30 network_devices = [network_class, cavium_pkx, avp_vnic]
31 crypto_devices = [encryption_class, intel_processor_class]
32 eventdev_devices = [cavium_sso, cavium_tim]
33 mempool_devices = [cavium_fpa]
35 # global dict ethernet devices present. Dictionary indexed by PCI address.
36 # Each device within this is itself a dictionary of device properties
38 # list of supported DPDK drivers
39 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
41 # command-line arg flags
49 '''Print usage information for the program'''
50 argv0 = basename(sys.argv[0])
55 %(argv0)s [options] DEVICE1 DEVICE2 ....
57 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
58 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
59 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
63 Display usage information and quit
66 Print the current status of all known network, crypto, event
68 For each device, it displays the PCI domain, bus, slot and function,
69 along with a text description of the device. Depending upon whether the
70 device is being used by a kernel driver, the igb_uio driver, or no
71 driver, other relevant information will be displayed:
72 * the Linux interface name e.g. if=eth0
73 * the driver being used e.g. drv=igb_uio
74 * any suitable drivers not currently using that device
76 NOTE: if this flag is passed along with a bind/unbind option, the
77 status display will always occur after the other operations have taken
81 Print the status of given device group. Supported device groups are:
82 "net", "crypto", "event" and "mempool"
84 -b driver, --bind=driver:
85 Select the driver to use or \"none\" to unbind the device
88 Unbind a device (Equivalent to \"-b none\")
91 By default, network devices which are used by Linux - as indicated by
92 having routes in the routing table - cannot be modified. Using the
93 --force flag overrides this behavior, allowing active links to be
95 WARNING: This can lead to loss of network connection and should be used
101 To display current device status:
104 To display current network device status:
105 %(argv0)s --status-dev net
107 To bind eth1 from the current driver and move to use igb_uio
108 %(argv0)s --bind=igb_uio eth1
110 To unbind 0000:01:00.0 from using any driver
111 %(argv0)s -u 0000:01:00.0
113 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
114 %(argv0)s -b ixgbe 02:00.0 02:00.1
116 """ % locals()) # replace items from local variables
119 # This is roughly compatible with check_output function in subprocess module
120 # which is only available in python 2.7.
121 def check_output(args, stderr=None):
122 '''Run a command and capture its output'''
123 return subprocess.Popen(args, stdout=subprocess.PIPE,
124 stderr=stderr).communicate()[0]
128 '''Checks that igb_uio is loaded'''
131 # list of supported modules
132 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
134 # first check if module is loaded
136 # Get list of sysfs modules (both built-in and dynamically loaded)
137 sysfs_path = '/sys/module/'
139 # Get the list of directories in sysfs_path
140 sysfs_mods = [os.path.join(sysfs_path, o) for o
141 in os.listdir(sysfs_path)
142 if os.path.isdir(os.path.join(sysfs_path, o))]
144 # Extract the last element of '/sys/module/abc' in the array
145 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
147 # special case for vfio_pci (module is named vfio-pci,
148 # but its .ko is named vfio_pci)
149 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
152 if mod["Name"] in sysfs_mods:
157 # check if we have at least one loaded module
158 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
159 if b_flag in dpdk_drivers:
160 print("Error - no supported modules(DPDK driver) are loaded")
163 print("Warning - no supported modules(DPDK driver) are loaded")
165 # change DPDK driver list to only contain drivers that are loaded
166 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
169 def has_driver(dev_id):
170 '''return true if a device is assigned to a driver. False otherwise'''
171 return "Driver_str" in devices[dev_id]
174 def get_pci_device_details(dev_id, probe_lspci):
175 '''This function gets additional details for a PCI device'''
179 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
181 # parse lspci details
182 for line in extra_info:
185 name, value = line.decode().split("\t", 1)
186 name = name.strip(":") + "_str"
188 # check for a unix interface name
189 device["Interface"] = ""
190 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
192 device["Interface"] = \
193 ",".join(os.listdir(os.path.join(base, "net")))
195 # check if a port is used for ssh connection
196 device["Ssh_if"] = False
197 device["Active"] = ""
202 '''This function clears any old data'''
205 def get_device_details(devices_type):
206 '''This function populates the "devices" dictionary. The keys used are
207 the pci addresses (domain:bus:slot.func). The values are themselves
208 dictionaries - one for each NIC.'''
212 # first loop through and read details for all devices
213 # request machine readable format, with numeric IDs and String
215 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
216 for dev_line in dev_lines:
217 if len(dev_line) == 0:
218 if device_type_match(dev, devices_type):
219 # Replace "Driver" with "Driver_str" to have consistency of
220 # of dictionary key names
221 if "Driver" in dev.keys():
222 dev["Driver_str"] = dev.pop("Driver")
223 if "Module" in dev.keys():
224 dev["Module_str"] = dev.pop("Module")
225 # use dict to make copy of dev
226 devices[dev["Slot"]] = dict(dev)
227 # Clear previous device's data
230 name, value = dev_line.decode().split("\t", 1)
231 value_list = value.rsplit(' ', 1)
232 if len(value_list) > 1:
233 # String stored in <name>_str
234 dev[name.rstrip(":") + '_str'] = value_list[0]
236 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
237 .rstrip("]").lstrip("[")
239 if devices_type == network_devices:
240 # check what is the interface if any for an ssh connection if
241 # any to this host, so we can mark it later.
243 route = check_output(["ip", "-o", "route"])
244 # filter out all lines for 169.254 routes
245 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
246 route.decode().splitlines()))
247 rt_info = route.split()
248 for i in range(len(rt_info) - 1):
249 if rt_info[i] == "dev":
250 ssh_if.append(rt_info[i+1])
252 # based on the basic info, get extended text details
253 for d in devices.keys():
254 if not device_type_match(devices[d], devices_type):
257 # get additional info and add it to existing data
258 devices[d] = devices[d].copy()
259 # No need to probe lspci
260 devices[d].update(get_pci_device_details(d, False).items())
262 if devices_type == network_devices:
264 if _if in devices[d]["Interface"].split(","):
265 devices[d]["Ssh_if"] = True
266 devices[d]["Active"] = "*Active*"
269 # add igb_uio to list of supporting modules if needed
270 if "Module_str" in devices[d]:
271 for driver in dpdk_drivers:
272 if driver not in devices[d]["Module_str"]:
273 devices[d]["Module_str"] = \
274 devices[d]["Module_str"] + ",%s" % driver
276 devices[d]["Module_str"] = ",".join(dpdk_drivers)
278 # make sure the driver and module strings do not have any duplicates
280 modules = devices[d]["Module_str"].split(",")
281 if devices[d]["Driver_str"] in modules:
282 modules.remove(devices[d]["Driver_str"])
283 devices[d]["Module_str"] = ",".join(modules)
286 def device_type_match(dev, devices_type):
287 for i in range(len(devices_type)):
289 [x for x in devices_type[i].values() if x is not None])
291 if dev["Class"][0:2] == devices_type[i]["Class"]:
292 match_count = match_count + 1
293 for key in devices_type[i].keys():
294 if key != 'Class' and devices_type[i][key]:
295 value_list = devices_type[i][key].split(',')
296 for value in value_list:
297 if value.strip(' ') == dev[key]:
298 match_count = match_count + 1
299 # count must be the number of non None parameters to match
300 if match_count == param_count:
304 def dev_id_from_dev_name(dev_name):
305 '''Take a device "name" - a string passed in by user to identify a NIC
306 device, and determine the device id - i.e. the domain:bus:slot.func - for
307 it, which can then be used to index into the devices array'''
309 # check if it's already a suitable index
310 if dev_name in devices:
312 # check if it's an index just missing the domain part
313 elif "0000:" + dev_name in devices:
314 return "0000:" + dev_name
316 # check if it's an interface name, e.g. eth1
317 for d in devices.keys():
318 if dev_name in devices[d]["Interface"].split(","):
319 return devices[d]["Slot"]
320 # if nothing else matches - error
321 print("Unknown device: %s. "
322 "Please specify device in \"bus:slot.func\" format" % dev_name)
326 def unbind_one(dev_id, force):
327 '''Unbind the device identified by "dev_id" from its current driver'''
328 dev = devices[dev_id]
329 if not has_driver(dev_id):
330 print("%s %s %s is not currently managed by any driver\n" %
331 (dev["Slot"], dev["Device_str"], dev["Interface"]))
334 # prevent us disconnecting ourselves
335 if dev["Ssh_if"] and not force:
336 print("Routing table indicates that interface %s is active. "
337 "Skipping unbind" % (dev_id))
340 # write to /sys to unbind
341 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
343 f = open(filename, "a")
345 print("Error: unbind failed for %s - Cannot open %s"
346 % (dev_id, filename))
352 def bind_one(dev_id, driver, force):
353 '''Bind the device given by "dev_id" to the driver "driver". If the device
354 is already bound to a different driver, it will be unbound first'''
355 dev = devices[dev_id]
356 saved_driver = None # used to rollback any unbind in case of failure
358 # prevent disconnection of our ssh session
359 if dev["Ssh_if"] and not force:
360 print("Routing table indicates that interface %s is active. "
361 "Not modifying" % (dev_id))
364 # unbind any existing drivers we don't want
365 if has_driver(dev_id):
366 if dev["Driver_str"] == driver:
367 print("%s already bound to driver %s, skipping\n"
371 saved_driver = dev["Driver_str"]
372 unbind_one(dev_id, force)
373 dev["Driver_str"] = "" # clear driver string
375 # For kernels >= 3.15 driver_override can be used to specify the driver
376 # for a device rather than relying on the driver to provide a positive
377 # match of the device. The existing process of looking up
378 # the vendor and device ID, adding them to the driver new_id,
379 # will erroneously bind other devices too which has the additional burden
380 # of unbinding those devices
381 if driver in dpdk_drivers:
382 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
383 if os.path.exists(filename):
385 f = open(filename, "w")
387 print("Error: bind failed for %s - Cannot open %s"
388 % (dev_id, filename))
391 f.write("%s" % driver)
394 print("Error: bind failed for %s - Cannot write driver %s to "
395 "PCI ID " % (dev_id, driver))
397 # For kernels < 3.15 use new_id to add PCI id's to the driver
399 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
401 f = open(filename, "w")
403 print("Error: bind failed for %s - Cannot open %s"
404 % (dev_id, filename))
407 # Convert Device and Vendor Id to int to write to new_id
408 f.write("%04x %04x" % (int(dev["Vendor"],16),
409 int(dev["Device"], 16)))
412 print("Error: bind failed for %s - Cannot write new PCI ID to "
413 "driver %s" % (dev_id, driver))
416 # do the bind by writing to /sys
417 filename = "/sys/bus/pci/drivers/%s/bind" % driver
419 f = open(filename, "a")
421 print("Error: bind failed for %s - Cannot open %s"
422 % (dev_id, filename))
423 if saved_driver is not None: # restore any previous driver
424 bind_one(dev_id, saved_driver, force)
430 # for some reason, closing dev_id after adding a new PCI ID to new_id
431 # results in IOError. however, if the device was successfully bound,
432 # we don't care for any errors and can safely ignore IOError
433 tmp = get_pci_device_details(dev_id, True)
434 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
436 print("Error: bind failed for %s - Cannot bind to driver %s"
438 if saved_driver is not None: # restore any previous driver
439 bind_one(dev_id, saved_driver, force)
442 # For kernels > 3.15 driver_override is used to bind a device to a driver.
443 # Before unbinding it, overwrite driver_override with empty string so that
444 # the device can be bound to any other driver
445 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
446 if os.path.exists(filename):
448 f = open(filename, "w")
450 print("Error: unbind failed for %s - Cannot open %s"
451 % (dev_id, filename))
457 print("Error: unbind failed for %s - Cannot open %s"
458 % (dev_id, filename))
462 def unbind_all(dev_list, force=False):
463 """Unbind method, takes a list of device locations"""
465 if dev_list[0] == "dpdk":
466 for d in devices.keys():
467 if "Driver_str" in devices[d]:
468 if devices[d]["Driver_str"] in dpdk_drivers:
469 unbind_one(devices[d]["Slot"], force)
472 dev_list = map(dev_id_from_dev_name, dev_list)
477 def bind_all(dev_list, driver, force=False):
478 """Bind method, takes a list of device locations"""
481 dev_list = map(dev_id_from_dev_name, dev_list)
484 bind_one(d, driver, force)
486 # For kernels < 3.15 when binding devices to a generic driver
487 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
488 # that are not bound to any other driver could be bound even if no one has
489 # asked them to. hence, we check the list of drivers again, and see if
490 # some of the previously-unbound devices were erroneously bound.
491 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
492 for d in devices.keys():
493 # skip devices that were already bound or that we know should be bound
494 if "Driver_str" in devices[d] or d in dev_list:
497 # update information about this device
498 devices[d] = dict(devices[d].items() +
499 get_pci_device_details(d, True).items())
501 # check if updated information indicates that the device was bound
502 if "Driver_str" in devices[d]:
506 def display_devices(title, dev_list, extra_params=None):
507 '''Displays to the user the details of a list of devices given in
508 "dev_list". The "extra_params" parameter, if given, should contain a string
509 with %()s fields in it for replacement by the named fields in each
510 device's dictionary.'''
511 strings = [] # this holds the strings to print. We sort before printing
512 print("\n%s" % title)
513 print("="*len(title))
514 if len(dev_list) == 0:
515 strings.append("<none>")
518 if extra_params is not None:
519 strings.append("%s '%s %s' %s" % (dev["Slot"],
524 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
525 # sort before printing, so that the entries appear in PCI order
527 print("\n".join(strings)) # print one per line
529 def show_device_status(devices_type, device_name):
535 # split our list of network devices into the three categories above
536 for d in devices.keys():
537 if device_type_match(devices[d], devices_type):
538 if not has_driver(d):
539 no_drv.append(devices[d])
541 if devices[d]["Driver_str"] in dpdk_drivers:
542 dpdk_drv.append(devices[d])
544 kernel_drv.append(devices[d])
546 # print each category separately, so we can clearly see what's used by DPDK
547 display_devices("%s devices using DPDK-compatible driver" % device_name,
548 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
549 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
550 "if=%(Interface)s drv=%(Driver_str)s "
551 "unused=%(Module_str)s %(Active)s")
552 display_devices("Other %s devices" % device_name, no_drv,
553 "unused=%(Module_str)s")
556 '''Function called when the script is passed the "--status" option.
557 Displays to the user what devices are bound to the igb_uio driver, the
558 kernel driver or to no driver'''
560 if status_dev == "net" or status_dev == "all":
561 show_device_status(network_devices, "Network")
563 if status_dev == "crypto" or status_dev == "all":
564 show_device_status(crypto_devices, "Crypto")
566 if status_dev == "event" or status_dev == "all":
567 show_device_status(eventdev_devices, "Eventdev")
569 if status_dev == "mempool" or status_dev == "all":
570 show_device_status(mempool_devices, "Mempool")
573 '''Parses the command-line arguments given by the user and takes the
574 appropriate action for each'''
580 if len(sys.argv) <= 1:
585 opts, args = getopt.getopt(sys.argv[1:], "b:us",
586 ["help", "usage", "status", "status-dev=",
587 "force", "bind=", "unbind", ])
588 except getopt.GetoptError as error:
590 print("Run '%s --usage' for further information" % sys.argv[0])
593 for opt, arg in opts:
594 if opt == "--help" or opt == "--usage":
597 if opt == "--status-dev":
600 if opt == "--status" or opt == "-s":
605 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
606 if b_flag is not None:
607 print("Error - Only one bind or unbind may be specified\n")
609 if opt == "-u" or opt == "--unbind":
615 def do_arg_actions():
616 '''do the actual action requested by the user'''
622 if b_flag is None and not status_flag:
623 print("Error: No action specified for devices."
624 "Please give a -b or -u option")
625 print("Run '%s --usage' for further information" % sys.argv[0])
628 if b_flag is not None and len(args) == 0:
629 print("Error: No devices specified.")
630 print("Run '%s --usage' for further information" % sys.argv[0])
633 if b_flag == "none" or b_flag == "None":
634 unbind_all(args, force_flag)
635 elif b_flag is not None:
636 bind_all(args, b_flag, force_flag)
638 if b_flag is not None:
640 # refresh if we have changed anything
641 get_device_details(network_devices)
642 get_device_details(crypto_devices)
643 get_device_details(eventdev_devices)
644 get_device_details(mempool_devices)
649 '''program main function'''
653 get_device_details(network_devices)
654 get_device_details(crypto_devices)
655 get_device_details(eventdev_devices)
656 get_device_details(mempool_devices)
659 if __name__ == "__main__":