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