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