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