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 = map(lambda a:
208 a if a != 'vfio_pci' else 'vfio-pci', sysfs_mods)
211 if mod["Name"] in sysfs_mods:
216 # check if we have at least one loaded module
217 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
218 if b_flag in dpdk_drivers:
219 print("Error - no supported modules(DPDK driver) are loaded")
222 print("Warning - no supported modules(DPDK driver) are loaded")
224 # change DPDK driver list to only contain drivers that are loaded
225 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
228 def has_driver(dev_id):
229 '''return true if a device is assigned to a driver. False otherwise'''
230 return "Driver_str" in devices[dev_id]
233 def get_pci_device_details(dev_id, probe_lspci):
234 '''This function gets additional details for a PCI device'''
238 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
240 # parse lspci details
241 for line in extra_info:
244 name, value = line.decode().split("\t", 1)
245 name = name.strip(":") + "_str"
247 # check for a unix interface name
248 device["Interface"] = ""
249 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
251 device["Interface"] = \
252 ",".join(os.listdir(os.path.join(base, "net")))
254 # check if a port is used for ssh connection
255 device["Ssh_if"] = False
256 device["Active"] = ""
261 '''This function clears any old data'''
264 def get_device_details(devices_type):
265 '''This function populates the "devices" dictionary. The keys used are
266 the pci addresses (domain:bus:slot.func). The values are themselves
267 dictionaries - one for each NIC.'''
271 # first loop through and read details for all devices
272 # request machine readable format, with numeric IDs and String
274 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
275 for dev_line in dev_lines:
276 if len(dev_line) == 0:
277 if device_type_match(dev, devices_type):
278 # Replace "Driver" with "Driver_str" to have consistency of
279 # of dictionary key names
280 if "Driver" in dev.keys():
281 dev["Driver_str"] = dev.pop("Driver")
282 # use dict to make copy of dev
283 devices[dev["Slot"]] = dict(dev)
284 # Clear previous device's data
287 name, value = dev_line.decode().split("\t", 1)
288 value_list = value.rsplit(' ', 1)
289 if len(value_list) > 1:
290 # String stored in <name>_str
291 dev[name.rstrip(":") + '_str'] = value_list[0]
293 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
294 .rstrip("]").lstrip("[")
296 if devices_type == network_devices:
297 # check what is the interface if any for an ssh connection if
298 # any to this host, so we can mark it later.
300 route = check_output(["ip", "-o", "route"])
301 # filter out all lines for 169.254 routes
302 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
303 route.decode().splitlines()))
304 rt_info = route.split()
305 for i in range(len(rt_info) - 1):
306 if rt_info[i] == "dev":
307 ssh_if.append(rt_info[i+1])
309 # based on the basic info, get extended text details
310 for d in devices.keys():
311 if not device_type_match(devices[d], devices_type):
314 # get additional info and add it to existing data
315 devices[d] = devices[d].copy()
316 # No need to probe lspci
317 devices[d].update(get_pci_device_details(d, False).items())
319 if devices_type == network_devices:
321 if _if in devices[d]["Interface"].split(","):
322 devices[d]["Ssh_if"] = True
323 devices[d]["Active"] = "*Active*"
326 # add igb_uio to list of supporting modules if needed
327 if "Module_str" in devices[d]:
328 for driver in dpdk_drivers:
329 if driver not in devices[d]["Module_str"]:
330 devices[d]["Module_str"] = \
331 devices[d]["Module_str"] + ",%s" % driver
333 devices[d]["Module_str"] = ",".join(dpdk_drivers)
335 # make sure the driver and module strings do not have any duplicates
337 modules = devices[d]["Module_str"].split(",")
338 if devices[d]["Driver_str"] in modules:
339 modules.remove(devices[d]["Driver_str"])
340 devices[d]["Module_str"] = ",".join(modules)
343 def device_type_match(dev, devices_type):
344 for i in range(len(devices_type)):
346 [x for x in devices_type[i].values() if x is not None])
348 if dev["Class"][0:2] == devices_type[i]["Class"]:
349 match_count = match_count + 1
350 for key in devices_type[i].keys():
351 if key != 'Class' and devices_type[i][key]:
352 value_list = devices_type[i][key].split(',')
353 for value in value_list:
354 if value.strip(' ') == dev[key]:
355 match_count = match_count + 1
356 # count must be the number of non None parameters to match
357 if match_count == param_count:
361 def dev_id_from_dev_name(dev_name):
362 '''Take a device "name" - a string passed in by user to identify a NIC
363 device, and determine the device id - i.e. the domain:bus:slot.func - for
364 it, which can then be used to index into the devices array'''
366 # check if it's already a suitable index
367 if dev_name in devices:
369 # check if it's an index just missing the domain part
370 elif "0000:" + dev_name in devices:
371 return "0000:" + dev_name
373 # check if it's an interface name, e.g. eth1
374 for d in devices.keys():
375 if dev_name in devices[d]["Interface"].split(","):
376 return devices[d]["Slot"]
377 # if nothing else matches - error
378 print("Unknown device: %s. "
379 "Please specify device in \"bus:slot.func\" format" % dev_name)
383 def unbind_one(dev_id, force):
384 '''Unbind the device identified by "dev_id" from its current driver'''
385 dev = devices[dev_id]
386 if not has_driver(dev_id):
387 print("%s %s %s is not currently managed by any driver\n" %
388 (dev["Slot"], dev["Device_str"], dev["Interface"]))
391 # prevent us disconnecting ourselves
392 if dev["Ssh_if"] and not force:
393 print("Routing table indicates that interface %s is active. "
394 "Skipping unbind" % (dev_id))
397 # write to /sys to unbind
398 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
400 f = open(filename, "a")
402 print("Error: unbind failed for %s - Cannot open %s"
403 % (dev_id, filename))
409 def bind_one(dev_id, driver, force):
410 '''Bind the device given by "dev_id" to the driver "driver". If the device
411 is already bound to a different driver, it will be unbound first'''
412 dev = devices[dev_id]
413 saved_driver = None # used to rollback any unbind in case of failure
415 # prevent disconnection of our ssh session
416 if dev["Ssh_if"] and not force:
417 print("Routing table indicates that interface %s is active. "
418 "Not modifying" % (dev_id))
421 # unbind any existing drivers we don't want
422 if has_driver(dev_id):
423 if dev["Driver_str"] == driver:
424 print("%s already bound to driver %s, skipping\n"
428 saved_driver = dev["Driver_str"]
429 unbind_one(dev_id, force)
430 dev["Driver_str"] = "" # clear driver string
432 # For kernels >= 3.15 driver_override can be used to specify the driver
433 # for a device rather than relying on the driver to provide a positive
434 # match of the device. The existing process of looking up
435 # the vendor and device ID, adding them to the driver new_id,
436 # will erroneously bind other devices too which has the additional burden
437 # of unbinding those devices
438 if driver in dpdk_drivers:
439 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
440 if os.path.exists(filename):
442 f = open(filename, "w")
444 print("Error: bind failed for %s - Cannot open %s"
445 % (dev_id, filename))
448 f.write("%s" % driver)
451 print("Error: bind failed for %s - Cannot write driver %s to "
452 "PCI ID " % (dev_id, driver))
454 # For kernels < 3.15 use new_id to add PCI id's to the driver
456 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
458 f = open(filename, "w")
460 print("Error: bind failed for %s - Cannot open %s"
461 % (dev_id, filename))
464 # Convert Device and Vendor Id to int to write to new_id
465 f.write("%04x %04x" % (int(dev["Vendor"],16),
466 int(dev["Device"], 16)))
469 print("Error: bind failed for %s - Cannot write new PCI ID to "
470 "driver %s" % (dev_id, driver))
473 # do the bind by writing to /sys
474 filename = "/sys/bus/pci/drivers/%s/bind" % driver
476 f = open(filename, "a")
478 print("Error: bind failed for %s - Cannot open %s"
479 % (dev_id, filename))
480 if saved_driver is not None: # restore any previous driver
481 bind_one(dev_id, saved_driver, force)
487 # for some reason, closing dev_id after adding a new PCI ID to new_id
488 # results in IOError. however, if the device was successfully bound,
489 # we don't care for any errors and can safely ignore IOError
490 tmp = get_pci_device_details(dev_id, True)
491 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
493 print("Error: bind failed for %s - Cannot bind to driver %s"
495 if saved_driver is not None: # restore any previous driver
496 bind_one(dev_id, saved_driver, force)
499 # For kernels > 3.15 driver_override is used to bind a device to a driver.
500 # Before unbinding it, overwrite driver_override with empty string so that
501 # the device can be bound to any other driver
502 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
503 if os.path.exists(filename):
505 f = open(filename, "w")
507 print("Error: unbind failed for %s - Cannot open %s"
508 % (dev_id, filename))
514 print("Error: unbind failed for %s - Cannot open %s"
515 % (dev_id, filename))
519 def unbind_all(dev_list, force=False):
520 """Unbind method, takes a list of device locations"""
522 if dev_list[0] == "dpdk":
523 for d in devices.keys():
524 if "Driver_str" in devices[d]:
525 if devices[d]["Driver_str"] in dpdk_drivers:
526 unbind_one(devices[d]["Slot"], force)
529 dev_list = map(dev_id_from_dev_name, dev_list)
534 def bind_all(dev_list, driver, force=False):
535 """Bind method, takes a list of device locations"""
538 dev_list = map(dev_id_from_dev_name, dev_list)
541 bind_one(d, driver, force)
543 # For kenels < 3.15 when binding devices to a generic driver
544 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
545 # that are not bound to any other driver could be bound even if no one has
546 # asked them to. hence, we check the list of drivers again, and see if
547 # some of the previously-unbound devices were erroneously bound.
548 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
549 for d in devices.keys():
550 # skip devices that were already bound or that we know should be bound
551 if "Driver_str" in devices[d] or d in dev_list:
554 # update information about this device
555 devices[d] = dict(devices[d].items() +
556 get_pci_device_details(d, True).items())
558 # check if updated information indicates that the device was bound
559 if "Driver_str" in devices[d]:
563 def display_devices(title, dev_list, extra_params=None):
564 '''Displays to the user the details of a list of devices given in
565 "dev_list". The "extra_params" parameter, if given, should contain a string
566 with %()s fields in it for replacement by the named fields in each
567 device's dictionary.'''
568 strings = [] # this holds the strings to print. We sort before printing
569 print("\n%s" % title)
570 print("="*len(title))
571 if len(dev_list) == 0:
572 strings.append("<none>")
575 if extra_params is not None:
576 strings.append("%s '%s %s' %s" % (dev["Slot"],
581 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
582 # sort before printing, so that the entries appear in PCI order
584 print("\n".join(strings)) # print one per line
586 def show_device_status(devices_type, device_name):
592 # split our list of network devices into the three categories above
593 for d in devices.keys():
594 if device_type_match(devices[d], devices_type):
595 if not has_driver(d):
596 no_drv.append(devices[d])
598 if devices[d]["Driver_str"] in dpdk_drivers:
599 dpdk_drv.append(devices[d])
601 kernel_drv.append(devices[d])
603 # print each category separately, so we can clearly see what's used by DPDK
604 display_devices("%s devices using DPDK-compatible driver" % device_name,
605 dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
606 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
607 "if=%(Interface)s drv=%(Driver_str)s "
608 "unused=%(Module_str)s %(Active)s")
609 display_devices("Other %s devices" % device_name, no_drv,
610 "unused=%(Module_str)s")
613 '''Function called when the script is passed the "--status" option.
614 Displays to the user what devices are bound to the igb_uio driver, the
615 kernel driver or to no driver'''
617 if status_dev == "net" or status_dev == "all":
618 show_device_status(network_devices, "Network")
620 if status_dev == "crypto" or status_dev == "all":
621 show_device_status(crypto_devices, "Crypto")
623 if status_dev == "event" or status_dev == "all":
624 show_device_status(eventdev_devices, "Eventdev")
626 if status_dev == "mempool" or status_dev == "all":
627 show_device_status(mempool_devices, "Mempool")
630 '''Parses the command-line arguments given by the user and takes the
631 appropriate action for each'''
637 if len(sys.argv) <= 1:
642 opts, args = getopt.getopt(sys.argv[1:], "b:us",
643 ["help", "usage", "status", "status-dev=",
644 "force", "bind=", "unbind", ])
645 except getopt.GetoptError as error:
647 print("Run '%s --usage' for further information" % sys.argv[0])
650 for opt, arg in opts:
651 if opt == "--help" or opt == "--usage":
654 if opt == "--status-dev":
657 if opt == "--status" or opt == "-s":
662 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
663 if b_flag is not None:
664 print("Error - Only one bind or unbind may be specified\n")
666 if opt == "-u" or opt == "--unbind":
672 def do_arg_actions():
673 '''do the actual action requested by the user'''
679 if b_flag is None and not status_flag:
680 print("Error: No action specified for devices."
681 "Please give a -b or -u option")
682 print("Run '%s --usage' for further information" % sys.argv[0])
685 if b_flag is not None and len(args) == 0:
686 print("Error: No devices specified.")
687 print("Run '%s --usage' for further information" % sys.argv[0])
690 if b_flag == "none" or b_flag == "None":
691 unbind_all(args, force_flag)
692 elif b_flag is not None:
693 bind_all(args, b_flag, force_flag)
695 if b_flag is not None:
697 # refresh if we have changed anything
698 get_device_details(network_devices)
699 get_device_details(crypto_devices)
700 get_device_details(eventdev_devices)
701 get_device_details(mempool_devices)
706 '''program main function'''
710 get_device_details(network_devices)
711 get_device_details(crypto_devices)
712 get_device_details(eventdev_devices)
713 get_device_details(mempool_devices)
716 if __name__ == "__main__":