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