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