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