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