mbuf: fix reset on mbuf free
[dpdk.git] / usertools / dpdk-devbind.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2010-2014 Intel Corporation
4 #
5
6 import sys
7 import os
8 import subprocess
9 import argparse
10
11 from glob import glob
12 from os.path import exists, basename
13 from os.path import join as path_join
14
15 # The PCI base class for all devices
16 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
17                  'SVendor': None, 'SDevice': None}
18 acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
19                       'SVendor': None, 'SDevice': None}
20 ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
21                'SVendor': None, 'SDevice': None}
22 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
23                     'SVendor': None, 'SDevice': None}
24 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
25                          'SVendor': None, 'SDevice': None}
26 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
27               'SVendor': None, 'SDevice': None}
28 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
29               'SVendor': None, 'SDevice': None}
30 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
31               'SVendor': None, 'SDevice': None}
32 cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
33               'SVendor': None, 'SDevice': None}
34 cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
35               'SVendor': None, 'SDevice': None}
36 avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
37             'SVendor': None, 'SDevice': None}
38
39 octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
40                  'SVendor': None, 'SDevice': None}
41 octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
42                  'SVendor': None, 'SDevice': None}
43 octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
44                  'SVendor': None, 'SDevice': None}
45 octeontx2_ree = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f4',
46                  'SVendor': None, 'SDevice': None}
47
48 intel_ioat_bdw = {'Class': '08', 'Vendor': '8086',
49                   'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
50                   'SVendor': None, 'SDevice': None}
51 intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
52                   'SVendor': None, 'SDevice': None}
53 intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00',
54                   'SVendor': None, 'SDevice': None}
55 intel_idxd_spr = {'Class': '08', 'Vendor': '8086', 'Device': '0b25',
56                   'SVendor': None, 'SDevice': None}
57 intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
58                  'SVendor': None, 'SDevice': None}
59 intel_ntb_icx = {'Class': '06', 'Vendor': '8086', 'Device': '347e',
60                  'SVendor': None, 'SDevice': None}
61
62 network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
63 baseband_devices = [acceleration_class]
64 crypto_devices = [encryption_class, intel_processor_class]
65 eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
66 mempool_devices = [cavium_fpa, octeontx2_npa]
67 compress_devices = [cavium_zip]
68 regex_devices = [octeontx2_ree]
69 misc_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
75 devices = {}
76 # list of supported DPDK drivers
77 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
78 # list of currently loaded kernel modules
79 loaded_modules = None
80
81 # command-line arg flags
82 b_flag = None
83 status_flag = False
84 force_flag = False
85 args = []
86
87 # check if a specific kernel module is loaded
88 def 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
113 def 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
133 def 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
138 def 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
164 def clear_data():
165     '''This function clears any old data'''
166     global devices
167     devices = {}
168
169 def 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
250 def 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
268 def 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
289 def 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
314 def 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
421 def 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
441 def 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
490 def 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
513 def 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
555 def 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
585 def 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
597 def 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="""
610 Examples:
611 ---------
612
613 To display current device status:
614         %(prog)s --status
615
616 To display current network device status:
617         %(prog)s --status-dev net
618
619 To bind eth1 from the current driver and move to use vfio-pci
620         %(prog)s --bind=vfio-pci eth1
621
622 To unbind 0000:01:00.0 from using any driver
623         %(prog)s -u 0000:01:00.0
624
625 To 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="""
654 Override restriction on binding devices in use by Linux"
655 WARNING: 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="""
662 Device specified as PCI "domain:bus:slot.func" syntax or "bus:slot.func" syntax.
663 For 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
700 def 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
726 def 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
747 if __name__ == "__main__":
748     main()