#! /usr/bin/python
#
# BSD LICENSE
-#
+#
# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
# All rights reserved.
-#
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
-#
+#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# * Neither the name of Intel Corporation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
-#
+#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# global dict ethernet devices present. Dictionary indexed by PCI address.
# Each device within this is itself a dictionary of device properties
devices = {}
-# list of vendor:device pairs (again stored as dictionary) supported by igb_uio
-module_dev_ids = []
def usage():
'''Print usage information for the program'''
Options:
--help, --usage:
Display usage information and quit
-
+
--status:
Print the current status of all known network interfaces.
For each device, it displays the PCI domain, bus, slot and function,
driver, other relevant information will be displayed:
* the Linux interface name e.g. if=eth0
* the driver being used e.g. drv=igb_uio
- * any suitable drivers not currently using that device
- e.g. unused=igb_uio
+ * any suitable drivers not currently using that device
+ e.g. unused=igb_uio
NOTE: if this flag is passed along with a bind/unbind option, the status
display will always occur after the other operations have taken place.
-
- -b driver, --bind=driver:
+
+ -b driver, --bind=driver:
Select the driver to use or \"none\" to unbind the device
-
- -u, --unbind:
+
+ -u, --unbind:
Unbind a device (Equivalent to \"-b none\")
-
+
--force:
By default, devices which are used by Linux - as indicated by having
routes in the routing table - cannot be modified. Using the --force
flag overrides this behavior, allowing active links to be forcibly
- unbound.
+ unbound.
WARNING: This can lead to loss of network connection and should be used
with caution.
-
+
Examples:
---------
-
+
To display current device status:
%(argv0)s --status
-
+
To bind eth1 from the current driver and move to use igb_uio
%(argv0)s --bind=igb_uio eth1
-
+
To unbind 0000:01:00.0 from using any driver
%(argv0)s -u 0000:01:00.0
-
+
To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
%(argv0)s -b ixgbe 02:00.0 02:00.1
-
+
""" % locals() # replace items from local variables
# This is roughly compatible with check_output function in subprocess module
-# which is only available in python 2.7.
+# which is only available in python 2.7.
def check_output(args, stderr=None):
'''Run a command and capture its output'''
return subprocess.Popen(args, stdout=subprocess.PIPE,
os.environ['RTE_TARGET'], mod)
if exists(path):
return path
-
+
# check using depmod
- try:
+ try:
depmod_out = check_output(["modinfo", "-n", mod], \
stderr=subprocess.STDOUT).lower()
if "error" not in depmod_out:
return path
except: # if modinfo can't find module, it fails, so continue
pass
-
+
# check for a copy based off current path
tools_dir = dirname(abspath(sys.argv[0]))
if (tools_dir.endswith("tools")):
return path
def check_modules():
- '''Checks that the needed modules (igb_uio) is loaded, and then
- determine from the .ko file, what its supported device ids are'''
- global module_dev_ids
-
+ '''Checks that igb_uio is loaded'''
+
fd = file("/proc/modules")
loaded_mods = fd.readlines()
fd.close()
mod = "igb_uio"
-
+
# first check if module is loaded
found = False
for line in loaded_mods:
if not found:
print "Error - module %s not loaded" %mod
sys.exit(1)
-
- # now find the .ko and get list of supported vendor/dev-ids
- modpath = find_module(mod)
- if modpath is None:
- print "Cannot find module file %s" % (mod + ".ko")
- sys.exit(1)
- depmod_output = check_output(["depmod", "-n", modpath]).splitlines()
- for line in depmod_output:
- if not line.startswith("alias"):
- continue
- if not line.endswith(mod):
- continue
- lineparts = line.split()
- if not(lineparts[1].startswith("pci:")):
- continue;
- else:
- lineparts[1] = lineparts[1][4:]
- vendor = lineparts[1][:9]
- device = lineparts[1][9:18]
- if vendor.startswith("v") and device.startswith("d"):
- module_dev_ids.append({"Vendor": int(vendor[1:],16),
- "Device": int(device[1:],16)})
-
-def is_supported_device(dev_id):
- '''return true if device is supported by igb_uio, false otherwise'''
- for dev in module_dev_ids:
- if (dev["Vendor"] == devices[dev_id]["Vendor"] and
- dev["Device"] == devices[dev_id]["Device"]):
- return True
- return False
def has_driver(dev_id):
'''return true if a device is assigned to a driver. False otherwise'''
return "Driver_str" in devices[dev_id]
+def get_pci_device_details(dev_id):
+ '''This function gets additional details for a PCI device'''
+ device = {}
+
+ extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
+
+ # parse lspci details
+ for line in extra_info:
+ if len(line) == 0:
+ continue
+ name, value = line.split("\t", 1)
+ name = name.strip(":") + "_str"
+ device[name] = value
+ # check for a unix interface name
+ sys_path = "/sys/bus/pci/devices/%s/net/" % dev_id
+ if exists(sys_path):
+ device["Interface"] = ",".join(os.listdir(sys_path))
+ else:
+ device["Interface"] = ""
+ # check if a port is used for ssh connection
+ device["Ssh_if"] = False
+ device["Active"] = ""
+
+ return device
+
def get_nic_details():
'''This function populates the "devices" dictionary. The keys used are
the pci addresses (domain:bus:slot.func). The values are themselves
dictionaries - one for each NIC.'''
global devices
-
+
# clear any old data
- devices = {}
+ devices = {}
# first loop through and read details for all devices
# request machine readable format, with numeric IDs
dev = {};
ssh_if = []
route = check_output(["ip", "-o", "route"])
# filter out all lines for 169.254 routes
- route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
+ route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
route.splitlines()))
rt_info = route.split()
for i in xrange(len(rt_info) - 1):
if rt_info[i] == "dev":
ssh_if.append(rt_info[i+1])
- # based on the basic info, get extended text details
+ # based on the basic info, get extended text details
for d in devices.keys():
- extra_info = check_output(["lspci", "-vmmks", d]).splitlines()
- # parse lspci details
- for line in extra_info:
- if len(line) == 0:
- continue
- name, value = line.split("\t", 1)
- name = name.strip(":") + "_str"
- devices[d][name] = value
- # check for a unix interface name
- sys_path = "/sys/bus/pci/devices/%s/net/" % d
- if exists(sys_path):
- devices[d]["Interface"] = ",".join(os.listdir(sys_path))
- else:
- devices[d]["Interface"] = ""
- # check if a port is used for ssh connection
- devices[d]["Ssh_if"] = False
- devices[d]["Active"] = ""
- for _if in ssh_if:
+ # get additional info and add it to existing data
+ devices[d] = dict(devices[d].items() +
+ get_pci_device_details(d).items())
+
+ for _if in ssh_if:
if _if in devices[d]["Interface"].split(","):
devices[d]["Ssh_if"] = True
devices[d]["Active"] = "*Active*"
break;
# add igb_uio to list of supporting modules if needed
- if is_supported_device(d):
- if "Module_str" in devices[d]:
- if "igb_uio" not in devices[d]["Module_str"]:
- devices[d]["Module_str"] = devices[d]["Module_str"] + ",igb_uio"
- else:
- devices[d]["Module_str"] = "igb_uio"
- if "Module_str" not in devices[d]:
- devices[d]["Module_str"] = "<none>"
+ if "Module_str" in devices[d]:
+ if "igb_uio" not in devices[d]["Module_str"]:
+ devices[d]["Module_str"] = devices[d]["Module_str"] + ",igb_uio"
+ else:
+ devices[d]["Module_str"] = "igb_uio"
+
# make sure the driver and module strings do not have any duplicates
if has_driver(d):
modules = devices[d]["Module_str"].split(",")
if devices[d]["Driver_str"] in modules:
modules.remove(devices[d]["Driver_str"])
devices[d]["Module_str"] = ",".join(modules)
-
+
def dev_id_from_dev_name(dev_name):
'''Take a device "name" - a string passed in by user to identify a NIC
device, and determine the device id - i.e. the domain:bus:slot.func - for
# check if it's already a suitable index
if dev_name in devices:
return dev_name
- # check if it's an index just missing the domain part
+ # check if it's an index just missing the domain part
elif "0000:" + dev_name in devices:
return "0000:" + dev_name
else:
print "%s %s %s is not currently managed by any driver\n" % \
(dev["Slot"], dev["Device_str"], dev["Interface"])
return
-
+
# prevent us disconnecting ourselves
if dev["Ssh_if"] and not force:
print "Routing table indicates that interface %s is active" \
". Skipping unbind" % (dev_id)
return
-
+
# write to /sys to unbind
filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
try:
is already bound to a different driver, it will be unbound first'''
dev = devices[dev_id]
saved_driver = None # used to rollback any unbind in case of failure
-
+
# prevent disconnection of our ssh session
if dev["Ssh_if"] and not force:
print "Routing table indicates that interface %s is active" \
unbind_one(dev_id, force)
dev["Driver_str"] = "" # clear driver string
+ # if we are binding to one of DPDK drivers, add PCI id's to that driver
+ if driver == "igb_uio":
+ filename = "/sys/bus/pci/drivers/%s/new_id" % driver
+ try:
+ f = open(filename, "w")
+ except:
+ print "Error: bind failed for %s - Cannot open %s" % (dev_id, filename)
+ return
+ try:
+ f.write("%04x %04x" % (dev["Vendor"], dev["Device"]))
+ f.close()
+ except:
+ print "Error: bind failed for %s - Cannot write new PCI ID to " \
+ "driver %s" % (dev_id, driver)
+ return
+
# do the bind by writing to /sys
filename = "/sys/bus/pci/drivers/%s/bind" % driver
try:
f.write(dev_id)
f.close()
except:
+ # for some reason, closing dev_id after adding a new PCI ID to new_id
+ # results in IOError. however, if the device was successfully bound,
+ # we don't care for any errors and can safely ignore IOError
+ tmp = get_pci_device_details(dev_id)
+ if "Driver_str" in tmp and tmp["Driver_str"] == driver:
+ return
print "Error: bind failed for %s - Cannot bind to driver %s" % (dev_id, driver)
if saved_driver is not None: # restore any previous driver
bind_one(dev_id, saved_driver, force)
'''Displays to the user the details of a list of devices given in "dev_list"
The "extra_params" parameter, if given, should contain a string with
%()s fields in it for replacement by the named fields in each device's
- dictionary.'''
+ dictionary.'''
strings = [] # this holds the strings to print. We sort before printing
print "\n%s" % title
print "="*len(title)
if len(sys.argv) <= 1:
usage()
sys.exit(0)
-
+
try:
opts, args = getopt.getopt(sys.argv[1:], "b:u",
["help", "usage", "status", "force",
print str(error)
print "Run '%s --usage' for further information" % sys.argv[0]
sys.exit(1)
-
+
for opt, arg in opts:
if opt == "--help" or opt == "--usage":
usage()
b_flag = "none"
else:
b_flag = arg
-
+
if b_flag is None and not status_flag:
print "Error: No action specified for devices. Please give a -b or -u option"
print "Run '%s --usage' for further information" % sys.argv[0]
sys.exit(1)
-
+
if b_flag is not None and len(args) == 0:
print "Error: No devices specified."
print "Run '%s --usage' for further information" % sys.argv[0]
if b_flag is not None:
get_nic_details() # refresh if we have changed anything
show_status()
-
+
def main():
'''program main function'''
check_modules()