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