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