xref: /dpdk/usertools/dpdk-devbind.py (revision ce7a737c8f02cd001f3f66f5c4e73ab7060ed588)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2010-2014 Intel Corporation
4#
5
6import sys
7import os
8import subprocess
9import argparse
10import platform
11
12from glob import glob
13from os.path import exists, basename
14from os.path import join as path_join
15
16# The PCI base class for all devices
17network_class = {'Class': '02', 'Vendor': None, 'Device': None,
18                 'SVendor': None, 'SDevice': None}
19acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
20                      'SVendor': None, 'SDevice': None}
21ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
22               'SVendor': None, 'SDevice': None}
23encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
24                    'SVendor': None, 'SDevice': None}
25intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
26                         'SVendor': None, 'SDevice': None}
27cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
28              'SVendor': None, 'SDevice': None}
29cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
30              'SVendor': None, 'SDevice': None}
31cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
32              'SVendor': None, 'SDevice': None}
33cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
34              'SVendor': None, 'SDevice': None}
35cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
36              'SVendor': None, 'SDevice': None}
37avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
38            'SVendor': None, 'SDevice': None}
39
40cnxk_bphy = {'Class': '08', 'Vendor': '177d', 'Device': 'a089',
41             'SVendor': None, 'SDevice': None}
42cnxk_bphy_cgx = {'Class': '08', 'Vendor': '177d', 'Device': 'a059,a060',
43                 'SVendor': None, 'SDevice': None}
44cnxk_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
45            'SVendor': None, 'SDevice': None}
46cnxk_inl_dev = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f0,a0f1',
47                'SVendor': None, 'SDevice': None}
48
49hisilicon_dma = {'Class': '08', 'Vendor': '19e5', 'Device': 'a122',
50                 'SVendor': None, 'SDevice': None}
51
52intel_dlb = {'Class': '0b', 'Vendor': '8086', 'Device': '270b,2710,2714',
53             'SVendor': None, 'SDevice': None}
54intel_ioat_bdw = {'Class': '08', 'Vendor': '8086',
55                  'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
56                  'SVendor': None, 'SDevice': None}
57intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
58                  'SVendor': None, 'SDevice': None}
59intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00',
60                  'SVendor': None, 'SDevice': None}
61intel_idxd_spr = {'Class': '08', 'Vendor': '8086', 'Device': '0b25',
62                  'SVendor': None, 'SDevice': None}
63intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
64                 'SVendor': None, 'SDevice': None}
65intel_ntb_icx = {'Class': '06', 'Vendor': '8086', 'Device': '347e',
66                 'SVendor': None, 'SDevice': None}
67
68cnxk_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
69                 'SVendor': None, 'SDevice': None}
70cnxk_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
71                 'SVendor': None, 'SDevice': None}
72cn9k_ree = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f4',
73                 'SVendor': None, 'SDevice': None}
74
75virtio_blk = {'Class': '01', 'Vendor': "1af4", 'Device': '1001,1042',
76                    'SVendor': None, 'SDevice': None}
77
78cnxk_ml = {'Class': '08', 'Vendor': '177d', 'Device': 'a092',
79            'SVendor': None, 'SDevice': None}
80
81network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
82baseband_devices = [acceleration_class]
83crypto_devices = [encryption_class, intel_processor_class]
84dma_devices = [cnxk_dma, hisilicon_dma,
85               intel_idxd_spr, intel_ioat_bdw, intel_ioat_icx, intel_ioat_skx]
86eventdev_devices = [cavium_sso, cavium_tim, intel_dlb, cnxk_sso]
87mempool_devices = [cavium_fpa, cnxk_npa]
88compress_devices = [cavium_zip]
89regex_devices = [cn9k_ree]
90ml_devices = [cnxk_ml]
91misc_devices = [cnxk_bphy, cnxk_bphy_cgx, cnxk_inl_dev,
92                intel_ntb_skx, intel_ntb_icx,
93                virtio_blk]
94
95# global dict ethernet devices present. Dictionary indexed by PCI address.
96# Each device within this is itself a dictionary of device properties
97devices = {}
98# list of supported DPDK drivers
99dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
100# list of currently loaded kernel modules
101loaded_modules = None
102
103# command-line arg flags
104b_flag = None
105status_flag = False
106force_flag = False
107args = []
108
109
110# check if a specific kernel module is loaded
111def module_is_loaded(module):
112    global loaded_modules
113
114    if module == 'vfio_pci':
115        module = 'vfio-pci'
116
117    if loaded_modules:
118        return module in loaded_modules
119
120    # Get list of sysfs modules (both built-in and dynamically loaded)
121    sysfs_path = '/sys/module/'
122
123    # Get the list of directories in sysfs_path
124    sysfs_mods = [m for m in os.listdir(sysfs_path)
125                  if os.path.isdir(os.path.join(sysfs_path, m))]
126
127    # special case for vfio_pci (module is named vfio-pci,
128    # but its .ko is named vfio_pci)
129    sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
130
131    loaded_modules = sysfs_mods
132
133    # add built-in modules as loaded
134    release = platform.uname().release
135    filename = os.path.join("/lib/modules/", release, "modules.builtin")
136    if os.path.exists(filename):
137        try:
138            with open(filename) as f:
139                loaded_modules += [os.path.splitext(os.path.basename(mod))[0] for mod in f]
140        except IOError:
141            print("Warning: cannot read list of built-in kernel modules")
142
143    return module in loaded_modules
144
145
146def check_modules():
147    '''Checks that igb_uio is loaded'''
148    global dpdk_drivers
149
150    # list of supported modules
151    mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
152
153    # first check if module is loaded
154    for mod in mods:
155        if module_is_loaded(mod["Name"]):
156            mod["Found"] = True
157
158    # check if we have at least one loaded module
159    if True not in [mod["Found"] for mod in mods] and b_flag is not None:
160        print("Warning: no supported DPDK kernel modules are loaded", file=sys.stderr)
161
162    # change DPDK driver list to only contain drivers that are loaded
163    dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
164
165
166def has_driver(dev_id):
167    '''return true if a device is assigned to a driver. False otherwise'''
168    return "Driver_str" in devices[dev_id]
169
170
171def get_pci_device_details(dev_id, probe_lspci):
172    '''This function gets additional details for a PCI device'''
173    device = {}
174
175    if probe_lspci:
176        extra_info = subprocess.check_output(["lspci", "-vmmks", dev_id]).splitlines()
177        # parse lspci details
178        for line in extra_info:
179            if not line:
180                continue
181            name, value = line.decode("utf8").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
197
198def clear_data():
199    '''This function clears any old data'''
200    global devices
201    devices = {}
202
203
204def get_device_details(devices_type):
205    '''This function populates the "devices" dictionary. The keys used are
206    the pci addresses (domain:bus:slot.func). The values are themselves
207    dictionaries - one for each NIC.'''
208    global devices
209    global dpdk_drivers
210
211    # first loop through and read details for all devices
212    # request machine readable format, with numeric IDs and String
213    dev = {}
214    dev_lines = subprocess.check_output(["lspci", "-Dvmmnnk"]).splitlines()
215    for dev_line in dev_lines:
216        if not dev_line:
217            if device_type_match(dev, devices_type):
218                # Replace "Driver" with "Driver_str" to have consistency of
219                # of dictionary key names
220                if "Driver" in dev.keys():
221                    dev["Driver_str"] = dev.pop("Driver")
222                if "Module" in dev.keys():
223                    dev["Module_str"] = dev.pop("Module")
224                # use dict to make copy of dev
225                devices[dev["Slot"]] = dict(dev)
226            # Clear previous device's data
227            dev = {}
228        else:
229            name, value = dev_line.decode("utf8").split("\t", 1)
230            value_list = value.rsplit(' ', 1)
231            if value_list:
232                # String stored in <name>_str
233                dev[name.rstrip(":") + '_str'] = value_list[0]
234            # Numeric IDs
235            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
236                .rstrip("]").lstrip("[")
237
238    if devices_type == network_devices:
239        # check what is the interface if any for an ssh connection if
240        # any to this host, so we can mark it later.
241        ssh_if = []
242        route = subprocess.check_output(["ip", "-o", "route"])
243        # filter out all lines for 169.254 routes
244        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
245                                 route.decode().splitlines()))
246        rt_info = route.split()
247        for i in range(len(rt_info) - 1):
248            if rt_info[i] == "dev":
249                ssh_if.append(rt_info[i + 1])
250
251    # based on the basic info, get extended text details
252    for d in devices.keys():
253        if not device_type_match(devices[d], devices_type):
254            continue
255
256        # get additional info and add it to existing data
257        devices[d] = devices[d].copy()
258        # No need to probe lspci
259        devices[d].update(get_pci_device_details(d, False).items())
260
261        if devices_type == network_devices:
262            for _if in ssh_if:
263                if _if in devices[d]["Interface"].split(","):
264                    devices[d]["Ssh_if"] = True
265                    devices[d]["Active"] = "*Active*"
266                    break
267
268        # add igb_uio to list of supporting modules if needed
269        if "Module_str" in devices[d]:
270            for driver in dpdk_drivers:
271                if driver not in devices[d]["Module_str"]:
272                    devices[d]["Module_str"] = \
273                        devices[d]["Module_str"] + ",%s" % driver
274        else:
275            devices[d]["Module_str"] = ",".join(dpdk_drivers)
276
277        # make sure the driver and module strings do not have any duplicates
278        if has_driver(d):
279            modules = devices[d]["Module_str"].split(",")
280            if devices[d]["Driver_str"] in modules:
281                modules.remove(devices[d]["Driver_str"])
282                devices[d]["Module_str"] = ",".join(modules)
283
284
285def device_type_match(dev, devices_type):
286    for i in range(len(devices_type)):
287        param_count = len(
288            [x for x in devices_type[i].values() if x is not None])
289        match_count = 0
290        if dev["Class"][0:2] == devices_type[i]["Class"]:
291            match_count = match_count + 1
292            for key in devices_type[i].keys():
293                if key != 'Class' and devices_type[i][key]:
294                    value_list = devices_type[i][key].split(',')
295                    for value in value_list:
296                        if value.strip(' ') == dev[key]:
297                            match_count = match_count + 1
298            # count must be the number of non None parameters to match
299            if match_count == param_count:
300                return True
301    return False
302
303
304def dev_id_from_dev_name(dev_name):
305    '''Take a device "name" - a string passed in by user to identify a NIC
306    device, and determine the device id - i.e. the domain:bus:slot.func - for
307    it, which can then be used to index into the devices array'''
308
309    # check if it's already a suitable index
310    if dev_name in devices:
311        return dev_name
312    # check if it's an index just missing the domain part
313    if "0000:" + dev_name in devices:
314        return "0000:" + dev_name
315
316    # check if it's an interface name, e.g. eth1
317    for d in devices.keys():
318        if dev_name in devices[d]["Interface"].split(","):
319            return devices[d]["Slot"]
320    # if nothing else matches - error
321    raise ValueError("Unknown device: %s. "
322                     "Please specify device in \"bus:slot.func\" format" % dev_name)
323
324
325def unbind_one(dev_id, force):
326    '''Unbind the device identified by "dev_id" from its current driver'''
327    dev = devices[dev_id]
328    if not has_driver(dev_id):
329        print("Notice: %s %s %s is not currently managed by any driver" %
330              (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr)
331        return
332
333    # prevent us disconnecting ourselves
334    if dev["Ssh_if"] and not force:
335        print("Warning: routing table indicates that interface %s is active. "
336              "Skipping unbind" % dev_id, file=sys.stderr)
337        return
338
339    # write to /sys to unbind
340    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
341    try:
342        f = open(filename, "a")
343    except OSError as err:
344        sys.exit("Error: unbind failed for %s - Cannot open %s: %s" %
345                 (dev_id, filename, err))
346    f.write(dev_id)
347    f.close()
348
349
350def bind_one(dev_id, driver, force):
351    '''Bind the device given by "dev_id" to the driver "driver". If the device
352    is already bound to a different driver, it will be unbound first'''
353    dev = devices[dev_id]
354    saved_driver = None  # used to rollback any unbind in case of failure
355
356    # prevent disconnection of our ssh session
357    if dev["Ssh_if"] and not force:
358        print("Warning: routing table indicates that interface %s is active. "
359              "Not modifying" % dev_id, file=sys.stderr)
360        return
361
362    # unbind any existing drivers we don't want
363    if has_driver(dev_id):
364        if dev["Driver_str"] == driver:
365            print("Notice: %s already bound to driver %s, skipping" %
366                  (dev_id, driver), file=sys.stderr)
367            return
368        saved_driver = dev["Driver_str"]
369        unbind_one(dev_id, force)
370        dev["Driver_str"] = ""  # clear driver string
371
372    # For kernels >= 3.15 driver_override can be used to specify the driver
373    # for a device rather than relying on the driver to provide a positive
374    # match of the device.  The existing process of looking up
375    # the vendor and device ID, adding them to the driver new_id,
376    # will erroneously bind other devices too which has the additional burden
377    # of unbinding those devices
378    if driver in dpdk_drivers:
379        filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
380        if exists(filename):
381            try:
382                f = open(filename, "w")
383            except OSError as err:
384                print("Error: bind failed for %s - Cannot open %s: %s"
385                      % (dev_id, filename, err), file=sys.stderr)
386                return
387            try:
388                f.write("%s" % driver)
389                f.close()
390            except OSError as err:
391                print("Error: bind failed for %s - Cannot write driver %s to "
392                      "PCI ID: %s" % (dev_id, driver, err), file=sys.stderr)
393                return
394        # For kernels < 3.15 use new_id to add PCI id's to the driver
395        else:
396            filename = "/sys/bus/pci/drivers/%s/new_id" % driver
397            try:
398                f = open(filename, "w")
399            except OSError as err:
400                print("Error: bind failed for %s - Cannot open %s: %s"
401                      % (dev_id, filename, err), file=sys.stderr)
402                return
403            try:
404                # Convert Device and Vendor Id to int to write to new_id
405                f.write("%04x %04x" % (int(dev["Vendor"], 16),
406                                       int(dev["Device"], 16)))
407                f.close()
408            except OSError as err:
409                print("Error: bind failed for %s - Cannot write new PCI ID to "
410                      "driver %s: %s" % (dev_id, driver, err), file=sys.stderr)
411                return
412
413    # do the bind by writing to /sys
414    filename = "/sys/bus/pci/drivers/%s/bind" % driver
415    try:
416        f = open(filename, "a")
417    except OSError as err:
418        print("Error: bind failed for %s - Cannot open %s: %s"
419              % (dev_id, filename, err), file=sys.stderr)
420        if saved_driver is not None:  # restore any previous driver
421            bind_one(dev_id, saved_driver, force)
422        return
423    try:
424        f.write(dev_id)
425        f.close()
426    except OSError as err:
427        # for some reason, closing dev_id after adding a new PCI ID to new_id
428        # results in IOError. however, if the device was successfully bound,
429        # we don't care for any errors and can safely ignore IOError
430        tmp = get_pci_device_details(dev_id, True)
431        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
432            return
433        print("Error: bind failed for %s - Cannot bind to driver %s: %s"
434              % (dev_id, driver, err), file=sys.stderr)
435        if saved_driver is not None:  # restore any previous driver
436            bind_one(dev_id, saved_driver, force)
437        return
438
439    # For kernels > 3.15 driver_override is used to bind a device to a driver.
440    # Before unbinding it, overwrite driver_override with empty string so that
441    # the device can be bound to any other driver
442    filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
443    if exists(filename):
444        try:
445            f = open(filename, "w")
446        except OSError as err:
447            sys.exit("Error: unbind failed for %s - Cannot open %s: %s"
448                     % (dev_id, filename, err))
449        try:
450            f.write("\00")
451            f.close()
452        except OSError as err:
453            sys.exit("Error: unbind failed for %s - Cannot write %s: %s"
454                     % (dev_id, filename, err))
455
456
457def unbind_all(dev_list, force=False):
458    """Unbind method, takes a list of device locations"""
459
460    if dev_list[0] == "dpdk":
461        for d in devices.keys():
462            if "Driver_str" in devices[d]:
463                if devices[d]["Driver_str"] in dpdk_drivers:
464                    unbind_one(devices[d]["Slot"], force)
465        return
466
467    try:
468        dev_list = map(dev_id_from_dev_name, dev_list)
469    except ValueError as ex:
470        print(ex)
471        sys.exit(1)
472
473    for d in dev_list:
474        unbind_one(d, force)
475
476
477def bind_all(dev_list, driver, force=False):
478    """Bind method, takes a list of device locations"""
479    global devices
480
481    # a common user error is to forget to specify the driver the devices need to
482    # be bound to. check if the driver is a valid device, and if it is, show
483    # a meaningful error.
484    try:
485        dev_id_from_dev_name(driver)
486        # if we've made it this far, this means that the "driver" was a valid
487        # device string, so it's probably not a valid driver name.
488        sys.exit("Error: Driver '%s' does not look like a valid driver. "
489                 "Did you forget to specify the driver to bind devices to?" % driver)
490    except ValueError:
491        # driver generated error - it's not a valid device ID, so all is well
492        pass
493
494    # check if we're attempting to bind to a driver that isn't loaded
495    if not module_is_loaded(driver.replace('-', '_')):
496        sys.exit("Error: Driver '%s' is not loaded." % driver)
497
498    try:
499        dev_list = map(dev_id_from_dev_name, dev_list)
500    except ValueError as ex:
501        sys.exit(ex)
502
503    for d in dev_list:
504        bind_one(d, driver, force)
505
506    # For kernels < 3.15 when binding devices to a generic driver
507    # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
508    # that are not bound to any other driver could be bound even if no one has
509    # asked them to. hence, we check the list of drivers again, and see if
510    # some of the previously-unbound devices were erroneously bound.
511    if not exists("/sys/bus/pci/devices/%s/driver_override" % d):
512        for d in devices.keys():
513            # skip devices that were already bound or that we know should be bound
514            if "Driver_str" in devices[d] or d in dev_list:
515                continue
516
517            # update information about this device
518            devices[d] = dict(devices[d].items()
519                              + get_pci_device_details(d, True).items())
520
521            # check if updated information indicates that the device was bound
522            if "Driver_str" in devices[d]:
523                unbind_one(d, force)
524
525
526def display_devices(title, dev_list, extra_params=None):
527    '''Displays to the user the details of a list of devices given in
528    "dev_list". The "extra_params" parameter, if given, should contain a string
529     with %()s fields in it for replacement by the named fields in each
530     device's dictionary.'''
531    strings = []  # this holds the strings to print. We sort before printing
532    print("\n%s" % title)
533    print("=" * len(title))
534    if not dev_list:
535        strings.append("<none>")
536    else:
537        for dev in dev_list:
538            if extra_params is not None:
539                strings.append("%s '%s %s' %s" % (dev["Slot"],
540                                                  dev["Device_str"],
541                                                  dev["Device"],
542                                                  extra_params % dev))
543            else:
544                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
545    # sort before printing, so that the entries appear in PCI order
546    strings.sort()
547    print("\n".join(strings))  # print one per line
548
549
550def show_device_status(devices_type, device_name, if_field=False):
551    global dpdk_drivers
552    kernel_drv = []
553    dpdk_drv = []
554    no_drv = []
555
556    # split our list of network devices into the three categories above
557    for d in devices.keys():
558        if device_type_match(devices[d], devices_type):
559            if not has_driver(d):
560                no_drv.append(devices[d])
561                continue
562            if devices[d]["Driver_str"] in dpdk_drivers:
563                dpdk_drv.append(devices[d])
564            else:
565                kernel_drv.append(devices[d])
566
567    n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
568
569    # don't bother displaying anything if there are no devices
570    if n_devs == 0:
571        msg = "No '%s' devices detected" % device_name
572        print("")
573        print(msg)
574        print("".join('=' * len(msg)))
575        return
576
577    # print each category separately, so we can clearly see what's used by DPDK
578    if dpdk_drv:
579        display_devices("%s devices using DPDK-compatible driver" % device_name,
580                        dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
581    if kernel_drv:
582        if_text = ""
583        if if_field:
584            if_text = "if=%(Interface)s "
585        display_devices("%s devices using kernel driver" % device_name, kernel_drv,
586                        if_text + "drv=%(Driver_str)s "
587                        "unused=%(Module_str)s %(Active)s")
588    if no_drv:
589        display_devices("Other %s devices" % device_name, no_drv,
590                        "unused=%(Module_str)s")
591
592
593def show_status():
594    '''Function called when the script is passed the "--status" option.
595    Displays to the user what devices are bound to the igb_uio driver, the
596    kernel driver or to no driver'''
597
598    if status_dev in ["net", "all"]:
599        show_device_status(network_devices, "Network", if_field=True)
600
601    if status_dev in ["baseband", "all"]:
602        show_device_status(baseband_devices, "Baseband")
603
604    if status_dev in ["crypto", "all"]:
605        show_device_status(crypto_devices, "Crypto")
606
607    if status_dev in ["dma", "all"]:
608        show_device_status(dma_devices, "DMA")
609
610    if status_dev in ["event", "all"]:
611        show_device_status(eventdev_devices, "Eventdev")
612
613    if status_dev in ["mempool", "all"]:
614        show_device_status(mempool_devices, "Mempool")
615
616    if status_dev in ["compress", "all"]:
617        show_device_status(compress_devices, "Compress")
618
619    if status_dev in ["misc", "all"]:
620        show_device_status(misc_devices, "Misc (rawdev)")
621
622    if status_dev in ["regex", "all"]:
623        show_device_status(regex_devices, "Regex")
624
625    if status_dev in ["ml", "all"]:
626        show_device_status(ml_devices, "ML")
627
628
629def pci_glob(arg):
630    '''Returns a list containing either:
631    * List of PCI B:D:F matching arg, using shell wildcards e.g. 80:04.*
632    * Only the passed arg if matching list is empty'''
633    sysfs_path = "/sys/bus/pci/devices"
634    for _glob in [arg, '0000:' + arg]:
635        paths = [basename(path) for path in glob(path_join(sysfs_path, _glob))]
636        if paths:
637            return paths
638    return [arg]
639
640
641def parse_args():
642    '''Parses the command-line arguments given by the user and takes the
643    appropriate action for each'''
644    global b_flag
645    global status_flag
646    global status_dev
647    global force_flag
648    global args
649
650    parser = argparse.ArgumentParser(
651        description='Utility to bind and unbind devices from Linux kernel',
652        formatter_class=argparse.RawDescriptionHelpFormatter,
653        epilog="""
654Examples:
655---------
656
657To display current device status:
658        %(prog)s --status
659
660To display current network device status:
661        %(prog)s --status-dev net
662
663To bind eth1 from the current driver and move to use vfio-pci
664        %(prog)s --bind=vfio-pci eth1
665
666To unbind 0000:01:00.0 from using any driver
667        %(prog)s -u 0000:01:00.0
668
669To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
670        %(prog)s -b ixgbe 02:00.0 02:00.1
671""")
672
673    parser.add_argument(
674        '-s',
675        '--status',
676        action='store_true',
677        help="Print the current status of all known devices.")
678    parser.add_argument(
679        '--status-dev',
680        help="Print the status of given device group.",
681        choices=['baseband', 'compress', 'crypto', 'dma', 'event',
682                 'mempool', 'misc', 'net', 'regex', 'ml'])
683    bind_group = parser.add_mutually_exclusive_group()
684    bind_group.add_argument(
685        '-b',
686        '--bind',
687        metavar='DRIVER',
688        help="Select the driver to use or \"none\" to unbind the device")
689    bind_group.add_argument(
690        '-u',
691        '--unbind',
692        action='store_true',
693        help="Unbind a device (equivalent to \"-b none\")")
694    parser.add_argument(
695        '--force',
696        action='store_true',
697        help="""
698Override restriction on binding devices in use by Linux"
699WARNING: This can lead to loss of network connection and should be used with caution.
700""")
701    parser.add_argument(
702        'devices',
703        metavar='DEVICE',
704        nargs='*',
705        help="""
706Device specified as PCI "domain:bus:slot.func" syntax or "bus:slot.func" syntax.
707For devices bound to Linux kernel drivers, they may be referred to by interface name.
708""")
709
710    opt = parser.parse_args()
711
712    if opt.status_dev:
713        status_flag = True
714        status_dev = opt.status_dev
715    if opt.status:
716        status_flag = True
717        status_dev = "all"
718    if opt.force:
719        force_flag = True
720    if opt.bind:
721        b_flag = opt.bind
722    elif opt.unbind:
723        b_flag = "none"
724    args = opt.devices
725
726    if not b_flag and not status_flag:
727        print("Error: No action specified for devices. "
728              "Please give a --bind, --ubind or --status option",
729              file=sys.stderr)
730        parser.print_usage()
731        sys.exit(1)
732
733    if b_flag and not args:
734        print("Error: No devices specified.", file=sys.stderr)
735        parser.print_usage()
736        sys.exit(1)
737
738    # resolve any PCI globs in the args
739    new_args = []
740    for arg in args:
741        new_args.extend(pci_glob(arg))
742    args = new_args
743
744
745def do_arg_actions():
746    '''do the actual action requested by the user'''
747    global b_flag
748    global status_flag
749    global force_flag
750    global args
751
752    if b_flag in ["none", "None"]:
753        unbind_all(args, force_flag)
754    elif b_flag is not None:
755        bind_all(args, b_flag, force_flag)
756    if status_flag:
757        if b_flag is not None:
758            clear_data()
759            # refresh if we have changed anything
760            get_device_details(network_devices)
761            get_device_details(baseband_devices)
762            get_device_details(crypto_devices)
763            get_device_details(dma_devices)
764            get_device_details(eventdev_devices)
765            get_device_details(mempool_devices)
766            get_device_details(compress_devices)
767            get_device_details(regex_devices)
768            get_device_details(ml_devices)
769            get_device_details(misc_devices)
770        show_status()
771
772
773def main():
774    '''program main function'''
775    # check if lspci is installed, suppress any output
776    with open(os.devnull, 'w') as devnull:
777        ret = subprocess.call(['which', 'lspci'],
778                              stdout=devnull, stderr=devnull)
779        if ret != 0:
780            sys.exit("'lspci' not found - please install 'pciutils'")
781    parse_args()
782    check_modules()
783    clear_data()
784    get_device_details(network_devices)
785    get_device_details(baseband_devices)
786    get_device_details(crypto_devices)
787    get_device_details(dma_devices)
788    get_device_details(eventdev_devices)
789    get_device_details(mempool_devices)
790    get_device_details(compress_devices)
791    get_device_details(regex_devices)
792    get_device_details(ml_devices)
793    get_device_details(misc_devices)
794    do_arg_actions()
795
796
797if __name__ == "__main__":
798    main()
799