xref: /dpdk/usertools/dpdk-devbind.py (revision 5630257fcc30397e7217139ec55da4f301f59fb7)
1#! /usr/bin/env python
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2010-2014 Intel Corporation
4#
5
6import sys
7import os
8import getopt
9import subprocess
10from os.path import exists, abspath, dirname, basename
11
12# The PCI base class for all devices
13network_class = {'Class': '02', 'Vendor': None, 'Device': None,
14                    'SVendor': None, 'SDevice': None}
15encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
16                   'SVendor': None, 'SDevice': None}
17intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
18                   'SVendor': None, 'SDevice': None}
19cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
20              'SVendor': None, 'SDevice': None}
21cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
22              'SVendor': None, 'SDevice': None}
23cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
24              'SVendor': None, 'SDevice': None}
25
26network_devices = [network_class, cavium_pkx]
27crypto_devices = [encryption_class, intel_processor_class]
28eventdev_devices = [cavium_sso]
29mempool_devices = [cavium_fpa]
30
31# global dict ethernet devices present. Dictionary indexed by PCI address.
32# Each device within this is itself a dictionary of device properties
33devices = {}
34# list of supported DPDK drivers
35dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
36
37# command-line arg flags
38b_flag = None
39status_flag = False
40force_flag = False
41args = []
42
43
44def usage():
45    '''Print usage information for the program'''
46    argv0 = basename(sys.argv[0])
47    print("""
48Usage:
49------
50
51     %(argv0)s [options] DEVICE1 DEVICE2 ....
52
53where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
54or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
55also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
56
57Options:
58    --help, --usage:
59        Display usage information and quit
60
61    -s, --status:
62        Print the current status of all known network, crypto, event
63        and mempool devices.
64        For each device, it displays the PCI domain, bus, slot and function,
65        along with a text description of the device. Depending upon whether the
66        device is being used by a kernel driver, the igb_uio driver, or no
67        driver, other relevant information will be displayed:
68        * the Linux interface name e.g. if=eth0
69        * the driver being used e.g. drv=igb_uio
70        * any suitable drivers not currently using that device
71            e.g. unused=igb_uio
72        NOTE: if this flag is passed along with a bind/unbind option, the
73        status display will always occur after the other operations have taken
74        place.
75
76    --status-dev:
77        Print the status of given device group. Supported device groups are:
78        "net", "crypto", "event" and "mempool"
79
80    -b driver, --bind=driver:
81        Select the driver to use or \"none\" to unbind the device
82
83    -u, --unbind:
84        Unbind a device (Equivalent to \"-b none\")
85
86    --force:
87        By default, network devices which are used by Linux - as indicated by
88        having routes in the routing table - cannot be modified. Using the
89        --force flag overrides this behavior, allowing active links to be
90        forcibly unbound.
91        WARNING: This can lead to loss of network connection and should be used
92        with caution.
93
94Examples:
95---------
96
97To display current device status:
98        %(argv0)s --status
99
100To display current network device status:
101        %(argv0)s --status-dev net
102
103To bind eth1 from the current driver and move to use igb_uio
104        %(argv0)s --bind=igb_uio eth1
105
106To unbind 0000:01:00.0 from using any driver
107        %(argv0)s -u 0000:01:00.0
108
109To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
110        %(argv0)s -b ixgbe 02:00.0 02:00.1
111
112    """ % locals())  # replace items from local variables
113
114
115# This is roughly compatible with check_output function in subprocess module
116# which is only available in python 2.7.
117def check_output(args, stderr=None):
118    '''Run a command and capture its output'''
119    return subprocess.Popen(args, stdout=subprocess.PIPE,
120                            stderr=stderr).communicate()[0]
121
122
123def check_modules():
124    '''Checks that igb_uio is loaded'''
125    global dpdk_drivers
126
127    # list of supported modules
128    mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
129
130    # first check if module is loaded
131    try:
132        # Get list of sysfs modules (both built-in and dynamically loaded)
133        sysfs_path = '/sys/module/'
134
135        # Get the list of directories in sysfs_path
136        sysfs_mods = [os.path.join(sysfs_path, o) for o
137                      in os.listdir(sysfs_path)
138                      if os.path.isdir(os.path.join(sysfs_path, o))]
139
140        # Extract the last element of '/sys/module/abc' in the array
141        sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
142
143        # special case for vfio_pci (module is named vfio-pci,
144        # but its .ko is named vfio_pci)
145        sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
146
147        for mod in mods:
148            if mod["Name"] in sysfs_mods:
149                mod["Found"] = True
150    except:
151        pass
152
153    # check if we have at least one loaded module
154    if True not in [mod["Found"] for mod in mods] and b_flag is not None:
155        if b_flag in dpdk_drivers:
156            print("Error - no supported modules(DPDK driver) are loaded")
157            sys.exit(1)
158        else:
159            print("Warning - no supported modules(DPDK driver) are loaded")
160
161    # change DPDK driver list to only contain drivers that are loaded
162    dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
163
164
165def 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]
168
169
170def get_pci_device_details(dev_id, probe_lspci):
171    '''This function gets additional details for a PCI device'''
172    device = {}
173
174    if probe_lspci:
175        extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
176
177        # parse lspci details
178        for line in extra_info:
179            if len(line) == 0:
180                continue
181            name, value = line.decode().split("\t", 1)
182            name = name.strip(":") + "_str"
183            device[name] = value
184    # check for a unix interface name
185    device["Interface"] = ""
186    for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
187        if "net" in dirs:
188            device["Interface"] = \
189                ",".join(os.listdir(os.path.join(base, "net")))
190            break
191    # check if a port is used for ssh connection
192    device["Ssh_if"] = False
193    device["Active"] = ""
194
195    return device
196
197def clear_data():
198    '''This function clears any old data'''
199    devices = {}
200
201def get_device_details(devices_type):
202    '''This function populates the "devices" dictionary. The keys used are
203    the pci addresses (domain:bus:slot.func). The values are themselves
204    dictionaries - one for each NIC.'''
205    global devices
206    global dpdk_drivers
207
208    # first loop through and read details for all devices
209    # request machine readable format, with numeric IDs and String
210    dev = {}
211    dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
212    for dev_line in dev_lines:
213        if len(dev_line) == 0:
214            if device_type_match(dev, devices_type):
215                # Replace "Driver" with "Driver_str" to have consistency of
216                # of dictionary key names
217                if "Driver" in dev.keys():
218                    dev["Driver_str"] = dev.pop("Driver")
219                if "Module" in dev.keys():
220                    dev["Module_str"] = dev.pop("Module")
221                # use dict to make copy of dev
222                devices[dev["Slot"]] = dict(dev)
223            # Clear previous device's data
224            dev = {}
225        else:
226            name, value = dev_line.decode().split("\t", 1)
227            value_list = value.rsplit(' ', 1)
228            if len(value_list) > 1:
229                # String stored in <name>_str
230                dev[name.rstrip(":") + '_str'] = value_list[0]
231            # Numeric IDs
232            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
233                .rstrip("]").lstrip("[")
234
235    if devices_type == network_devices:
236        # check what is the interface if any for an ssh connection if
237        # any to this host, so we can mark it later.
238        ssh_if = []
239        route = check_output(["ip", "-o", "route"])
240        # filter out all lines for 169.254 routes
241        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
242                             route.decode().splitlines()))
243        rt_info = route.split()
244        for i in range(len(rt_info) - 1):
245            if rt_info[i] == "dev":
246                ssh_if.append(rt_info[i+1])
247
248    # based on the basic info, get extended text details
249    for d in devices.keys():
250        if not device_type_match(devices[d], devices_type):
251            continue
252
253        # get additional info and add it to existing data
254        devices[d] = devices[d].copy()
255        # No need to probe lspci
256        devices[d].update(get_pci_device_details(d, False).items())
257
258        if devices_type == network_devices:
259            for _if in ssh_if:
260                if _if in devices[d]["Interface"].split(","):
261                    devices[d]["Ssh_if"] = True
262                    devices[d]["Active"] = "*Active*"
263                    break
264
265        # add igb_uio to list of supporting modules if needed
266        if "Module_str" in devices[d]:
267            for driver in dpdk_drivers:
268                if driver not in devices[d]["Module_str"]:
269                    devices[d]["Module_str"] = \
270                        devices[d]["Module_str"] + ",%s" % driver
271        else:
272            devices[d]["Module_str"] = ",".join(dpdk_drivers)
273
274        # make sure the driver and module strings do not have any duplicates
275        if has_driver(d):
276            modules = devices[d]["Module_str"].split(",")
277            if devices[d]["Driver_str"] in modules:
278                modules.remove(devices[d]["Driver_str"])
279                devices[d]["Module_str"] = ",".join(modules)
280
281
282def device_type_match(dev, devices_type):
283    for i in range(len(devices_type)):
284        param_count = len(
285            [x for x in devices_type[i].values() if x is not None])
286        match_count = 0
287        if dev["Class"][0:2] == devices_type[i]["Class"]:
288            match_count = match_count + 1
289            for key in devices_type[i].keys():
290                if key != 'Class' and devices_type[i][key]:
291                    value_list = devices_type[i][key].split(',')
292                    for value in value_list:
293                        if value.strip(' ') == dev[key]:
294                            match_count = match_count + 1
295            # count must be the number of non None parameters to match
296            if match_count == param_count:
297                return True
298    return False
299
300def dev_id_from_dev_name(dev_name):
301    '''Take a device "name" - a string passed in by user to identify a NIC
302    device, and determine the device id - i.e. the domain:bus:slot.func - for
303    it, which can then be used to index into the devices array'''
304
305    # check if it's already a suitable index
306    if dev_name in devices:
307        return dev_name
308    # check if it's an index just missing the domain part
309    elif "0000:" + dev_name in devices:
310        return "0000:" + dev_name
311    else:
312        # check if it's an interface name, e.g. eth1
313        for d in devices.keys():
314            if dev_name in devices[d]["Interface"].split(","):
315                return devices[d]["Slot"]
316    # if nothing else matches - error
317    print("Unknown device: %s. "
318          "Please specify device in \"bus:slot.func\" format" % dev_name)
319    sys.exit(1)
320
321
322def unbind_one(dev_id, force):
323    '''Unbind the device identified by "dev_id" from its current driver'''
324    dev = devices[dev_id]
325    if not has_driver(dev_id):
326        print("%s %s %s is not currently managed by any driver\n" %
327              (dev["Slot"], dev["Device_str"], dev["Interface"]))
328        return
329
330    # prevent us disconnecting ourselves
331    if dev["Ssh_if"] and not force:
332        print("Routing table indicates that interface %s is active. "
333              "Skipping unbind" % (dev_id))
334        return
335
336    # write to /sys to unbind
337    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
338    try:
339        f = open(filename, "a")
340    except:
341        print("Error: unbind failed for %s - Cannot open %s"
342              % (dev_id, filename))
343        sys.exit(1)
344    f.write(dev_id)
345    f.close()
346
347
348def bind_one(dev_id, driver, force):
349    '''Bind the device given by "dev_id" to the driver "driver". If the device
350    is already bound to a different driver, it will be unbound first'''
351    dev = devices[dev_id]
352    saved_driver = None  # used to rollback any unbind in case of failure
353
354    # prevent disconnection of our ssh session
355    if dev["Ssh_if"] and not force:
356        print("Routing table indicates that interface %s is active. "
357              "Not modifying" % (dev_id))
358        return
359
360    # unbind any existing drivers we don't want
361    if has_driver(dev_id):
362        if dev["Driver_str"] == driver:
363            print("%s already bound to driver %s, skipping\n"
364                  % (dev_id, driver))
365            return
366        else:
367            saved_driver = dev["Driver_str"]
368            unbind_one(dev_id, force)
369            dev["Driver_str"] = ""  # clear driver string
370
371    # For kernels >= 3.15 driver_override can be used to specify the driver
372    # for a device rather than relying on the driver to provide a positive
373    # match of the device.  The existing process of looking up
374    # the vendor and device ID, adding them to the driver new_id,
375    # will erroneously bind other devices too which has the additional burden
376    # of unbinding those devices
377    if driver in dpdk_drivers:
378        filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
379        if os.path.exists(filename):
380            try:
381                f = open(filename, "w")
382            except:
383                print("Error: bind failed for %s - Cannot open %s"
384                      % (dev_id, filename))
385                return
386            try:
387                f.write("%s" % driver)
388                f.close()
389            except:
390                print("Error: bind failed for %s - Cannot write driver %s to "
391                      "PCI ID " % (dev_id, driver))
392                return
393        # For kernels < 3.15 use new_id to add PCI id's to the driver
394        else:
395            filename = "/sys/bus/pci/drivers/%s/new_id" % driver
396            try:
397                f = open(filename, "w")
398            except:
399                print("Error: bind failed for %s - Cannot open %s"
400                      % (dev_id, filename))
401                return
402            try:
403                # Convert Device and Vendor Id to int to write to new_id
404                f.write("%04x %04x" % (int(dev["Vendor"],16),
405                        int(dev["Device"], 16)))
406                f.close()
407            except:
408                print("Error: bind failed for %s - Cannot write new PCI ID to "
409                      "driver %s" % (dev_id, driver))
410                return
411
412    # do the bind by writing to /sys
413    filename = "/sys/bus/pci/drivers/%s/bind" % driver
414    try:
415        f = open(filename, "a")
416    except:
417        print("Error: bind failed for %s - Cannot open %s"
418              % (dev_id, filename))
419        if saved_driver is not None:  # restore any previous driver
420            bind_one(dev_id, saved_driver, force)
421        return
422    try:
423        f.write(dev_id)
424        f.close()
425    except:
426        # for some reason, closing dev_id after adding a new PCI ID to new_id
427        # results in IOError. however, if the device was successfully bound,
428        # we don't care for any errors and can safely ignore IOError
429        tmp = get_pci_device_details(dev_id, True)
430        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
431            return
432        print("Error: bind failed for %s - Cannot bind to driver %s"
433              % (dev_id, driver))
434        if saved_driver is not None:  # restore any previous driver
435            bind_one(dev_id, saved_driver, force)
436        return
437
438    # For kernels > 3.15 driver_override is used to bind a device to a driver.
439    # Before unbinding it, overwrite driver_override with empty string so that
440    # the device can be bound to any other driver
441    filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
442    if os.path.exists(filename):
443        try:
444            f = open(filename, "w")
445        except:
446            print("Error: unbind failed for %s - Cannot open %s"
447                  % (dev_id, filename))
448            sys.exit(1)
449        try:
450            f.write("\00")
451            f.close()
452        except:
453            print("Error: unbind failed for %s - Cannot open %s"
454                  % (dev_id, filename))
455            sys.exit(1)
456
457
458def unbind_all(dev_list, force=False):
459    """Unbind method, takes a list of device locations"""
460
461    if dev_list[0] == "dpdk":
462        for d in devices.keys():
463            if "Driver_str" in devices[d]:
464                if devices[d]["Driver_str"] in dpdk_drivers:
465                    unbind_one(devices[d]["Slot"], force)
466        return
467
468    dev_list = map(dev_id_from_dev_name, dev_list)
469    for d in dev_list:
470        unbind_one(d, force)
471
472
473def bind_all(dev_list, driver, force=False):
474    """Bind method, takes a list of device locations"""
475    global devices
476
477    dev_list = map(dev_id_from_dev_name, dev_list)
478
479    for d in dev_list:
480        bind_one(d, driver, force)
481
482    # For kernels < 3.15 when binding devices to a generic driver
483    # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
484    # that are not bound to any other driver could be bound even if no one has
485    # asked them to. hence, we check the list of drivers again, and see if
486    # some of the previously-unbound devices were erroneously bound.
487    if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
488        for d in devices.keys():
489            # skip devices that were already bound or that we know should be bound
490            if "Driver_str" in devices[d] or d in dev_list:
491                continue
492
493            # update information about this device
494            devices[d] = dict(devices[d].items() +
495                              get_pci_device_details(d, True).items())
496
497            # check if updated information indicates that the device was bound
498            if "Driver_str" in devices[d]:
499                unbind_one(d, force)
500
501
502def display_devices(title, dev_list, extra_params=None):
503    '''Displays to the user the details of a list of devices given in
504    "dev_list". The "extra_params" parameter, if given, should contain a string
505     with %()s fields in it for replacement by the named fields in each
506     device's dictionary.'''
507    strings = []  # this holds the strings to print. We sort before printing
508    print("\n%s" % title)
509    print("="*len(title))
510    if len(dev_list) == 0:
511        strings.append("<none>")
512    else:
513        for dev in dev_list:
514            if extra_params is not None:
515                strings.append("%s '%s %s' %s" % (dev["Slot"],
516                                               dev["Device_str"],
517                                               dev["Device"],
518                                               extra_params % dev))
519            else:
520                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
521    # sort before printing, so that the entries appear in PCI order
522    strings.sort()
523    print("\n".join(strings))  # print one per line
524
525def show_device_status(devices_type, device_name):
526    global dpdk_drivers
527    kernel_drv = []
528    dpdk_drv = []
529    no_drv = []
530
531    # split our list of network devices into the three categories above
532    for d in devices.keys():
533        if device_type_match(devices[d], devices_type):
534            if not has_driver(d):
535                no_drv.append(devices[d])
536                continue
537            if devices[d]["Driver_str"] in dpdk_drivers:
538                dpdk_drv.append(devices[d])
539            else:
540                kernel_drv.append(devices[d])
541
542    # print each category separately, so we can clearly see what's used by DPDK
543    display_devices("%s devices using DPDK-compatible driver" % device_name,
544                    dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
545    display_devices("%s devices using kernel driver" % device_name, kernel_drv,
546                    "if=%(Interface)s drv=%(Driver_str)s "
547                    "unused=%(Module_str)s %(Active)s")
548    display_devices("Other %s devices" % device_name, no_drv,
549                    "unused=%(Module_str)s")
550
551def show_status():
552    '''Function called when the script is passed the "--status" option.
553    Displays to the user what devices are bound to the igb_uio driver, the
554    kernel driver or to no driver'''
555
556    if status_dev == "net" or status_dev == "all":
557        show_device_status(network_devices, "Network")
558
559    if status_dev == "crypto" or status_dev == "all":
560        show_device_status(crypto_devices, "Crypto")
561
562    if status_dev == "event" or status_dev == "all":
563        show_device_status(eventdev_devices, "Eventdev")
564
565    if status_dev == "mempool" or status_dev == "all":
566        show_device_status(mempool_devices, "Mempool")
567
568def parse_args():
569    '''Parses the command-line arguments given by the user and takes the
570    appropriate action for each'''
571    global b_flag
572    global status_flag
573    global status_dev
574    global force_flag
575    global args
576    if len(sys.argv) <= 1:
577        usage()
578        sys.exit(0)
579
580    try:
581        opts, args = getopt.getopt(sys.argv[1:], "b:us",
582                                   ["help", "usage", "status", "status-dev=",
583                                    "force", "bind=", "unbind", ])
584    except getopt.GetoptError as error:
585        print(str(error))
586        print("Run '%s --usage' for further information" % sys.argv[0])
587        sys.exit(1)
588
589    for opt, arg in opts:
590        if opt == "--help" or opt == "--usage":
591            usage()
592            sys.exit(0)
593        if opt == "--status-dev":
594            status_flag = True
595            status_dev = arg
596        if opt == "--status" or opt == "-s":
597            status_flag = True
598            status_dev = "all"
599        if opt == "--force":
600            force_flag = True
601        if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
602            if b_flag is not None:
603                print("Error - Only one bind or unbind may be specified\n")
604                sys.exit(1)
605            if opt == "-u" or opt == "--unbind":
606                b_flag = "none"
607            else:
608                b_flag = arg
609
610
611def do_arg_actions():
612    '''do the actual action requested by the user'''
613    global b_flag
614    global status_flag
615    global force_flag
616    global args
617
618    if b_flag is None and not status_flag:
619        print("Error: No action specified for devices."
620              "Please give a -b or -u option")
621        print("Run '%s --usage' for further information" % sys.argv[0])
622        sys.exit(1)
623
624    if b_flag is not None and len(args) == 0:
625        print("Error: No devices specified.")
626        print("Run '%s --usage' for further information" % sys.argv[0])
627        sys.exit(1)
628
629    if b_flag == "none" or b_flag == "None":
630        unbind_all(args, force_flag)
631    elif b_flag is not None:
632        bind_all(args, b_flag, force_flag)
633    if status_flag:
634        if b_flag is not None:
635            clear_data()
636            # refresh if we have changed anything
637            get_device_details(network_devices)
638            get_device_details(crypto_devices)
639            get_device_details(eventdev_devices)
640            get_device_details(mempool_devices)
641        show_status()
642
643
644def main():
645    '''program main function'''
646    parse_args()
647    check_modules()
648    clear_data()
649    get_device_details(network_devices)
650    get_device_details(crypto_devices)
651    get_device_details(eventdev_devices)
652    get_device_details(mempool_devices)
653    do_arg_actions()
654
655if __name__ == "__main__":
656    main()
657