xref: /dpdk/usertools/dpdk-devbind.py (revision c3ce205d5729867bd1c4c4429a80e01a528d5905)
1c6dab2a8SThomas Monjalon#! /usr/bin/env python
2c6dab2a8SThomas Monjalon#
3c6dab2a8SThomas Monjalon#   BSD LICENSE
4c6dab2a8SThomas Monjalon#
5c6dab2a8SThomas Monjalon#   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
6c6dab2a8SThomas Monjalon#   All rights reserved.
7c6dab2a8SThomas Monjalon#
8c6dab2a8SThomas Monjalon#   Redistribution and use in source and binary forms, with or without
9c6dab2a8SThomas Monjalon#   modification, are permitted provided that the following conditions
10c6dab2a8SThomas Monjalon#   are met:
11c6dab2a8SThomas Monjalon#
12c6dab2a8SThomas Monjalon#     * Redistributions of source code must retain the above copyright
13c6dab2a8SThomas Monjalon#       notice, this list of conditions and the following disclaimer.
14c6dab2a8SThomas Monjalon#     * Redistributions in binary form must reproduce the above copyright
15c6dab2a8SThomas Monjalon#       notice, this list of conditions and the following disclaimer in
16c6dab2a8SThomas Monjalon#       the documentation and/or other materials provided with the
17c6dab2a8SThomas Monjalon#       distribution.
18c6dab2a8SThomas Monjalon#     * Neither the name of Intel Corporation nor the names of its
19c6dab2a8SThomas Monjalon#       contributors may be used to endorse or promote products derived
20c6dab2a8SThomas Monjalon#       from this software without specific prior written permission.
21c6dab2a8SThomas Monjalon#
22c6dab2a8SThomas Monjalon#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23c6dab2a8SThomas Monjalon#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24c6dab2a8SThomas Monjalon#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25c6dab2a8SThomas Monjalon#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26c6dab2a8SThomas Monjalon#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27c6dab2a8SThomas Monjalon#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28c6dab2a8SThomas Monjalon#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29c6dab2a8SThomas Monjalon#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30c6dab2a8SThomas Monjalon#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31c6dab2a8SThomas Monjalon#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32c6dab2a8SThomas Monjalon#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33c6dab2a8SThomas Monjalon#
34c6dab2a8SThomas Monjalon
35c6dab2a8SThomas Monjalonimport sys
36c6dab2a8SThomas Monjalonimport os
37c6dab2a8SThomas Monjalonimport getopt
38c6dab2a8SThomas Monjalonimport subprocess
39c6dab2a8SThomas Monjalonfrom os.path import exists, abspath, dirname, basename
40c6dab2a8SThomas Monjalon
41c6dab2a8SThomas Monjalon# The PCI base class for NETWORK devices
42c6dab2a8SThomas MonjalonNETWORK_BASE_CLASS = "02"
43c6dab2a8SThomas MonjalonCRYPTO_BASE_CLASS = "0b"
44c6dab2a8SThomas Monjalon
45c6dab2a8SThomas Monjalon# global dict ethernet devices present. Dictionary indexed by PCI address.
46c6dab2a8SThomas Monjalon# Each device within this is itself a dictionary of device properties
47c6dab2a8SThomas Monjalondevices = {}
48c6dab2a8SThomas Monjalon# list of supported DPDK drivers
49c6dab2a8SThomas Monjalondpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
50c6dab2a8SThomas Monjalon
51c6dab2a8SThomas Monjalon# command-line arg flags
52c6dab2a8SThomas Monjalonb_flag = None
53c6dab2a8SThomas Monjalonstatus_flag = False
54c6dab2a8SThomas Monjalonforce_flag = False
55c6dab2a8SThomas Monjalonargs = []
56c6dab2a8SThomas Monjalon
57c6dab2a8SThomas Monjalon
58c6dab2a8SThomas Monjalondef usage():
59c6dab2a8SThomas Monjalon    '''Print usage information for the program'''
60c6dab2a8SThomas Monjalon    argv0 = basename(sys.argv[0])
61c6dab2a8SThomas Monjalon    print("""
62c6dab2a8SThomas MonjalonUsage:
63c6dab2a8SThomas Monjalon------
64c6dab2a8SThomas Monjalon
65c6dab2a8SThomas Monjalon     %(argv0)s [options] DEVICE1 DEVICE2 ....
66c6dab2a8SThomas Monjalon
67c6dab2a8SThomas Monjalonwhere DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
68c6dab2a8SThomas Monjalonor "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
69c6dab2a8SThomas Monjalonalso be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
70c6dab2a8SThomas Monjalon
71c6dab2a8SThomas MonjalonOptions:
72c6dab2a8SThomas Monjalon    --help, --usage:
73c6dab2a8SThomas Monjalon        Display usage information and quit
74c6dab2a8SThomas Monjalon
75c6dab2a8SThomas Monjalon    -s, --status:
76c6dab2a8SThomas Monjalon        Print the current status of all known network and crypto devices.
77c6dab2a8SThomas Monjalon        For each device, it displays the PCI domain, bus, slot and function,
78c6dab2a8SThomas Monjalon        along with a text description of the device. Depending upon whether the
79c6dab2a8SThomas Monjalon        device is being used by a kernel driver, the igb_uio driver, or no
80c6dab2a8SThomas Monjalon        driver, other relevant information will be displayed:
81c6dab2a8SThomas Monjalon        * the Linux interface name e.g. if=eth0
82c6dab2a8SThomas Monjalon        * the driver being used e.g. drv=igb_uio
83c6dab2a8SThomas Monjalon        * any suitable drivers not currently using that device
84c6dab2a8SThomas Monjalon            e.g. unused=igb_uio
85c6dab2a8SThomas Monjalon        NOTE: if this flag is passed along with a bind/unbind option, the
86c6dab2a8SThomas Monjalon        status display will always occur after the other operations have taken
87c6dab2a8SThomas Monjalon        place.
88c6dab2a8SThomas Monjalon
89c6dab2a8SThomas Monjalon    -b driver, --bind=driver:
90c6dab2a8SThomas Monjalon        Select the driver to use or \"none\" to unbind the device
91c6dab2a8SThomas Monjalon
92c6dab2a8SThomas Monjalon    -u, --unbind:
93c6dab2a8SThomas Monjalon        Unbind a device (Equivalent to \"-b none\")
94c6dab2a8SThomas Monjalon
95c6dab2a8SThomas Monjalon    --force:
96c6dab2a8SThomas Monjalon        By default, network devices which are used by Linux - as indicated by
97c6dab2a8SThomas Monjalon        having routes in the routing table - cannot be modified. Using the
98c6dab2a8SThomas Monjalon        --force flag overrides this behavior, allowing active links to be
99c6dab2a8SThomas Monjalon        forcibly unbound.
100c6dab2a8SThomas Monjalon        WARNING: This can lead to loss of network connection and should be used
101c6dab2a8SThomas Monjalon        with caution.
102c6dab2a8SThomas Monjalon
103c6dab2a8SThomas MonjalonExamples:
104c6dab2a8SThomas Monjalon---------
105c6dab2a8SThomas Monjalon
106c6dab2a8SThomas MonjalonTo display current device status:
107c6dab2a8SThomas Monjalon        %(argv0)s --status
108c6dab2a8SThomas Monjalon
109c6dab2a8SThomas MonjalonTo bind eth1 from the current driver and move to use igb_uio
110c6dab2a8SThomas Monjalon        %(argv0)s --bind=igb_uio eth1
111c6dab2a8SThomas Monjalon
112c6dab2a8SThomas MonjalonTo unbind 0000:01:00.0 from using any driver
113c6dab2a8SThomas Monjalon        %(argv0)s -u 0000:01:00.0
114c6dab2a8SThomas Monjalon
115c6dab2a8SThomas MonjalonTo bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
116c6dab2a8SThomas Monjalon        %(argv0)s -b ixgbe 02:00.0 02:00.1
117c6dab2a8SThomas Monjalon
118c6dab2a8SThomas Monjalon    """ % locals())  # replace items from local variables
119c6dab2a8SThomas Monjalon
120c6dab2a8SThomas Monjalon
121c6dab2a8SThomas Monjalon# This is roughly compatible with check_output function in subprocess module
122c6dab2a8SThomas Monjalon# which is only available in python 2.7.
123c6dab2a8SThomas Monjalondef check_output(args, stderr=None):
124c6dab2a8SThomas Monjalon    '''Run a command and capture its output'''
125c6dab2a8SThomas Monjalon    return subprocess.Popen(args, stdout=subprocess.PIPE,
126c6dab2a8SThomas Monjalon                            stderr=stderr).communicate()[0]
127c6dab2a8SThomas Monjalon
128c6dab2a8SThomas Monjalon
129c6dab2a8SThomas Monjalondef find_module(mod):
130c6dab2a8SThomas Monjalon    '''find the .ko file for kernel module named mod.
131c6dab2a8SThomas Monjalon    Searches the $RTE_SDK/$RTE_TARGET directory, the kernel
132c6dab2a8SThomas Monjalon    modules directory and finally under the parent directory of
133c6dab2a8SThomas Monjalon    the script '''
134c6dab2a8SThomas Monjalon    # check $RTE_SDK/$RTE_TARGET directory
135c6dab2a8SThomas Monjalon    if 'RTE_SDK' in os.environ and 'RTE_TARGET' in os.environ:
136c6dab2a8SThomas Monjalon        path = "%s/%s/kmod/%s.ko" % (os.environ['RTE_SDK'],
137c6dab2a8SThomas Monjalon                                     os.environ['RTE_TARGET'], mod)
138c6dab2a8SThomas Monjalon        if exists(path):
139c6dab2a8SThomas Monjalon            return path
140c6dab2a8SThomas Monjalon
141c6dab2a8SThomas Monjalon    # check using depmod
142c6dab2a8SThomas Monjalon    try:
143c6dab2a8SThomas Monjalon        depmod_out = check_output(["modinfo", "-n", mod],
144c6dab2a8SThomas Monjalon                                  stderr=subprocess.STDOUT).lower()
145c6dab2a8SThomas Monjalon        if "error" not in depmod_out:
146c6dab2a8SThomas Monjalon            path = depmod_out.strip()
147c6dab2a8SThomas Monjalon            if exists(path):
148c6dab2a8SThomas Monjalon                return path
149c6dab2a8SThomas Monjalon    except:  # if modinfo can't find module, it fails, so continue
150c6dab2a8SThomas Monjalon        pass
151c6dab2a8SThomas Monjalon
152c6dab2a8SThomas Monjalon    # check for a copy based off current path
153c6dab2a8SThomas Monjalon    tools_dir = dirname(abspath(sys.argv[0]))
154c6dab2a8SThomas Monjalon    if tools_dir.endswith("tools"):
155c6dab2a8SThomas Monjalon        base_dir = dirname(tools_dir)
156c6dab2a8SThomas Monjalon        find_out = check_output(["find", base_dir, "-name", mod + ".ko"])
157c6dab2a8SThomas Monjalon        if len(find_out) > 0:  # something matched
158c6dab2a8SThomas Monjalon            path = find_out.splitlines()[0]
159c6dab2a8SThomas Monjalon            if exists(path):
160c6dab2a8SThomas Monjalon                return path
161c6dab2a8SThomas Monjalon
162c6dab2a8SThomas Monjalon
163c6dab2a8SThomas Monjalondef check_modules():
164c6dab2a8SThomas Monjalon    '''Checks that igb_uio is loaded'''
165c6dab2a8SThomas Monjalon    global dpdk_drivers
166c6dab2a8SThomas Monjalon
167c6dab2a8SThomas Monjalon    # list of supported modules
168c6dab2a8SThomas Monjalon    mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
169c6dab2a8SThomas Monjalon
170c6dab2a8SThomas Monjalon    # first check if module is loaded
171c6dab2a8SThomas Monjalon    try:
172c6dab2a8SThomas Monjalon        # Get list of sysfs modules (both built-in and dynamically loaded)
173c6dab2a8SThomas Monjalon        sysfs_path = '/sys/module/'
174c6dab2a8SThomas Monjalon
175c6dab2a8SThomas Monjalon        # Get the list of directories in sysfs_path
176c6dab2a8SThomas Monjalon        sysfs_mods = [os.path.join(sysfs_path, o) for o
177c6dab2a8SThomas Monjalon                      in os.listdir(sysfs_path)
178c6dab2a8SThomas Monjalon                      if os.path.isdir(os.path.join(sysfs_path, o))]
179c6dab2a8SThomas Monjalon
180c6dab2a8SThomas Monjalon        # Extract the last element of '/sys/module/abc' in the array
181c6dab2a8SThomas Monjalon        sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
182c6dab2a8SThomas Monjalon
183c6dab2a8SThomas Monjalon        # special case for vfio_pci (module is named vfio-pci,
184c6dab2a8SThomas Monjalon        # but its .ko is named vfio_pci)
185c6dab2a8SThomas Monjalon        sysfs_mods = map(lambda a:
186c6dab2a8SThomas Monjalon                         a if a != 'vfio_pci' else 'vfio-pci', sysfs_mods)
187c6dab2a8SThomas Monjalon
188c6dab2a8SThomas Monjalon        for mod in mods:
189c6dab2a8SThomas Monjalon            if mod["Name"] in sysfs_mods:
190c6dab2a8SThomas Monjalon                mod["Found"] = True
191c6dab2a8SThomas Monjalon    except:
192c6dab2a8SThomas Monjalon        pass
193c6dab2a8SThomas Monjalon
194c6dab2a8SThomas Monjalon    # check if we have at least one loaded module
195c6dab2a8SThomas Monjalon    if True not in [mod["Found"] for mod in mods] and b_flag is not None:
196c6dab2a8SThomas Monjalon        if b_flag in dpdk_drivers:
197c6dab2a8SThomas Monjalon            print("Error - no supported modules(DPDK driver) are loaded")
198c6dab2a8SThomas Monjalon            sys.exit(1)
199c6dab2a8SThomas Monjalon        else:
200c6dab2a8SThomas Monjalon            print("Warning - no supported modules(DPDK driver) are loaded")
201c6dab2a8SThomas Monjalon
202c6dab2a8SThomas Monjalon    # change DPDK driver list to only contain drivers that are loaded
203c6dab2a8SThomas Monjalon    dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
204c6dab2a8SThomas Monjalon
205c6dab2a8SThomas Monjalon
206c6dab2a8SThomas Monjalondef has_driver(dev_id):
207c6dab2a8SThomas Monjalon    '''return true if a device is assigned to a driver. False otherwise'''
208c6dab2a8SThomas Monjalon    return "Driver_str" in devices[dev_id]
209c6dab2a8SThomas Monjalon
210c6dab2a8SThomas Monjalon
211*c3ce205dSGuduri Prathyushadef get_pci_device_details(dev_id, probe_lspci):
212c6dab2a8SThomas Monjalon    '''This function gets additional details for a PCI device'''
213c6dab2a8SThomas Monjalon    device = {}
214c6dab2a8SThomas Monjalon
215*c3ce205dSGuduri Prathyusha    if probe_lspci:
216c6dab2a8SThomas Monjalon        extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
217c6dab2a8SThomas Monjalon
218c6dab2a8SThomas Monjalon        # parse lspci details
219c6dab2a8SThomas Monjalon        for line in extra_info:
220c6dab2a8SThomas Monjalon            if len(line) == 0:
221c6dab2a8SThomas Monjalon                continue
222c6dab2a8SThomas Monjalon            name, value = line.decode().split("\t", 1)
223c6dab2a8SThomas Monjalon            name = name.strip(":") + "_str"
224c6dab2a8SThomas Monjalon            device[name] = value
225c6dab2a8SThomas Monjalon    # check for a unix interface name
226c6dab2a8SThomas Monjalon    device["Interface"] = ""
227c6dab2a8SThomas Monjalon    for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
228c6dab2a8SThomas Monjalon        if "net" in dirs:
229c6dab2a8SThomas Monjalon            device["Interface"] = \
230c6dab2a8SThomas Monjalon                ",".join(os.listdir(os.path.join(base, "net")))
231c6dab2a8SThomas Monjalon            break
232c6dab2a8SThomas Monjalon    # check if a port is used for ssh connection
233c6dab2a8SThomas Monjalon    device["Ssh_if"] = False
234c6dab2a8SThomas Monjalon    device["Active"] = ""
235c6dab2a8SThomas Monjalon
236c6dab2a8SThomas Monjalon    return device
237c6dab2a8SThomas Monjalon
238ea9f00f7SGuduri Prathyushadef clear_data():
239ea9f00f7SGuduri Prathyusha    '''This function clears any old data'''
240ea9f00f7SGuduri Prathyusha    devices = {}
241c6dab2a8SThomas Monjalon
242ea9f00f7SGuduri Prathyushadef get_device_details(devices_type):
243c6dab2a8SThomas Monjalon    '''This function populates the "devices" dictionary. The keys used are
244c6dab2a8SThomas Monjalon    the pci addresses (domain:bus:slot.func). The values are themselves
245c6dab2a8SThomas Monjalon    dictionaries - one for each NIC.'''
246c6dab2a8SThomas Monjalon    global devices
247c6dab2a8SThomas Monjalon    global dpdk_drivers
248c6dab2a8SThomas Monjalon
249c6dab2a8SThomas Monjalon    # first loop through and read details for all devices
250*c3ce205dSGuduri Prathyusha    # request machine readable format, with numeric IDs and String
251c6dab2a8SThomas Monjalon    dev = {}
252*c3ce205dSGuduri Prathyusha    dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
253c6dab2a8SThomas Monjalon    for dev_line in dev_lines:
254c6dab2a8SThomas Monjalon        if len(dev_line) == 0:
255ea9f00f7SGuduri Prathyusha            if dev["Class"][0:2] == devices_type:
256c6dab2a8SThomas Monjalon                # convert device and vendor ids to numbers, then add to global
257c6dab2a8SThomas Monjalon                dev["Vendor"] = int(dev["Vendor"], 16)
258c6dab2a8SThomas Monjalon                dev["Device"] = int(dev["Device"], 16)
259*c3ce205dSGuduri Prathyusha                if "Driver" in dev.keys():
260*c3ce205dSGuduri Prathyusha                    dev["Driver_str"] = dev.pop("Driver")
261c6dab2a8SThomas Monjalon                # use dict to make copy of dev
262c6dab2a8SThomas Monjalon                devices[dev["Slot"]] = dict(dev)
263*c3ce205dSGuduri Prathyusha            # Clear previous device's data
264*c3ce205dSGuduri Prathyusha            dev = {}
265c6dab2a8SThomas Monjalon        else:
266c6dab2a8SThomas Monjalon            name, value = dev_line.decode().split("\t", 1)
267*c3ce205dSGuduri Prathyusha            value_list = value.rsplit(' ', 1)
268*c3ce205dSGuduri Prathyusha            if len(value_list) > 1:
269*c3ce205dSGuduri Prathyusha                # String stored in <name>_str
270*c3ce205dSGuduri Prathyusha                dev[name.rstrip(":") + '_str'] = value_list[0]
271*c3ce205dSGuduri Prathyusha            # Numeric IDs
272*c3ce205dSGuduri Prathyusha            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
273*c3ce205dSGuduri Prathyusha                .rstrip("]").lstrip("[")
274c6dab2a8SThomas Monjalon
275ea9f00f7SGuduri Prathyusha    if devices_type == NETWORK_BASE_CLASS:
276c6dab2a8SThomas Monjalon        # check what is the interface if any for an ssh connection if
277c6dab2a8SThomas Monjalon        # any to this host, so we can mark it later.
278c6dab2a8SThomas Monjalon        ssh_if = []
279c6dab2a8SThomas Monjalon        route = check_output(["ip", "-o", "route"])
280c6dab2a8SThomas Monjalon        # filter out all lines for 169.254 routes
281c6dab2a8SThomas Monjalon        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
282c6dab2a8SThomas Monjalon                             route.decode().splitlines()))
283c6dab2a8SThomas Monjalon        rt_info = route.split()
284c6dab2a8SThomas Monjalon        for i in range(len(rt_info) - 1):
285c6dab2a8SThomas Monjalon            if rt_info[i] == "dev":
286c6dab2a8SThomas Monjalon                ssh_if.append(rt_info[i+1])
287c6dab2a8SThomas Monjalon
288c6dab2a8SThomas Monjalon    # based on the basic info, get extended text details
289c6dab2a8SThomas Monjalon    for d in devices.keys():
290ea9f00f7SGuduri Prathyusha        if devices[d]["Class"][0:2] != devices_type:
291617d9052SYoni Gilad            continue
292617d9052SYoni Gilad
293c6dab2a8SThomas Monjalon        # get additional info and add it to existing data
294c6dab2a8SThomas Monjalon        devices[d] = devices[d].copy()
295*c3ce205dSGuduri Prathyusha        # No need to probe lspci
296*c3ce205dSGuduri Prathyusha        devices[d].update(get_pci_device_details(d, False).items())
297c6dab2a8SThomas Monjalon
298ea9f00f7SGuduri Prathyusha        if devices_type == NETWORK_BASE_CLASS:
299ea9f00f7SGuduri Prathyusha            for _if in ssh_if:
300ea9f00f7SGuduri Prathyusha                if _if in devices[d]["Interface"].split(","):
301ea9f00f7SGuduri Prathyusha                    devices[d]["Ssh_if"] = True
302ea9f00f7SGuduri Prathyusha                    devices[d]["Active"] = "*Active*"
303ea9f00f7SGuduri Prathyusha                    break
304ea9f00f7SGuduri Prathyusha
305c6dab2a8SThomas Monjalon        # add igb_uio to list of supporting modules if needed
306c6dab2a8SThomas Monjalon        if "Module_str" in devices[d]:
307c6dab2a8SThomas Monjalon            for driver in dpdk_drivers:
308c6dab2a8SThomas Monjalon                if driver not in devices[d]["Module_str"]:
309c6dab2a8SThomas Monjalon                    devices[d]["Module_str"] = \
310c6dab2a8SThomas Monjalon                        devices[d]["Module_str"] + ",%s" % driver
311c6dab2a8SThomas Monjalon        else:
312c6dab2a8SThomas Monjalon            devices[d]["Module_str"] = ",".join(dpdk_drivers)
313c6dab2a8SThomas Monjalon
314c6dab2a8SThomas Monjalon        # make sure the driver and module strings do not have any duplicates
315c6dab2a8SThomas Monjalon        if has_driver(d):
316c6dab2a8SThomas Monjalon            modules = devices[d]["Module_str"].split(",")
317c6dab2a8SThomas Monjalon            if devices[d]["Driver_str"] in modules:
318c6dab2a8SThomas Monjalon                modules.remove(devices[d]["Driver_str"])
319c6dab2a8SThomas Monjalon                devices[d]["Module_str"] = ",".join(modules)
320c6dab2a8SThomas Monjalon
321c6dab2a8SThomas Monjalon
322c6dab2a8SThomas Monjalondef dev_id_from_dev_name(dev_name):
323c6dab2a8SThomas Monjalon    '''Take a device "name" - a string passed in by user to identify a NIC
324c6dab2a8SThomas Monjalon    device, and determine the device id - i.e. the domain:bus:slot.func - for
325c6dab2a8SThomas Monjalon    it, which can then be used to index into the devices array'''
326c6dab2a8SThomas Monjalon
327c6dab2a8SThomas Monjalon    # check if it's already a suitable index
328c6dab2a8SThomas Monjalon    if dev_name in devices:
329c6dab2a8SThomas Monjalon        return dev_name
330c6dab2a8SThomas Monjalon    # check if it's an index just missing the domain part
331c6dab2a8SThomas Monjalon    elif "0000:" + dev_name in devices:
332c6dab2a8SThomas Monjalon        return "0000:" + dev_name
333c6dab2a8SThomas Monjalon    else:
334c6dab2a8SThomas Monjalon        # check if it's an interface name, e.g. eth1
335c6dab2a8SThomas Monjalon        for d in devices.keys():
336c6dab2a8SThomas Monjalon            if dev_name in devices[d]["Interface"].split(","):
337c6dab2a8SThomas Monjalon                return devices[d]["Slot"]
338c6dab2a8SThomas Monjalon    # if nothing else matches - error
339c6dab2a8SThomas Monjalon    print("Unknown device: %s. "
340c6dab2a8SThomas Monjalon          "Please specify device in \"bus:slot.func\" format" % dev_name)
341c6dab2a8SThomas Monjalon    sys.exit(1)
342c6dab2a8SThomas Monjalon
343c6dab2a8SThomas Monjalon
344c6dab2a8SThomas Monjalondef unbind_one(dev_id, force):
345c6dab2a8SThomas Monjalon    '''Unbind the device identified by "dev_id" from its current driver'''
346c6dab2a8SThomas Monjalon    dev = devices[dev_id]
347c6dab2a8SThomas Monjalon    if not has_driver(dev_id):
348c6dab2a8SThomas Monjalon        print("%s %s %s is not currently managed by any driver\n" %
349c6dab2a8SThomas Monjalon              (dev["Slot"], dev["Device_str"], dev["Interface"]))
350c6dab2a8SThomas Monjalon        return
351c6dab2a8SThomas Monjalon
352c6dab2a8SThomas Monjalon    # prevent us disconnecting ourselves
353c6dab2a8SThomas Monjalon    if dev["Ssh_if"] and not force:
354c6dab2a8SThomas Monjalon        print("Routing table indicates that interface %s is active. "
355c6dab2a8SThomas Monjalon              "Skipping unbind" % (dev_id))
356c6dab2a8SThomas Monjalon        return
357c6dab2a8SThomas Monjalon
358c6dab2a8SThomas Monjalon    # write to /sys to unbind
359c6dab2a8SThomas Monjalon    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
360c6dab2a8SThomas Monjalon    try:
361c6dab2a8SThomas Monjalon        f = open(filename, "a")
362c6dab2a8SThomas Monjalon    except:
363c6dab2a8SThomas Monjalon        print("Error: unbind failed for %s - Cannot open %s"
364c6dab2a8SThomas Monjalon              % (dev_id, filename))
365c6dab2a8SThomas Monjalon        sys.exit(1)
366c6dab2a8SThomas Monjalon    f.write(dev_id)
367c6dab2a8SThomas Monjalon    f.close()
368c6dab2a8SThomas Monjalon
369c6dab2a8SThomas Monjalon
370c6dab2a8SThomas Monjalondef bind_one(dev_id, driver, force):
371c6dab2a8SThomas Monjalon    '''Bind the device given by "dev_id" to the driver "driver". If the device
372c6dab2a8SThomas Monjalon    is already bound to a different driver, it will be unbound first'''
373c6dab2a8SThomas Monjalon    dev = devices[dev_id]
374c6dab2a8SThomas Monjalon    saved_driver = None  # used to rollback any unbind in case of failure
375c6dab2a8SThomas Monjalon
376c6dab2a8SThomas Monjalon    # prevent disconnection of our ssh session
377c6dab2a8SThomas Monjalon    if dev["Ssh_if"] and not force:
378c6dab2a8SThomas Monjalon        print("Routing table indicates that interface %s is active. "
379c6dab2a8SThomas Monjalon              "Not modifying" % (dev_id))
380c6dab2a8SThomas Monjalon        return
381c6dab2a8SThomas Monjalon
382c6dab2a8SThomas Monjalon    # unbind any existing drivers we don't want
383c6dab2a8SThomas Monjalon    if has_driver(dev_id):
384c6dab2a8SThomas Monjalon        if dev["Driver_str"] == driver:
385c6dab2a8SThomas Monjalon            print("%s already bound to driver %s, skipping\n"
386c6dab2a8SThomas Monjalon                  % (dev_id, driver))
387c6dab2a8SThomas Monjalon            return
388c6dab2a8SThomas Monjalon        else:
389c6dab2a8SThomas Monjalon            saved_driver = dev["Driver_str"]
390c6dab2a8SThomas Monjalon            unbind_one(dev_id, force)
391c6dab2a8SThomas Monjalon            dev["Driver_str"] = ""  # clear driver string
392c6dab2a8SThomas Monjalon
393c6dab2a8SThomas Monjalon    # if we are binding to one of DPDK drivers, add PCI id's to that driver
394c6dab2a8SThomas Monjalon    if driver in dpdk_drivers:
395c6dab2a8SThomas Monjalon        filename = "/sys/bus/pci/drivers/%s/new_id" % driver
396c6dab2a8SThomas Monjalon        try:
397c6dab2a8SThomas Monjalon            f = open(filename, "w")
398c6dab2a8SThomas Monjalon        except:
399c6dab2a8SThomas Monjalon            print("Error: bind failed for %s - Cannot open %s"
400c6dab2a8SThomas Monjalon                  % (dev_id, filename))
401c6dab2a8SThomas Monjalon            return
402c6dab2a8SThomas Monjalon        try:
403c6dab2a8SThomas Monjalon            f.write("%04x %04x" % (dev["Vendor"], dev["Device"]))
404c6dab2a8SThomas Monjalon            f.close()
405c6dab2a8SThomas Monjalon        except:
406c6dab2a8SThomas Monjalon            print("Error: bind failed for %s - Cannot write new PCI ID to "
407c6dab2a8SThomas Monjalon                  "driver %s" % (dev_id, driver))
408c6dab2a8SThomas Monjalon            return
409c6dab2a8SThomas Monjalon
410c6dab2a8SThomas Monjalon    # do the bind by writing to /sys
411c6dab2a8SThomas Monjalon    filename = "/sys/bus/pci/drivers/%s/bind" % driver
412c6dab2a8SThomas Monjalon    try:
413c6dab2a8SThomas Monjalon        f = open(filename, "a")
414c6dab2a8SThomas Monjalon    except:
415c6dab2a8SThomas Monjalon        print("Error: bind failed for %s - Cannot open %s"
416c6dab2a8SThomas Monjalon              % (dev_id, filename))
417c6dab2a8SThomas Monjalon        if saved_driver is not None:  # restore any previous driver
418c6dab2a8SThomas Monjalon            bind_one(dev_id, saved_driver, force)
419c6dab2a8SThomas Monjalon        return
420c6dab2a8SThomas Monjalon    try:
421c6dab2a8SThomas Monjalon        f.write(dev_id)
422c6dab2a8SThomas Monjalon        f.close()
423c6dab2a8SThomas Monjalon    except:
424c6dab2a8SThomas Monjalon        # for some reason, closing dev_id after adding a new PCI ID to new_id
425c6dab2a8SThomas Monjalon        # results in IOError. however, if the device was successfully bound,
426c6dab2a8SThomas Monjalon        # we don't care for any errors and can safely ignore IOError
427*c3ce205dSGuduri Prathyusha        tmp = get_pci_device_details(dev_id, True)
428c6dab2a8SThomas Monjalon        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
429c6dab2a8SThomas Monjalon            return
430c6dab2a8SThomas Monjalon        print("Error: bind failed for %s - Cannot bind to driver %s"
431c6dab2a8SThomas Monjalon              % (dev_id, driver))
432c6dab2a8SThomas Monjalon        if saved_driver is not None:  # restore any previous driver
433c6dab2a8SThomas Monjalon            bind_one(dev_id, saved_driver, force)
434c6dab2a8SThomas Monjalon        return
435c6dab2a8SThomas Monjalon
436c6dab2a8SThomas Monjalon
437c6dab2a8SThomas Monjalondef unbind_all(dev_list, force=False):
438c6dab2a8SThomas Monjalon    """Unbind method, takes a list of device locations"""
439c6dab2a8SThomas Monjalon    dev_list = map(dev_id_from_dev_name, dev_list)
440c6dab2a8SThomas Monjalon    for d in dev_list:
441c6dab2a8SThomas Monjalon        unbind_one(d, force)
442c6dab2a8SThomas Monjalon
443c6dab2a8SThomas Monjalon
444c6dab2a8SThomas Monjalondef bind_all(dev_list, driver, force=False):
445c6dab2a8SThomas Monjalon    """Bind method, takes a list of device locations"""
446c6dab2a8SThomas Monjalon    global devices
447c6dab2a8SThomas Monjalon
448c6dab2a8SThomas Monjalon    dev_list = map(dev_id_from_dev_name, dev_list)
449c6dab2a8SThomas Monjalon
450c6dab2a8SThomas Monjalon    for d in dev_list:
451c6dab2a8SThomas Monjalon        bind_one(d, driver, force)
452c6dab2a8SThomas Monjalon
453c6dab2a8SThomas Monjalon    # when binding devices to a generic driver (i.e. one that doesn't have a
454c6dab2a8SThomas Monjalon    # PCI ID table), some devices that are not bound to any other driver could
455c6dab2a8SThomas Monjalon    # be bound even if no one has asked them to. hence, we check the list of
456c6dab2a8SThomas Monjalon    # drivers again, and see if some of the previously-unbound devices were
457c6dab2a8SThomas Monjalon    # erroneously bound.
458c6dab2a8SThomas Monjalon    for d in devices.keys():
459c6dab2a8SThomas Monjalon        # skip devices that were already bound or that we know should be bound
460c6dab2a8SThomas Monjalon        if "Driver_str" in devices[d] or d in dev_list:
461c6dab2a8SThomas Monjalon            continue
462c6dab2a8SThomas Monjalon
463c6dab2a8SThomas Monjalon        # update information about this device
464c6dab2a8SThomas Monjalon        devices[d] = dict(devices[d].items() +
465*c3ce205dSGuduri Prathyusha                          get_pci_device_details(d, True).items())
466c6dab2a8SThomas Monjalon
467c6dab2a8SThomas Monjalon        # check if updated information indicates that the device was bound
468c6dab2a8SThomas Monjalon        if "Driver_str" in devices[d]:
469c6dab2a8SThomas Monjalon            unbind_one(d, force)
470c6dab2a8SThomas Monjalon
471c6dab2a8SThomas Monjalon
472c6dab2a8SThomas Monjalondef display_devices(title, dev_list, extra_params=None):
473c6dab2a8SThomas Monjalon    '''Displays to the user the details of a list of devices given in
474c6dab2a8SThomas Monjalon    "dev_list". The "extra_params" parameter, if given, should contain a string
475c6dab2a8SThomas Monjalon     with %()s fields in it for replacement by the named fields in each
476c6dab2a8SThomas Monjalon     device's dictionary.'''
477c6dab2a8SThomas Monjalon    strings = []  # this holds the strings to print. We sort before printing
478c6dab2a8SThomas Monjalon    print("\n%s" % title)
479c6dab2a8SThomas Monjalon    print("="*len(title))
480c6dab2a8SThomas Monjalon    if len(dev_list) == 0:
481c6dab2a8SThomas Monjalon        strings.append("<none>")
482c6dab2a8SThomas Monjalon    else:
483c6dab2a8SThomas Monjalon        for dev in dev_list:
484c6dab2a8SThomas Monjalon            if extra_params is not None:
485*c3ce205dSGuduri Prathyusha                strings.append("%s '%s %s' %s" % (dev["Slot"],
486c6dab2a8SThomas Monjalon                                               dev["Device_str"],
487*c3ce205dSGuduri Prathyusha                                               dev["Device"],
488c6dab2a8SThomas Monjalon                                               extra_params % dev))
489c6dab2a8SThomas Monjalon            else:
490c6dab2a8SThomas Monjalon                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
491c6dab2a8SThomas Monjalon    # sort before printing, so that the entries appear in PCI order
492c6dab2a8SThomas Monjalon    strings.sort()
493c6dab2a8SThomas Monjalon    print("\n".join(strings))  # print one per line
494c6dab2a8SThomas Monjalon
495c7dd412bSGuduri Prathyushadef show_device_status(devices_type, device_name):
496c6dab2a8SThomas Monjalon    global dpdk_drivers
497c6dab2a8SThomas Monjalon    kernel_drv = []
498c6dab2a8SThomas Monjalon    dpdk_drv = []
499c6dab2a8SThomas Monjalon    no_drv = []
500c6dab2a8SThomas Monjalon
501c6dab2a8SThomas Monjalon    # split our list of network devices into the three categories above
502c6dab2a8SThomas Monjalon    for d in devices.keys():
503c7dd412bSGuduri Prathyusha        if devices_type in devices[d]["Class"]:
504c6dab2a8SThomas Monjalon            if not has_driver(d):
505c6dab2a8SThomas Monjalon                no_drv.append(devices[d])
506c6dab2a8SThomas Monjalon                continue
507c6dab2a8SThomas Monjalon            if devices[d]["Driver_str"] in dpdk_drivers:
508c6dab2a8SThomas Monjalon                dpdk_drv.append(devices[d])
509c6dab2a8SThomas Monjalon            else:
510c6dab2a8SThomas Monjalon                kernel_drv.append(devices[d])
511c6dab2a8SThomas Monjalon
512c6dab2a8SThomas Monjalon    # print each category separately, so we can clearly see what's used by DPDK
513*c3ce205dSGuduri Prathyusha    display_devices("%s devices using DPDK-compatible driver" % device_name,
514*c3ce205dSGuduri Prathyusha                    dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
515c7dd412bSGuduri Prathyusha    display_devices("%s devices using kernel driver" % device_name, kernel_drv,
516c6dab2a8SThomas Monjalon                    "if=%(Interface)s drv=%(Driver_str)s "
517c6dab2a8SThomas Monjalon                    "unused=%(Module_str)s %(Active)s")
518*c3ce205dSGuduri Prathyusha    display_devices("Other %s devices" % device_name, no_drv,
519*c3ce205dSGuduri Prathyusha                    "unused=%(Module_str)s")
520c6dab2a8SThomas Monjalon
521c7dd412bSGuduri Prathyushadef show_status():
522c7dd412bSGuduri Prathyusha    '''Function called when the script is passed the "--status" option.
523c7dd412bSGuduri Prathyusha    Displays to the user what devices are bound to the igb_uio driver, the
524c7dd412bSGuduri Prathyusha    kernel driver or to no driver'''
525c6dab2a8SThomas Monjalon
526c7dd412bSGuduri Prathyusha    show_device_status(network_devices, "Network")
527c7dd412bSGuduri Prathyusha    show_device_status(crypto_devices, "Crypto")
528c6dab2a8SThomas Monjalon
529c6dab2a8SThomas Monjalondef parse_args():
530c6dab2a8SThomas Monjalon    '''Parses the command-line arguments given by the user and takes the
531c6dab2a8SThomas Monjalon    appropriate action for each'''
532c6dab2a8SThomas Monjalon    global b_flag
533c6dab2a8SThomas Monjalon    global status_flag
534c6dab2a8SThomas Monjalon    global force_flag
535c6dab2a8SThomas Monjalon    global args
536c6dab2a8SThomas Monjalon    if len(sys.argv) <= 1:
537c6dab2a8SThomas Monjalon        usage()
538c6dab2a8SThomas Monjalon        sys.exit(0)
539c6dab2a8SThomas Monjalon
540c6dab2a8SThomas Monjalon    try:
541c6dab2a8SThomas Monjalon        opts, args = getopt.getopt(sys.argv[1:], "b:us",
542c6dab2a8SThomas Monjalon                                   ["help", "usage", "status", "force",
543c6dab2a8SThomas Monjalon                                    "bind=", "unbind"])
544c6dab2a8SThomas Monjalon    except getopt.GetoptError as error:
545c6dab2a8SThomas Monjalon        print(str(error))
546c6dab2a8SThomas Monjalon        print("Run '%s --usage' for further information" % sys.argv[0])
547c6dab2a8SThomas Monjalon        sys.exit(1)
548c6dab2a8SThomas Monjalon
549c6dab2a8SThomas Monjalon    for opt, arg in opts:
550c6dab2a8SThomas Monjalon        if opt == "--help" or opt == "--usage":
551c6dab2a8SThomas Monjalon            usage()
552c6dab2a8SThomas Monjalon            sys.exit(0)
553c6dab2a8SThomas Monjalon        if opt == "--status" or opt == "-s":
554c6dab2a8SThomas Monjalon            status_flag = True
555c6dab2a8SThomas Monjalon        if opt == "--force":
556c6dab2a8SThomas Monjalon            force_flag = True
557c6dab2a8SThomas Monjalon        if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
558c6dab2a8SThomas Monjalon            if b_flag is not None:
559c6dab2a8SThomas Monjalon                print("Error - Only one bind or unbind may be specified\n")
560c6dab2a8SThomas Monjalon                sys.exit(1)
561c6dab2a8SThomas Monjalon            if opt == "-u" or opt == "--unbind":
562c6dab2a8SThomas Monjalon                b_flag = "none"
563c6dab2a8SThomas Monjalon            else:
564c6dab2a8SThomas Monjalon                b_flag = arg
565c6dab2a8SThomas Monjalon
566c6dab2a8SThomas Monjalon
567c6dab2a8SThomas Monjalondef do_arg_actions():
568c6dab2a8SThomas Monjalon    '''do the actual action requested by the user'''
569c6dab2a8SThomas Monjalon    global b_flag
570c6dab2a8SThomas Monjalon    global status_flag
571c6dab2a8SThomas Monjalon    global force_flag
572c6dab2a8SThomas Monjalon    global args
573c6dab2a8SThomas Monjalon
574c6dab2a8SThomas Monjalon    if b_flag is None and not status_flag:
575c6dab2a8SThomas Monjalon        print("Error: No action specified for devices."
576c6dab2a8SThomas Monjalon              "Please give a -b or -u option")
577c6dab2a8SThomas Monjalon        print("Run '%s --usage' for further information" % sys.argv[0])
578c6dab2a8SThomas Monjalon        sys.exit(1)
579c6dab2a8SThomas Monjalon
580c6dab2a8SThomas Monjalon    if b_flag is not None and len(args) == 0:
581c6dab2a8SThomas Monjalon        print("Error: No devices specified.")
582c6dab2a8SThomas Monjalon        print("Run '%s --usage' for further information" % sys.argv[0])
583c6dab2a8SThomas Monjalon        sys.exit(1)
584c6dab2a8SThomas Monjalon
585c6dab2a8SThomas Monjalon    if b_flag == "none" or b_flag == "None":
586c6dab2a8SThomas Monjalon        unbind_all(args, force_flag)
587c6dab2a8SThomas Monjalon    elif b_flag is not None:
588c6dab2a8SThomas Monjalon        bind_all(args, b_flag, force_flag)
589c6dab2a8SThomas Monjalon    if status_flag:
590c6dab2a8SThomas Monjalon        if b_flag is not None:
591ea9f00f7SGuduri Prathyusha            clear_data()
592ea9f00f7SGuduri Prathyusha            get_device_details(NETWORK_BASE_CLASS)  # refresh if we have changed anything
593ea9f00f7SGuduri Prathyusha            get_device_details(CRYPTO_BASE_CLASS)  # refresh if we have changed anything
594c6dab2a8SThomas Monjalon        show_status()
595c6dab2a8SThomas Monjalon
596c6dab2a8SThomas Monjalon
597c6dab2a8SThomas Monjalondef main():
598c6dab2a8SThomas Monjalon    '''program main function'''
599c6dab2a8SThomas Monjalon    parse_args()
600c6dab2a8SThomas Monjalon    check_modules()
601ea9f00f7SGuduri Prathyusha    clear_data()
602ea9f00f7SGuduri Prathyusha    get_device_details(NETWORK_BASE_CLASS)
603ea9f00f7SGuduri Prathyusha    get_device_details(CRYPTO_BASE_CLASS)
604c6dab2a8SThomas Monjalon    do_arg_actions()
605c6dab2a8SThomas Monjalon
606c6dab2a8SThomas Monjalonif __name__ == "__main__":
607c6dab2a8SThomas Monjalon    main()
608