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 NETWORK devices
42 NETWORK_BASE_CLASS = "02"
43 CRYPTO_BASE_CLASS = "0b"
45 # global dict ethernet devices present. Dictionary indexed by PCI address.
46 # Each device within this is itself a dictionary of device properties
48 # list of supported DPDK drivers
49 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
51 # command-line arg flags
59 '''Print usage information for the program'''
60 argv0 = basename(sys.argv[0])
65 %(argv0)s [options] DEVICE1 DEVICE2 ....
67 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
68 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
69 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
73 Display usage information and quit
76 Print the current status of all known network and crypto devices.
77 For each device, it displays the PCI domain, bus, slot and function,
78 along with a text description of the device. Depending upon whether the
79 device is being used by a kernel driver, the igb_uio driver, or no
80 driver, other relevant information will be displayed:
81 * the Linux interface name e.g. if=eth0
82 * the driver being used e.g. drv=igb_uio
83 * any suitable drivers not currently using that device
85 NOTE: if this flag is passed along with a bind/unbind option, the
86 status display will always occur after the other operations have taken
89 -b driver, --bind=driver:
90 Select the driver to use or \"none\" to unbind the device
93 Unbind a device (Equivalent to \"-b none\")
96 By default, network devices which are used by Linux - as indicated by
97 having routes in the routing table - cannot be modified. Using the
98 --force flag overrides this behavior, allowing active links to be
100 WARNING: This can lead to loss of network connection and should be used
106 To display current device status:
109 To bind eth1 from the current driver and move to use igb_uio
110 %(argv0)s --bind=igb_uio eth1
112 To unbind 0000:01:00.0 from using any driver
113 %(argv0)s -u 0000:01:00.0
115 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
116 %(argv0)s -b ixgbe 02:00.0 02:00.1
118 """ % locals()) # replace items from local variables
121 # This is roughly compatible with check_output function in subprocess module
122 # which is only available in python 2.7.
123 def check_output(args, stderr=None):
124 '''Run a command and capture its output'''
125 return subprocess.Popen(args, stdout=subprocess.PIPE,
126 stderr=stderr).communicate()[0]
129 def find_module(mod):
130 '''find the .ko file for kernel module named mod.
131 Searches the $RTE_SDK/$RTE_TARGET directory, the kernel
132 modules directory and finally under the parent directory of
134 # check $RTE_SDK/$RTE_TARGET directory
135 if 'RTE_SDK' in os.environ and 'RTE_TARGET' in os.environ:
136 path = "%s/%s/kmod/%s.ko" % (os.environ['RTE_SDK'],
137 os.environ['RTE_TARGET'], mod)
143 depmod_out = check_output(["modinfo", "-n", mod],
144 stderr=subprocess.STDOUT).lower()
145 if "error" not in depmod_out:
146 path = depmod_out.strip()
149 except: # if modinfo can't find module, it fails, so continue
152 # check for a copy based off current path
153 tools_dir = dirname(abspath(sys.argv[0]))
154 if tools_dir.endswith("tools"):
155 base_dir = dirname(tools_dir)
156 find_out = check_output(["find", base_dir, "-name", mod + ".ko"])
157 if len(find_out) > 0: # something matched
158 path = find_out.splitlines()[0]
164 '''Checks that igb_uio is loaded'''
167 # list of supported modules
168 mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
170 # first check if module is loaded
172 # Get list of sysfs modules (both built-in and dynamically loaded)
173 sysfs_path = '/sys/module/'
175 # Get the list of directories in sysfs_path
176 sysfs_mods = [os.path.join(sysfs_path, o) for o
177 in os.listdir(sysfs_path)
178 if os.path.isdir(os.path.join(sysfs_path, o))]
180 # Extract the last element of '/sys/module/abc' in the array
181 sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
183 # special case for vfio_pci (module is named vfio-pci,
184 # but its .ko is named vfio_pci)
185 sysfs_mods = map(lambda a:
186 a if a != 'vfio_pci' else 'vfio-pci', sysfs_mods)
189 if mod["Name"] in sysfs_mods:
194 # check if we have at least one loaded module
195 if True not in [mod["Found"] for mod in mods] and b_flag is not None:
196 if b_flag in dpdk_drivers:
197 print("Error - no supported modules(DPDK driver) are loaded")
200 print("Warning - no supported modules(DPDK driver) are loaded")
202 # change DPDK driver list to only contain drivers that are loaded
203 dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
206 def has_driver(dev_id):
207 '''return true if a device is assigned to a driver. False otherwise'''
208 return "Driver_str" in devices[dev_id]
211 def get_pci_device_details(dev_id, probe_lspci):
212 '''This function gets additional details for a PCI device'''
216 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
218 # parse lspci details
219 for line in extra_info:
222 name, value = line.decode().split("\t", 1)
223 name = name.strip(":") + "_str"
225 # check for a unix interface name
226 device["Interface"] = ""
227 for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
229 device["Interface"] = \
230 ",".join(os.listdir(os.path.join(base, "net")))
232 # check if a port is used for ssh connection
233 device["Ssh_if"] = False
234 device["Active"] = ""
239 '''This function clears any old data'''
242 def get_device_details(devices_type):
243 '''This function populates the "devices" dictionary. The keys used are
244 the pci addresses (domain:bus:slot.func). The values are themselves
245 dictionaries - one for each NIC.'''
249 # first loop through and read details for all devices
250 # request machine readable format, with numeric IDs and String
252 dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
253 for dev_line in dev_lines:
254 if len(dev_line) == 0:
255 if dev["Class"][0:2] == devices_type:
256 # convert device and vendor ids to numbers, then add to global
257 dev["Vendor"] = int(dev["Vendor"], 16)
258 dev["Device"] = int(dev["Device"], 16)
259 if "Driver" in dev.keys():
260 dev["Driver_str"] = dev.pop("Driver")
261 # use dict to make copy of dev
262 devices[dev["Slot"]] = dict(dev)
263 # Clear previous device's data
266 name, value = dev_line.decode().split("\t", 1)
267 value_list = value.rsplit(' ', 1)
268 if len(value_list) > 1:
269 # String stored in <name>_str
270 dev[name.rstrip(":") + '_str'] = value_list[0]
272 dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
273 .rstrip("]").lstrip("[")
275 if devices_type == NETWORK_BASE_CLASS:
276 # check what is the interface if any for an ssh connection if
277 # any to this host, so we can mark it later.
279 route = check_output(["ip", "-o", "route"])
280 # filter out all lines for 169.254 routes
281 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
282 route.decode().splitlines()))
283 rt_info = route.split()
284 for i in range(len(rt_info) - 1):
285 if rt_info[i] == "dev":
286 ssh_if.append(rt_info[i+1])
288 # based on the basic info, get extended text details
289 for d in devices.keys():
290 if devices[d]["Class"][0:2] != devices_type:
293 # get additional info and add it to existing data
294 devices[d] = devices[d].copy()
295 # No need to probe lspci
296 devices[d].update(get_pci_device_details(d, False).items())
298 if devices_type == NETWORK_BASE_CLASS:
300 if _if in devices[d]["Interface"].split(","):
301 devices[d]["Ssh_if"] = True
302 devices[d]["Active"] = "*Active*"
305 # add igb_uio to list of supporting modules if needed
306 if "Module_str" in devices[d]:
307 for driver in dpdk_drivers:
308 if driver not in devices[d]["Module_str"]:
309 devices[d]["Module_str"] = \
310 devices[d]["Module_str"] + ",%s" % driver
312 devices[d]["Module_str"] = ",".join(dpdk_drivers)
314 # make sure the driver and module strings do not have any duplicates
316 modules = devices[d]["Module_str"].split(",")
317 if devices[d]["Driver_str"] in modules:
318 modules.remove(devices[d]["Driver_str"])
319 devices[d]["Module_str"] = ",".join(modules)
322 def dev_id_from_dev_name(dev_name):
323 '''Take a device "name" - a string passed in by user to identify a NIC
324 device, and determine the device id - i.e. the domain:bus:slot.func - for
325 it, which can then be used to index into the devices array'''
327 # check if it's already a suitable index
328 if dev_name in devices:
330 # check if it's an index just missing the domain part
331 elif "0000:" + dev_name in devices:
332 return "0000:" + dev_name
334 # check if it's an interface name, e.g. eth1
335 for d in devices.keys():
336 if dev_name in devices[d]["Interface"].split(","):
337 return devices[d]["Slot"]
338 # if nothing else matches - error
339 print("Unknown device: %s. "
340 "Please specify device in \"bus:slot.func\" format" % dev_name)
344 def unbind_one(dev_id, force):
345 '''Unbind the device identified by "dev_id" from its current driver'''
346 dev = devices[dev_id]
347 if not has_driver(dev_id):
348 print("%s %s %s is not currently managed by any driver\n" %
349 (dev["Slot"], dev["Device_str"], dev["Interface"]))
352 # prevent us disconnecting ourselves
353 if dev["Ssh_if"] and not force:
354 print("Routing table indicates that interface %s is active. "
355 "Skipping unbind" % (dev_id))
358 # For kernels > 3.15 driver_override is used to bind a device to a driver.
359 # Before unbinding it, overwrite driver_override with empty string so that
360 # the device can be bound to any other driver
361 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
362 if os.path.exists(filename):
364 f = open(filename, "w")
366 print("Error: unbind failed for %s - Cannot open %s"
367 % (dev_id, filename))
373 print("Error: unbind failed for %s - Cannot open %s"
374 % (dev_id, filename))
377 # write to /sys to unbind
378 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
380 f = open(filename, "a")
382 print("Error: unbind failed for %s - Cannot open %s"
383 % (dev_id, filename))
389 def bind_one(dev_id, driver, force):
390 '''Bind the device given by "dev_id" to the driver "driver". If the device
391 is already bound to a different driver, it will be unbound first'''
392 dev = devices[dev_id]
393 saved_driver = None # used to rollback any unbind in case of failure
395 # prevent disconnection of our ssh session
396 if dev["Ssh_if"] and not force:
397 print("Routing table indicates that interface %s is active. "
398 "Not modifying" % (dev_id))
401 # unbind any existing drivers we don't want
402 if has_driver(dev_id):
403 if dev["Driver_str"] == driver:
404 print("%s already bound to driver %s, skipping\n"
408 saved_driver = dev["Driver_str"]
409 unbind_one(dev_id, force)
410 dev["Driver_str"] = "" # clear driver string
412 # For kernels >= 3.15 driver_override can be used to specify the driver
413 # for a device rather than relying on the driver to provide a positive
414 # match of the device. The existing process of looking up
415 # the vendor and device ID, adding them to the driver new_id,
416 # will erroneously bind other devices too which has the additional burden
417 # of unbinding those devices
418 if driver in dpdk_drivers:
419 filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
420 if os.path.exists(filename):
422 f = open(filename, "w")
424 print("Error: bind failed for %s - Cannot open %s"
425 % (dev_id, filename))
428 f.write("%s" % driver)
431 print("Error: bind failed for %s - Cannot write driver %s to "
432 "PCI ID " % (dev_id, driver))
434 # For kernels < 3.15 use new_id to add PCI id's to the driver
436 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
438 f = open(filename, "w")
440 print("Error: bind failed for %s - Cannot open %s"
441 % (dev_id, filename))
444 f.write("%04x %04x" % (dev["Vendor"], dev["Device"]))
447 print("Error: bind failed for %s - Cannot write new PCI ID to "
448 "driver %s" % (dev_id, driver))
451 # do the bind by writing to /sys
452 filename = "/sys/bus/pci/drivers/%s/bind" % driver
454 f = open(filename, "a")
456 print("Error: bind failed for %s - Cannot open %s"
457 % (dev_id, filename))
458 if saved_driver is not None: # restore any previous driver
459 bind_one(dev_id, saved_driver, force)
465 # for some reason, closing dev_id after adding a new PCI ID to new_id
466 # results in IOError. however, if the device was successfully bound,
467 # we don't care for any errors and can safely ignore IOError
468 tmp = get_pci_device_details(dev_id, True)
469 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
471 print("Error: bind failed for %s - Cannot bind to driver %s"
473 if saved_driver is not None: # restore any previous driver
474 bind_one(dev_id, saved_driver, force)
478 def unbind_all(dev_list, force=False):
479 """Unbind method, takes a list of device locations"""
480 dev_list = map(dev_id_from_dev_name, dev_list)
485 def bind_all(dev_list, driver, force=False):
486 """Bind method, takes a list of device locations"""
489 dev_list = map(dev_id_from_dev_name, dev_list)
492 bind_one(d, driver, force)
494 # For kenels < 3.15 when binding devices to a generic driver
495 # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
496 # that are not bound to any other driver could be bound even if no one has
497 # asked them to. hence, we check the list of drivers again, and see if
498 # some of the previously-unbound devices were erroneously bound.
499 if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
500 for d in devices.keys():
501 # skip devices that were already bound or that we know should be bound
502 if "Driver_str" in devices[d] or d in dev_list:
505 # update information about this device
506 devices[d] = dict(devices[d].items() +
507 get_pci_device_details(d, True).items())
509 # check if updated information indicates that the device was bound
510 if "Driver_str" in devices[d]:
514 def display_devices(title, dev_list, extra_params=None):
515 '''Displays to the user the details of a list of devices given in
516 "dev_list". The "extra_params" parameter, if given, should contain a string
517 with %()s fields in it for replacement by the named fields in each
518 device's dictionary.'''
519 strings = [] # this holds the strings to print. We sort before printing
520 print("\n%s" % title)
521 print("="*len(title))
522 if len(dev_list) == 0:
523 strings.append("<none>")
526 if extra_params is not None:
527 strings.append("%s '%s %s' %s" % (dev["Slot"],
532 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
533 # sort before printing, so that the entries appear in PCI order
535 print("\n".join(strings)) # print one per line
537 def show_device_status(devices_type, device_name):
543 # split our list of network devices into the three categories above
544 for d in devices.keys():
545 if devices_type in devices[d]["Class"]:
546 if not has_driver(d):
547 no_drv.append(devices[d])
549 if devices[d]["Driver_str"] in dpdk_drivers:
550 dpdk_drv.append(devices[d])
552 kernel_drv.append(devices[d])
554 # print each category separately, so we can clearly see what's used by DPDK
555 display_devices("%s devices using DPDK-compatible driver" % device_name, dpdk_drv,
556 "drv=%(Driver_str)s unused=%(Module_str)s")
557 display_devices("%s devices using kernel driver" % device_name, kernel_drv,
558 "if=%(Interface)s drv=%(Driver_str)s "
559 "unused=%(Module_str)s %(Active)s")
560 display_devices("Other %s devices" % device_name, no_drv, "unused=%(Module_str)s")
563 '''Function called when the script is passed the "--status" option.
564 Displays to the user what devices are bound to the igb_uio driver, the
565 kernel driver or to no driver'''
567 show_device_status(network_devices, "Network")
568 show_device_status(crypto_devices, "Crypto")
571 '''Parses the command-line arguments given by the user and takes the
572 appropriate action for each'''
577 if len(sys.argv) <= 1:
582 opts, args = getopt.getopt(sys.argv[1:], "b:us",
583 ["help", "usage", "status", "force",
585 except getopt.GetoptError as error:
587 print("Run '%s --usage' for further information" % sys.argv[0])
590 for opt, arg in opts:
591 if opt == "--help" or opt == "--usage":
594 if opt == "--status" or opt == "-s":
598 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
599 if b_flag is not None:
600 print("Error - Only one bind or unbind may be specified\n")
602 if opt == "-u" or opt == "--unbind":
608 def do_arg_actions():
609 '''do the actual action requested by the user'''
615 if b_flag is None and not status_flag:
616 print("Error: No action specified for devices."
617 "Please give a -b or -u option")
618 print("Run '%s --usage' for further information" % sys.argv[0])
621 if b_flag is not None and len(args) == 0:
622 print("Error: No devices specified.")
623 print("Run '%s --usage' for further information" % sys.argv[0])
626 if b_flag == "none" or b_flag == "None":
627 unbind_all(args, force_flag)
628 elif b_flag is not None:
629 bind_all(args, b_flag, force_flag)
631 if b_flag is not None:
633 get_device_details(NETWORK_BASE_CLASS) # refresh if we have changed anything
634 get_device_details(CRYPTO_BASE_CLASS) # refresh if we have changed anything
639 '''program main function'''
643 get_device_details(NETWORK_BASE_CLASS)
644 get_device_details(CRYPTO_BASE_CLASS)
647 if __name__ == "__main__":