5 # Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
12 # * Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 # * Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in
16 # the documentation and/or other materials provided with the
18 # * Neither the name of Intel Corporation nor the names of its
19 # contributors may be used to endorse or promote products derived
20 # from this software without specific prior written permission.
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 from os.path import exists, abspath, dirname, basename
41 # The PCI base class for all devices
42 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
43 'SVendor': None, 'SDevice': None}
44 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
45 'SVendor': None, 'SDevice': None}
46 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
47 'SVendor': None, 'SDevice': None}
48 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
49 'SVendor': None, 'SDevice': None}
50 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
51 'SVendor': None, 'SDevice': None}
52 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
53 'SVendor': None, 'SDevice': None}
55 network_devices = [network_class, cavium_pkx]
56 crypto_devices = [encryption_class, intel_processor_class]
57 eventdev_devices = [cavium_sso]
58 mempool_devices = [cavium_fpa]
60 # global dict ethernet devices present. Dictionary indexed by PCI address.
61 # Each device within this is itself a dictionary of device properties
63 # list of supported DPDK drivers
64 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
66 # command-line arg flags
74 '''Print usage information for the program'''
75 argv0 = basename(sys.argv[0])
80 %(argv0)s [options] DEVICE1 DEVICE2 ....
82 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
83 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
84 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
88 Display usage information and quit
91 Print the current status of all known network, crypto, event
93 For each device, it displays the PCI domain, bus, slot and function,
94 along with a text description of the device. Depending upon whether the
95 device is being used by a kernel driver, the igb_uio driver, or no
96 driver, other relevant information will be displayed:
97 * the Linux interface name e.g. if=eth0
98 * the driver being used e.g. drv=igb_uio
99 * any suitable drivers not currently using that device
101 NOTE: if this flag is passed along with a bind/unbind option, the
102 status display will always occur after the other operations have taken
106 Print the status of given device group. Supported device groups are:
107 "net", "crypto", "event" and "mempool"
109 -b driver, --bind=driver:
110 Select the driver to use or \"none\" to unbind the device
113 Unbind a device (Equivalent to \"-b none\")
116 By default, network devices which are used by Linux - as indicated by
117 having routes in the routing table - cannot be modified. Using the
118 --force flag overrides this behavior, allowing active links to be
120 WARNING: This can lead to loss of network connection and should be used
126 To display current device status:
129 To display current network device status:
130 %(argv0)s --status-dev net
132 To bind eth1 from the current driver and move to use igb_uio
133 %(argv0)s --bind=igb_uio eth1
135 To unbind 0000:01:00.0 from using any driver
136 %(argv0)s -u 0000:01:00.0
138 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
139 %(argv0)s -b ixgbe 02:00.0 02:00.1
141 """ % locals()) # replace items from local variables
144 # This is roughly compatible with check_output function in subprocess module
145 # which is only available in python 2.7.
146 def check_output(args, stderr=None):
147 '''Run a command and capture its output'''
148 return subprocess.Popen(args, stdout=subprocess.PIPE,
149 stderr=stderr).communicate()[0]
152 def find_module(mod):
153 '''find the .ko file for kernel module named mod.
154 Searches the $RTE_SDK/$RTE_TARGET directory, the kernel
155 modules directory and finally under the parent directory of
157 # check $RTE_SDK/$RTE_TARGET directory
158 if 'RTE_SDK' in os.environ and 'RTE_TARGET' in os.environ:
159 path = "%s/%s/kmod/%s.ko" % (os.environ['RTE_SDK'],
160 os.environ['RTE_TARGET'], mod)
166 with open(os.devnull, "w") as fnull:
167 path = check_output(["modinfo", "-n", mod], stderr=fnull).strip()
169 if path and exists(path):
171 except: # if modinfo can't find module, it fails, so continue
174 # check for a copy based off current path
175 tools_dir = dirname(abspath(sys.argv[0]))
176 if tools_dir.endswith("tools"):
177 base_dir = dirname(tools_dir)
178 find_out = check_output(["find", base_dir, "-name", mod + ".ko"])
179 if len(find_out) > 0: # something matched
180 path = find_out.splitlines()[0]
186 '''Checks that igb_uio is loaded'''
189 # list of supported modules
190 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
192 # first check if module is loaded
194 # Get list of sysfs modules (both built-in and dynamically loaded)
195 sysfs_path = '/sys/module/'
197 # Get the list of directories in sysfs_path
198 sysfs_mods = [os.path.join(sysfs_path, o) for o
199 in os.listdir(sysfs_path)
200 if os.path.isdir(os.path.join(sysfs_path, o))]
202 # Extract the last element of '/sys/module/abc' in the array
203 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
205 # special case for vfio_pci (module is named vfio-pci,
206 # but its .ko is named vfio_pci)
207 sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
210 if mod["Name"] in sysfs_mods:
215 # check if we have at least one loaded module
216 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
217 if b_flag in dpdk_drivers:
218 print("Error - no supported modules(DPDK driver) are loaded")
221 print("Warning - no supported modules(DPDK driver) are loaded")
223 # change DPDK driver list to only contain drivers that are loaded
224 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
227 def has_driver(dev_id):
228 '''return true if a device is assigned to a driver. False otherwise'''
229 return "Driver_str" in devices[dev_id]
232 def get_pci_device_details(dev_id, probe_lspci):
233 '''This function gets additional details for a PCI device'''
237 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
239 # parse lspci details
240 for line in extra_info:
243 name, value = line.decode().split("\t", 1)
244 name = name.strip(":") + "_str"
246 # check for a unix interface name
247 device["Interface"] = ""
248 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
250 device["Interface"] = \
251 ",".join(os.listdir(os.path.join(base, "net")))
253 # check if a port is used for ssh connection
254 device["Ssh_if"] = False
255 device["Active"] = ""
260 '''This function clears any old data'''
263 def get_device_details(devices_type):
264 '''This function populates the "devices" dictionary. The keys used are
265 the pci addresses (domain:bus:slot.func). The values are themselves
266 dictionaries - one for each NIC.'''
270 # first loop through and read details for all devices
271 # request machine readable format, with numeric IDs and String
273 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
274 for dev_line in dev_lines:
275 if len(dev_line) == 0:
276 if device_type_match(dev, devices_type):
277 # Replace "Driver" with "Driver_str" to have consistency of
278 # of dictionary key names
279 if "Driver" in dev.keys():
280 dev["Driver_str"] = dev.pop("Driver")
281 if "Module" in dev.keys():
282 dev["Module_str"] = dev.pop("Module")
283 # use dict to make copy of dev
284 devices[dev["Slot"]] = dict(dev)
285 # Clear previous device's data
288 name, value = dev_line.decode().split("\t", 1)
289 value_list = value.rsplit(' ', 1)
290 if len(value_list) > 1:
291 # String stored in <name>_str
292 dev[name.rstrip(":") + '_str'] = value_list[0]
294 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
295 .rstrip("]").lstrip("[")
297 if devices_type == network_devices:
298 # check what is the interface if any for an ssh connection if
299 # any to this host, so we can mark it later.
301 route = check_output(["ip", "-o", "route"])
302 # filter out all lines for 169.254 routes
303 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
304 route.decode().splitlines()))
305 rt_info = route.split()
306 for i in range(len(rt_info) - 1):
307 if rt_info[i] == "dev":
308 ssh_if.append(rt_info[i+1])
310 # based on the basic info, get extended text details
311 for d in devices.keys():
312 if not device_type_match(devices[d], devices_type):
315 # get additional info and add it to existing data
316 devices[d] = devices[d].copy()
317 # No need to probe lspci
318 devices[d].update(get_pci_device_details(d, False).items())
320 if devices_type == network_devices:
322 if _if in devices[d]["Interface"].split(","):
323 devices[d]["Ssh_if"] = True
324 devices[d]["Active"] = "*Active*"
327 # add igb_uio to list of supporting modules if needed
328 if "Module_str" in devices[d]:
329 for driver in dpdk_drivers:
330 if driver not in devices[d]["Module_str"]:
331 devices[d]["Module_str"] = \
332 devices[d]["Module_str"] + ",%s" % driver
334 devices[d]["Module_str"] = ",".join(dpdk_drivers)
336 # make sure the driver and module strings do not have any duplicates
338 modules = devices[d]["Module_str"].split(",")
339 if devices[d]["Driver_str"] in modules:
340 modules.remove(devices[d]["Driver_str"])
341 devices[d]["Module_str"] = ",".join(modules)
344 def device_type_match(dev, devices_type):
345 for i in range(len(devices_type)):
347 [x for x in devices_type[i].values() if x is not None])
349 if dev["Class"][0:2] == devices_type[i]["Class"]:
350 match_count = match_count + 1
351 for key in devices_type[i].keys():
352 if key != 'Class' and devices_type[i][key]:
353 value_list = devices_type[i][key].split(',')
354 for value in value_list:
355 if value.strip(' ') == dev[key]:
356 match_count = match_count + 1
357 # count must be the number of non None parameters to match
358 if match_count == param_count:
362 def dev_id_from_dev_name(dev_name):
363 '''Take a device "name" - a string passed in by user to identify a NIC
364 device, and determine the device id - i.e. the domain:bus:slot.func - for
365 it, which can then be used to index into the devices array'''
367 # check if it's already a suitable index
368 if dev_name in devices:
370 # check if it's an index just missing the domain part
371 elif "0000:" + dev_name in devices:
372 return "0000:" + dev_name
374 # check if it's an interface name, e.g. eth1
375 for d in devices.keys():
376 if dev_name in devices[d]["Interface"].split(","):
377 return devices[d]["Slot"]
378 # if nothing else matches - error
379 print("Unknown device: %s. "
380 "Please specify device in \"bus:slot.func\" format" % dev_name)
384 def unbind_one(dev_id, force):
385 '''Unbind the device identified by "dev_id" from its current driver'''
386 dev = devices[dev_id]
387 if not has_driver(dev_id):
388 print("%s %s %s is not currently managed by any driver\n" %
389 (dev["Slot"], dev["Device_str"], dev["Interface"]))
392 # prevent us disconnecting ourselves
393 if dev["Ssh_if"] and not force:
394 print("Routing table indicates that interface %s is active. "
395 "Skipping unbind" % (dev_id))
398 # write to /sys to unbind
399 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
401 f = open(filename, "a")
403 print("Error: unbind failed for %s - Cannot open %s"
404 % (dev_id, filename))
410 def bind_one(dev_id, driver, force):
411 '''Bind the device given by "dev_id" to the driver "driver". If the device
412 is already bound to a different driver, it will be unbound first'''
413 dev = devices[dev_id]
414 saved_driver = None # used to rollback any unbind in case of failure
416 # prevent disconnection of our ssh session
417 if dev["Ssh_if"] and not force:
418 print("Routing table indicates that interface %s is active. "
419 "Not modifying" % (dev_id))
422 # unbind any existing drivers we don't want
423 if has_driver(dev_id):
424 if dev["Driver_str"] == driver:
425 print("%s already bound to driver %s, skipping\n"
429 saved_driver = dev["Driver_str"]
430 unbind_one(dev_id, force)
431 dev["Driver_str"] = "" # clear driver string
433 # For kernels >= 3.15 driver_override can be used to specify the driver
434 # for a device rather than relying on the driver to provide a positive
435 # match of the device. The existing process of looking up
436 # the vendor and device ID, adding them to the driver new_id,
437 # will erroneously bind other devices too which has the additional burden
438 # of unbinding those devices
439 if driver in dpdk_drivers:
440 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
441 if os.path.exists(filename):
443 f = open(filename, "w")
445 print("Error: bind failed for %s - Cannot open %s"
446 % (dev_id, filename))
449 f.write("%s" % driver)
452 print("Error: bind failed for %s - Cannot write driver %s to "
453 "PCI ID " % (dev_id, driver))
455 # For kernels < 3.15 use new_id to add PCI id's to the driver
457 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
459 f = open(filename, "w")
461 print("Error: bind failed for %s - Cannot open %s"
462 % (dev_id, filename))
465 # Convert Device and Vendor Id to int to write to new_id
466 f.write("%04x %04x" % (int(dev["Vendor"],16),
467 int(dev["Device"], 16)))
470 print("Error: bind failed for %s - Cannot write new PCI ID to "
471 "driver %s" % (dev_id, driver))
474 # do the bind by writing to /sys
475 filename = "/sys/bus/pci/drivers/%s/bind" % driver
477 f = open(filename, "a")
479 print("Error: bind failed for %s - Cannot open %s"
480 % (dev_id, filename))
481 if saved_driver is not None: # restore any previous driver
482 bind_one(dev_id, saved_driver, force)
488 # for some reason, closing dev_id after adding a new PCI ID to new_id
489 # results in IOError. however, if the device was successfully bound,
490 # we don't care for any errors and can safely ignore IOError
491 tmp = get_pci_device_details(dev_id, True)
492 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
494 print("Error: bind failed for %s - Cannot bind to driver %s"
496 if saved_driver is not None: # restore any previous driver
497 bind_one(dev_id, saved_driver, force)
500 # For kernels > 3.15 driver_override is used to bind a device to a driver.
501 # Before unbinding it, overwrite driver_override with empty string so that
502 # the device can be bound to any other driver
503 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
504 if os.path.exists(filename):
506 f = open(filename, "w")
508 print("Error: unbind failed for %s - Cannot open %s"
509 % (dev_id, filename))
515 print("Error: unbind failed for %s - Cannot open %s"
516 % (dev_id, filename))
520 def unbind_all(dev_list, force=False):
521 """Unbind method, takes a list of device locations"""
523 if dev_list[0] == "dpdk":
524 for d in devices.keys():
525 if "Driver_str" in devices[d]:
526 if devices[d]["Driver_str"] in dpdk_drivers:
527 unbind_one(devices[d]["Slot"], force)
530 dev_list = map(dev_id_from_dev_name, dev_list)
535 def bind_all(dev_list, driver, force=False):
536 """Bind method, takes a list of device locations"""
539 dev_list = map(dev_id_from_dev_name, dev_list)
542 bind_one(d, driver, force)
544 # For kernels < 3.15 when binding devices to a generic driver
545 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
546 # that are not bound to any other driver could be bound even if no one has
547 # asked them to. hence, we check the list of drivers again, and see if
548 # some of the previously-unbound devices were erroneously bound.
549 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
550 for d in devices.keys():
551 # skip devices that were already bound or that we know should be bound
552 if "Driver_str" in devices[d] or d in dev_list:
555 # update information about this device
556 devices[d] = dict(devices[d].items() +
557 get_pci_device_details(d, True).items())
559 # check if updated information indicates that the device was bound
560 if "Driver_str" in devices[d]:
564 def display_devices(title, dev_list, extra_params=None):
565 '''Displays to the user the details of a list of devices given in
566 "dev_list". The "extra_params" parameter, if given, should contain a string
567 with %()s fields in it for replacement by the named fields in each
568 device's dictionary.'''
569 strings = [] # this holds the strings to print. We sort before printing
570 print("\n%s" % title)
571 print("="*len(title))
572 if len(dev_list) == 0:
573 strings.append("<none>")
576 if extra_params is not None:
577 strings.append("%s '%s %s' %s" % (dev["Slot"],
582 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
583 # sort before printing, so that the entries appear in PCI order
585 print("\n".join(strings)) # print one per line
587 def show_device_status(devices_type, device_name):
593 # split our list of network devices into the three categories above
594 for d in devices.keys():
595 if device_type_match(devices[d], devices_type):
596 if not has_driver(d):
597 no_drv.append(devices[d])
599 if devices[d]["Driver_str"] in dpdk_drivers:
600 dpdk_drv.append(devices[d])
602 kernel_drv.append(devices[d])
604 # print each category separately, so we can clearly see what's used by DPDK
605 display_devices("%s devices using DPDK-compatible driver" % device_name,
606 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
607 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
608 "if=%(Interface)s drv=%(Driver_str)s "
609 "unused=%(Module_str)s %(Active)s")
610 display_devices("Other %s devices" % device_name, no_drv,
611 "unused=%(Module_str)s")
614 '''Function called when the script is passed the "--status" option.
615 Displays to the user what devices are bound to the igb_uio driver, the
616 kernel driver or to no driver'''
618 if status_dev == "net" or status_dev == "all":
619 show_device_status(network_devices, "Network")
621 if status_dev == "crypto" or status_dev == "all":
622 show_device_status(crypto_devices, "Crypto")
624 if status_dev == "event" or status_dev == "all":
625 show_device_status(eventdev_devices, "Eventdev")
627 if status_dev == "mempool" or status_dev == "all":
628 show_device_status(mempool_devices, "Mempool")
631 '''Parses the command-line arguments given by the user and takes the
632 appropriate action for each'''
638 if len(sys.argv) <= 1:
643 opts, args = getopt.getopt(sys.argv[1:], "b:us",
644 ["help", "usage", "status", "status-dev=",
645 "force", "bind=", "unbind", ])
646 except getopt.GetoptError as error:
648 print("Run '%s --usage' for further information" % sys.argv[0])
651 for opt, arg in opts:
652 if opt == "--help" or opt == "--usage":
655 if opt == "--status-dev":
658 if opt == "--status" or opt == "-s":
663 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
664 if b_flag is not None:
665 print("Error - Only one bind or unbind may be specified\n")
667 if opt == "-u" or opt == "--unbind":
673 def do_arg_actions():
674 '''do the actual action requested by the user'''
680 if b_flag is None and not status_flag:
681 print("Error: No action specified for devices."
682 "Please give a -b or -u option")
683 print("Run '%s --usage' for further information" % sys.argv[0])
686 if b_flag is not None and len(args) == 0:
687 print("Error: No devices specified.")
688 print("Run '%s --usage' for further information" % sys.argv[0])
691 if b_flag == "none" or b_flag == "None":
692 unbind_all(args, force_flag)
693 elif b_flag is not None:
694 bind_all(args, b_flag, force_flag)
696 if b_flag is not None:
698 # refresh if we have changed anything
699 get_device_details(network_devices)
700 get_device_details(crypto_devices)
701 get_device_details(eventdev_devices)
702 get_device_details(mempool_devices)
707 '''program main function'''
711 get_device_details(network_devices)
712 get_device_details(crypto_devices)
713 get_device_details(eventdev_devices)
714 get_device_details(mempool_devices)
717 if __name__ == "__main__":