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.
35 import sys, os, getopt, subprocess
36 from os.path import exists, abspath, dirname, basename
39 # The PCI device class for ETHERNET devices
40 ETHERNET_CLASS = "0200"
42 # global dict ethernet devices present. Dictionary indexed by PCI address.
43 # Each device within this is itself a dictionary of device properties
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 interfaces.
65 For each device, it displays the PCI domain, bus, slot and function,
66 along with a text description of the device. Depending upon whether the
67 device is being used by a kernel driver, the igb_uio driver, or no
68 driver, other relevant information will be displayed:
69 * the Linux interface name e.g. if=eth0
70 * the driver being used e.g. drv=igb_uio
71 * any suitable drivers not currently using that device
73 NOTE: if this flag is passed along with a bind/unbind option, the status
74 display will always occur after the other operations have taken place.
76 -b driver, --bind=driver:
77 Select the driver to use or \"none\" to unbind the device
80 Unbind a device (Equivalent to \"-b none\")
83 By default, devices which are used by Linux - as indicated by having
84 routes in the routing table - cannot be modified. Using the --force
85 flag overrides this behavior, allowing active links to be forcibly
87 WARNING: This can lead to loss of network connection and should be used
93 To display current device status:
96 To bind eth1 from the current driver and move to use igb_uio
97 %(argv0)s --bind=igb_uio eth1
99 To unbind 0000:01:00.0 from using any driver
100 %(argv0)s -u 0000:01:00.0
102 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
103 %(argv0)s -b ixgbe 02:00.0 02:00.1
105 """ % locals() # replace items from local variables
107 # This is roughly compatible with check_output function in subprocess module
108 # which is only available in python 2.7.
109 def check_output(args, stderr=None):
110 '''Run a command and capture its output'''
111 return subprocess.Popen(args, stdout=subprocess.PIPE,
112 stderr=stderr).communicate()[0]
114 def find_module(mod):
115 '''find the .ko file for kernel module named mod.
116 Searches the $RTE_SDK/$RTE_TARGET directory, the kernel
117 modules directory and finally under the parent directory of
119 # check $RTE_SDK/$RTE_TARGET directory
120 if 'RTE_SDK' in os.environ and 'RTE_TARGET' in os.environ:
121 path = "%s/%s/kmod/%s.ko" % (os.environ['RTE_SDK'],\
122 os.environ['RTE_TARGET'], mod)
128 depmod_out = check_output(["modinfo", "-n", mod], \
129 stderr=subprocess.STDOUT).lower()
130 if "error" not in depmod_out:
131 path = depmod_out.strip()
134 except: # if modinfo can't find module, it fails, so continue
137 # check for a copy based off current path
138 tools_dir = dirname(abspath(sys.argv[0]))
139 if (tools_dir.endswith("tools")):
140 base_dir = dirname(tools_dir)
141 find_out = check_output(["find", base_dir, "-name", mod + ".ko"])
142 if len(find_out) > 0: #something matched
143 path = find_out.splitlines()[0]
148 '''Checks that igb_uio is loaded'''
150 fd = file("/proc/modules")
151 loaded_mods = fd.readlines()
155 # first check if module is loaded
157 for line in loaded_mods:
158 if line.startswith(mod):
162 print "Error - module %s not loaded" %mod
165 def has_driver(dev_id):
166 '''return true if a device is assigned to a driver. False otherwise'''
167 return "Driver_str" in devices[dev_id]
169 def get_pci_device_details(dev_id):
170 '''This function gets additional details for a PCI device'''
173 extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
175 # parse lspci details
176 for line in extra_info:
179 name, value = line.split("\t", 1)
180 name = name.strip(":") + "_str"
182 # check for a unix interface name
183 sys_path = "/sys/bus/pci/devices/%s/net/" % dev_id
185 device["Interface"] = ",".join(os.listdir(sys_path))
187 device["Interface"] = ""
188 # check if a port is used for ssh connection
189 device["Ssh_if"] = False
190 device["Active"] = ""
194 def get_nic_details():
195 '''This function populates the "devices" dictionary. The keys used are
196 the pci addresses (domain:bus:slot.func). The values are themselves
197 dictionaries - one for each NIC.'''
202 # first loop through and read details for all devices
203 # request machine readable format, with numeric IDs
205 dev_lines = check_output(["lspci", "-Dvmmn"]).splitlines()
206 for dev_line in dev_lines:
207 if (len(dev_line) == 0):
208 if dev["Class"] == ETHERNET_CLASS:
209 #convert device and vendor ids to numbers, then add to global
210 dev["Vendor"] = int(dev["Vendor"],16)
211 dev["Device"] = int(dev["Device"],16)
212 devices[dev["Slot"]] = dict(dev) # use dict to make copy of dev
214 name, value = dev_line.split("\t", 1)
215 dev[name.rstrip(":")] = value
217 # check what is the interface if any for an ssh connection if
218 # any to this host, so we can mark it later.
220 route = check_output(["ip", "-o", "route"])
221 # filter out all lines for 169.254 routes
222 route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
224 rt_info = route.split()
225 for i in xrange(len(rt_info) - 1):
226 if rt_info[i] == "dev":
227 ssh_if.append(rt_info[i+1])
229 # based on the basic info, get extended text details
230 for d in devices.keys():
231 # get additional info and add it to existing data
232 devices[d] = dict(devices[d].items() +
233 get_pci_device_details(d).items())
236 if _if in devices[d]["Interface"].split(","):
237 devices[d]["Ssh_if"] = True
238 devices[d]["Active"] = "*Active*"
241 # add igb_uio to list of supporting modules if needed
242 if "Module_str" in devices[d]:
243 if "igb_uio" not in devices[d]["Module_str"]:
244 devices[d]["Module_str"] = devices[d]["Module_str"] + ",igb_uio"
246 devices[d]["Module_str"] = "igb_uio"
248 # make sure the driver and module strings do not have any duplicates
250 modules = devices[d]["Module_str"].split(",")
251 if devices[d]["Driver_str"] in modules:
252 modules.remove(devices[d]["Driver_str"])
253 devices[d]["Module_str"] = ",".join(modules)
255 def dev_id_from_dev_name(dev_name):
256 '''Take a device "name" - a string passed in by user to identify a NIC
257 device, and determine the device id - i.e. the domain:bus:slot.func - for
258 it, which can then be used to index into the devices array'''
260 # check if it's already a suitable index
261 if dev_name in devices:
263 # check if it's an index just missing the domain part
264 elif "0000:" + dev_name in devices:
265 return "0000:" + dev_name
267 # check if it's an interface name, e.g. eth1
268 for d in devices.keys():
269 if dev_name in devices[d]["Interface"].split(","):
270 return devices[d]["Slot"]
271 # if nothing else matches - error
272 print "Unknown device: %s. " \
273 "Please specify device in \"bus:slot.func\" format" % dev_name
276 def unbind_one(dev_id, force):
277 '''Unbind the device identified by "dev_id" from its current driver'''
278 dev = devices[dev_id]
279 if not has_driver(dev_id):
280 print "%s %s %s is not currently managed by any driver\n" % \
281 (dev["Slot"], dev["Device_str"], dev["Interface"])
284 # prevent us disconnecting ourselves
285 if dev["Ssh_if"] and not force:
286 print "Routing table indicates that interface %s is active" \
287 ". Skipping unbind" % (dev_id)
290 # write to /sys to unbind
291 filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
293 f = open(filename, "a")
295 print "Error: unbind failed for %s - Cannot open %s" % (dev_id, filename)
300 def bind_one(dev_id, driver, force):
301 '''Bind the device given by "dev_id" to the driver "driver". If the device
302 is already bound to a different driver, it will be unbound first'''
303 dev = devices[dev_id]
304 saved_driver = None # used to rollback any unbind in case of failure
306 # prevent disconnection of our ssh session
307 if dev["Ssh_if"] and not force:
308 print "Routing table indicates that interface %s is active" \
309 ". Not modifying" % (dev_id)
312 # unbind any existing drivers we don't want
313 if has_driver(dev_id):
314 if dev["Driver_str"] == driver:
315 print "%s already bound to driver %s, skipping\n" % (dev_id, driver)
318 saved_driver = dev["Driver_str"]
319 unbind_one(dev_id, force)
320 dev["Driver_str"] = "" # clear driver string
322 # if we are binding to one of DPDK drivers, add PCI id's to that driver
323 if driver == "igb_uio":
324 filename = "/sys/bus/pci/drivers/%s/new_id" % driver
326 f = open(filename, "w")
328 print "Error: bind failed for %s - Cannot open %s" % (dev_id, filename)
331 f.write("%04x %04x" % (dev["Vendor"], dev["Device"]))
334 print "Error: bind failed for %s - Cannot write new PCI ID to " \
335 "driver %s" % (dev_id, driver)
338 # do the bind by writing to /sys
339 filename = "/sys/bus/pci/drivers/%s/bind" % driver
341 f = open(filename, "a")
343 print "Error: bind failed for %s - Cannot open %s" % (dev_id, filename)
344 if saved_driver is not None: # restore any previous driver
345 bind_one(dev_id, saved_driver, force)
351 # for some reason, closing dev_id after adding a new PCI ID to new_id
352 # results in IOError. however, if the device was successfully bound,
353 # we don't care for any errors and can safely ignore IOError
354 tmp = get_pci_device_details(dev_id)
355 if "Driver_str" in tmp and tmp["Driver_str"] == driver:
357 print "Error: bind failed for %s - Cannot bind to driver %s" % (dev_id, driver)
358 if saved_driver is not None: # restore any previous driver
359 bind_one(dev_id, saved_driver, force)
363 def unbind_all(dev_list, force=False):
364 """Unbind method, takes a list of device locations"""
365 dev_list = map(dev_id_from_dev_name, dev_list)
369 def bind_all(dev_list, driver, force=False):
370 """Unbind method, takes a list of device locations"""
371 dev_list = map(dev_id_from_dev_name, dev_list)
373 bind_one(d, driver, force)
375 def display_devices(title, dev_list, extra_params = None):
376 '''Displays to the user the details of a list of devices given in "dev_list"
377 The "extra_params" parameter, if given, should contain a string with
378 %()s fields in it for replacement by the named fields in each device's
380 strings = [] # this holds the strings to print. We sort before printing
383 if len(dev_list) == 0:
384 strings.append("<none>")
387 if extra_params is not None:
388 strings.append("%s '%s' %s" % (dev["Slot"], \
389 dev["Device_str"], extra_params % dev))
391 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
392 # sort before printing, so that the entries appear in PCI order
394 print "\n".join(strings) # print one per line
397 '''Function called when the script is passed the "--status" option. Displays
398 to the user what devices are bound to the igb_uio driver, the kernel driver
403 # split our list of devices into the three categories above
404 for d in devices.keys():
405 if not has_driver(d):
406 no_drv.append(devices[d])
408 if devices[d]["Driver_str"] == "igb_uio":
409 uio_drv.append(devices[d])
411 kernel_drv.append(devices[d])
413 # print each category separately, so we can clearly see what's used by DPDK
414 display_devices("Network devices using IGB_UIO driver", uio_drv, \
415 "drv=%(Driver_str)s unused=%(Module_str)s")
416 display_devices("Network devices using kernel driver", kernel_drv,
417 "if=%(Interface)s drv=%(Driver_str)s unused=%(Module_str)s %(Active)s")
418 display_devices("Other network devices", no_drv,\
419 "unused=%(Module_str)s")
422 '''Parses the command-line arguments given by the user and takes the
423 appropriate action for each'''
427 if len(sys.argv) <= 1:
432 opts, args = getopt.getopt(sys.argv[1:], "b:u",
433 ["help", "usage", "status", "force",
435 except getopt.GetoptError, error:
437 print "Run '%s --usage' for further information" % sys.argv[0]
440 for opt, arg in opts:
441 if opt == "--help" or opt == "--usage":
444 if opt == "--status":
448 if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
449 if b_flag is not None:
450 print "Error - Only one bind or unbind may be specified\n"
452 if opt == "-u" or opt == "--unbind":
457 if b_flag is None and not status_flag:
458 print "Error: No action specified for devices. Please give a -b or -u option"
459 print "Run '%s --usage' for further information" % sys.argv[0]
462 if b_flag is not None and len(args) == 0:
463 print "Error: No devices specified."
464 print "Run '%s --usage' for further information" % sys.argv[0]
467 if b_flag == "none" or b_flag == "None":
468 unbind_all(args, force_flag)
469 elif b_flag is not None:
470 bind_all(args, b_flag, force_flag)
472 if b_flag is not None:
473 get_nic_details() # refresh if we have changed anything
477 '''program main function'''
482 if __name__ == "__main__":