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