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