New upstream version 18.02
[deb_dpdk.git] / usertools / dpdk-devbind.py
1 #! /usr/bin/env python
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 os.path import exists, abspath, dirname, basename
11
12 # The PCI base class for all devices
13 network_class = {'Class': '02', 'Vendor': None, 'Device': None,
14                     'SVendor': None, 'SDevice': None}
15 encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
16                    'SVendor': None, 'SDevice': None}
17 intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
18                    'SVendor': None, 'SDevice': None}
19 cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
20               'SVendor': None, 'SDevice': None}
21 cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
22               'SVendor': None, 'SDevice': None}
23 cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
24               'SVendor': None, 'SDevice': None}
25
26 network_devices = [network_class, cavium_pkx]
27 crypto_devices = [encryption_class, intel_processor_class]
28 eventdev_devices = [cavium_sso]
29 mempool_devices = [cavium_fpa]
30
31 # global dict ethernet devices present. Dictionary indexed by PCI address.
32 # Each device within this is itself a dictionary of device properties
33 devices = {}
34 # list of supported DPDK drivers
35 dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
36
37 # command-line arg flags
38 b_flag = None
39 status_flag = False
40 force_flag = False
41 args = []
42
43
44 def usage():
45     '''Print usage information for the program'''
46     argv0 = basename(sys.argv[0])
47     print("""
48 Usage:
49 ------
50
51      %(argv0)s [options] DEVICE1 DEVICE2 ....
52
53 where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
54 or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
55 also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
56
57 Options:
58     --help, --usage:
59         Display usage information and quit
60
61     -s, --status:
62         Print the current status of all known network, crypto, event
63         and mempool devices.
64         For each device, it displays the PCI domain, bus, slot and function,
65         along with a text description of the device. Depending upon whether the
66         device is being used by a kernel driver, the igb_uio driver, or no
67         driver, other relevant information will be displayed:
68         * the Linux interface name e.g. if=eth0
69         * the driver being used e.g. drv=igb_uio
70         * any suitable drivers not currently using that device
71             e.g. unused=igb_uio
72         NOTE: if this flag is passed along with a bind/unbind option, the
73         status display will always occur after the other operations have taken
74         place.
75
76     --status-dev:
77         Print the status of given device group. Supported device groups are:
78         "net", "crypto", "event" and "mempool"
79
80     -b driver, --bind=driver:
81         Select the driver to use or \"none\" to unbind the device
82
83     -u, --unbind:
84         Unbind a device (Equivalent to \"-b none\")
85
86     --force:
87         By default, network devices which are used by Linux - as indicated by
88         having routes in the routing table - cannot be modified. Using the
89         --force flag overrides this behavior, allowing active links to be
90         forcibly unbound.
91         WARNING: This can lead to loss of network connection and should be used
92         with caution.
93
94 Examples:
95 ---------
96
97 To display current device status:
98         %(argv0)s --status
99
100 To display current network device status:
101         %(argv0)s --status-dev net
102
103 To bind eth1 from the current driver and move to use igb_uio
104         %(argv0)s --bind=igb_uio eth1
105
106 To unbind 0000:01:00.0 from using any driver
107         %(argv0)s -u 0000:01:00.0
108
109 To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
110         %(argv0)s -b ixgbe 02:00.0 02:00.1
111
112     """ % locals())  # replace items from local variables
113
114
115 # This is roughly compatible with check_output function in subprocess module
116 # which is only available in python 2.7.
117 def check_output(args, stderr=None):
118     '''Run a command and capture its output'''
119     return subprocess.Popen(args, stdout=subprocess.PIPE,
120                             stderr=stderr).communicate()[0]
121
122
123 def check_modules():
124     '''Checks that igb_uio is loaded'''
125     global dpdk_drivers
126
127     # list of supported modules
128     mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
129
130     # first check if module is loaded
131     try:
132         # Get list of sysfs modules (both built-in and dynamically loaded)
133         sysfs_path = '/sys/module/'
134
135         # Get the list of directories in sysfs_path
136         sysfs_mods = [os.path.join(sysfs_path, o) for o
137                       in os.listdir(sysfs_path)
138                       if os.path.isdir(os.path.join(sysfs_path, o))]
139
140         # Extract the last element of '/sys/module/abc' in the array
141         sysfs_mods = [a.split('/')[-1] for a in sysfs_mods]
142
143         # special case for vfio_pci (module is named vfio-pci,
144         # but its .ko is named vfio_pci)
145         sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
146
147         for mod in mods:
148             if mod["Name"] in sysfs_mods:
149                 mod["Found"] = True
150     except:
151         pass
152
153     # check if we have at least one loaded module
154     if True not in [mod["Found"] for mod in mods] and b_flag is not None:
155         if b_flag in dpdk_drivers:
156             print("Error - no supported modules(DPDK driver) are loaded")
157             sys.exit(1)
158         else:
159             print("Warning - no supported modules(DPDK driver) are loaded")
160
161     # change DPDK driver list to only contain drivers that are loaded
162     dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
163
164
165 def has_driver(dev_id):
166     '''return true if a device is assigned to a driver. False otherwise'''
167     return "Driver_str" in devices[dev_id]
168
169
170 def get_pci_device_details(dev_id, probe_lspci):
171     '''This function gets additional details for a PCI device'''
172     device = {}
173
174     if probe_lspci:
175         extra_info = check_output(["lspci", "-vmmks", dev_id]).splitlines()
176
177         # parse lspci details
178         for line in extra_info:
179             if len(line) == 0:
180                 continue
181             name, value = line.decode().split("\t", 1)
182             name = name.strip(":") + "_str"
183             device[name] = value
184     # check for a unix interface name
185     device["Interface"] = ""
186     for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
187         if "net" in dirs:
188             device["Interface"] = \
189                 ",".join(os.listdir(os.path.join(base, "net")))
190             break
191     # check if a port is used for ssh connection
192     device["Ssh_if"] = False
193     device["Active"] = ""
194
195     return device
196
197 def clear_data():
198     '''This function clears any old data'''
199     devices = {}
200
201 def get_device_details(devices_type):
202     '''This function populates the "devices" dictionary. The keys used are
203     the pci addresses (domain:bus:slot.func). The values are themselves
204     dictionaries - one for each NIC.'''
205     global devices
206     global dpdk_drivers
207
208     # first loop through and read details for all devices
209     # request machine readable format, with numeric IDs and String
210     dev = {}
211     dev_lines = check_output(["lspci", "-Dvmmnnk"]).splitlines()
212     for dev_line in dev_lines:
213         if len(dev_line) == 0:
214             if device_type_match(dev, devices_type):
215                 # Replace "Driver" with "Driver_str" to have consistency of
216                 # of dictionary key names
217                 if "Driver" in dev.keys():
218                     dev["Driver_str"] = dev.pop("Driver")
219                 if "Module" in dev.keys():
220                     dev["Module_str"] = dev.pop("Module")
221                 # use dict to make copy of dev
222                 devices[dev["Slot"]] = dict(dev)
223             # Clear previous device's data
224             dev = {}
225         else:
226             name, value = dev_line.decode().split("\t", 1)
227             value_list = value.rsplit(' ', 1)
228             if len(value_list) > 1:
229                 # String stored in <name>_str
230                 dev[name.rstrip(":") + '_str'] = value_list[0]
231             # Numeric IDs
232             dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
233                 .rstrip("]").lstrip("[")
234
235     if devices_type == network_devices:
236         # check what is the interface if any for an ssh connection if
237         # any to this host, so we can mark it later.
238         ssh_if = []
239         route = check_output(["ip", "-o", "route"])
240         # filter out all lines for 169.254 routes
241         route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
242                              route.decode().splitlines()))
243         rt_info = route.split()
244         for i in range(len(rt_info) - 1):
245             if rt_info[i] == "dev":
246                 ssh_if.append(rt_info[i+1])
247
248     # based on the basic info, get extended text details
249     for d in devices.keys():
250         if not device_type_match(devices[d], devices_type):
251             continue
252
253         # get additional info and add it to existing data
254         devices[d] = devices[d].copy()
255         # No need to probe lspci
256         devices[d].update(get_pci_device_details(d, False).items())
257
258         if devices_type == network_devices:
259             for _if in ssh_if:
260                 if _if in devices[d]["Interface"].split(","):
261                     devices[d]["Ssh_if"] = True
262                     devices[d]["Active"] = "*Active*"
263                     break
264
265         # add igb_uio to list of supporting modules if needed
266         if "Module_str" in devices[d]:
267             for driver in dpdk_drivers:
268                 if driver not in devices[d]["Module_str"]:
269                     devices[d]["Module_str"] = \
270                         devices[d]["Module_str"] + ",%s" % driver
271         else:
272             devices[d]["Module_str"] = ",".join(dpdk_drivers)
273
274         # make sure the driver and module strings do not have any duplicates
275         if has_driver(d):
276             modules = devices[d]["Module_str"].split(",")
277             if devices[d]["Driver_str"] in modules:
278                 modules.remove(devices[d]["Driver_str"])
279                 devices[d]["Module_str"] = ",".join(modules)
280
281
282 def device_type_match(dev, devices_type):
283     for i in range(len(devices_type)):
284         param_count = len(
285             [x for x in devices_type[i].values() if x is not None])
286         match_count = 0
287         if dev["Class"][0:2] == devices_type[i]["Class"]:
288             match_count = match_count + 1
289             for key in devices_type[i].keys():
290                 if key != 'Class' and devices_type[i][key]:
291                     value_list = devices_type[i][key].split(',')
292                     for value in value_list:
293                         if value.strip(' ') == dev[key]:
294                             match_count = match_count + 1
295             # count must be the number of non None parameters to match
296             if match_count == param_count:
297                 return True
298     return False
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     elif "0000:" + dev_name in devices:
310         return "0000:" + dev_name
311     else:
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     print("Unknown device: %s. "
318           "Please specify device in \"bus:slot.func\" format" % dev_name)
319     sys.exit(1)
320
321
322 def unbind_one(dev_id, force):
323     '''Unbind the device identified by "dev_id" from its current driver'''
324     dev = devices[dev_id]
325     if not has_driver(dev_id):
326         print("%s %s %s is not currently managed by any driver\n" %
327               (dev["Slot"], dev["Device_str"], dev["Interface"]))
328         return
329
330     # prevent us disconnecting ourselves
331     if dev["Ssh_if"] and not force:
332         print("Routing table indicates that interface %s is active. "
333               "Skipping unbind" % (dev_id))
334         return
335
336     # write to /sys to unbind
337     filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
338     try:
339         f = open(filename, "a")
340     except:
341         print("Error: unbind failed for %s - Cannot open %s"
342               % (dev_id, filename))
343         sys.exit(1)
344     f.write(dev_id)
345     f.close()
346
347
348 def bind_one(dev_id, driver, force):
349     '''Bind the device given by "dev_id" to the driver "driver". If the device
350     is already bound to a different driver, it will be unbound first'''
351     dev = devices[dev_id]
352     saved_driver = None  # used to rollback any unbind in case of failure
353
354     # prevent disconnection of our ssh session
355     if dev["Ssh_if"] and not force:
356         print("Routing table indicates that interface %s is active. "
357               "Not modifying" % (dev_id))
358         return
359
360     # unbind any existing drivers we don't want
361     if has_driver(dev_id):
362         if dev["Driver_str"] == driver:
363             print("%s already bound to driver %s, skipping\n"
364                   % (dev_id, driver))
365             return
366         else:
367             saved_driver = dev["Driver_str"]
368             unbind_one(dev_id, force)
369             dev["Driver_str"] = ""  # clear driver string
370
371     # For kernels >= 3.15 driver_override can be used to specify the driver
372     # for a device rather than relying on the driver to provide a positive
373     # match of the device.  The existing process of looking up
374     # the vendor and device ID, adding them to the driver new_id,
375     # will erroneously bind other devices too which has the additional burden
376     # of unbinding those devices
377     if driver in dpdk_drivers:
378         filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
379         if os.path.exists(filename):
380             try:
381                 f = open(filename, "w")
382             except:
383                 print("Error: bind failed for %s - Cannot open %s"
384                       % (dev_id, filename))
385                 return
386             try:
387                 f.write("%s" % driver)
388                 f.close()
389             except:
390                 print("Error: bind failed for %s - Cannot write driver %s to "
391                       "PCI ID " % (dev_id, driver))
392                 return
393         # For kernels < 3.15 use new_id to add PCI id's to the driver
394         else:
395             filename = "/sys/bus/pci/drivers/%s/new_id" % driver
396             try:
397                 f = open(filename, "w")
398             except:
399                 print("Error: bind failed for %s - Cannot open %s"
400                       % (dev_id, filename))
401                 return
402             try:
403                 # Convert Device and Vendor Id to int to write to new_id
404                 f.write("%04x %04x" % (int(dev["Vendor"],16),
405                         int(dev["Device"], 16)))
406                 f.close()
407             except:
408                 print("Error: bind failed for %s - Cannot write new PCI ID to "
409                       "driver %s" % (dev_id, driver))
410                 return
411
412     # do the bind by writing to /sys
413     filename = "/sys/bus/pci/drivers/%s/bind" % driver
414     try:
415         f = open(filename, "a")
416     except:
417         print("Error: bind failed for %s - Cannot open %s"
418               % (dev_id, filename))
419         if saved_driver is not None:  # restore any previous driver
420             bind_one(dev_id, saved_driver, force)
421         return
422     try:
423         f.write(dev_id)
424         f.close()
425     except:
426         # for some reason, closing dev_id after adding a new PCI ID to new_id
427         # results in IOError. however, if the device was successfully bound,
428         # we don't care for any errors and can safely ignore IOError
429         tmp = get_pci_device_details(dev_id, True)
430         if "Driver_str" in tmp and tmp["Driver_str"] == driver:
431             return
432         print("Error: bind failed for %s - Cannot bind to driver %s"
433               % (dev_id, driver))
434         if saved_driver is not None:  # restore any previous driver
435             bind_one(dev_id, saved_driver, force)
436         return
437
438     # For kernels > 3.15 driver_override is used to bind a device to a driver.
439     # Before unbinding it, overwrite driver_override with empty string so that
440     # the device can be bound to any other driver
441     filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
442     if os.path.exists(filename):
443         try:
444             f = open(filename, "w")
445         except:
446             print("Error: unbind failed for %s - Cannot open %s"
447                   % (dev_id, filename))
448             sys.exit(1)
449         try:
450             f.write("\00")
451             f.close()
452         except:
453             print("Error: unbind failed for %s - Cannot open %s"
454                   % (dev_id, filename))
455             sys.exit(1)
456
457
458 def unbind_all(dev_list, force=False):
459     """Unbind method, takes a list of device locations"""
460
461     if dev_list[0] == "dpdk":
462         for d in devices.keys():
463             if "Driver_str" in devices[d]:
464                 if devices[d]["Driver_str"] in dpdk_drivers:
465                     unbind_one(devices[d]["Slot"], force)
466         return
467
468     dev_list = map(dev_id_from_dev_name, dev_list)
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     dev_list = map(dev_id_from_dev_name, dev_list)
478
479     for d in dev_list:
480         bind_one(d, driver, force)
481
482     # For kernels < 3.15 when binding devices to a generic driver
483     # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
484     # that are not bound to any other driver could be bound even if no one has
485     # asked them to. hence, we check the list of drivers again, and see if
486     # some of the previously-unbound devices were erroneously bound.
487     if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
488         for d in devices.keys():
489             # skip devices that were already bound or that we know should be bound
490             if "Driver_str" in devices[d] or d in dev_list:
491                 continue
492
493             # update information about this device
494             devices[d] = dict(devices[d].items() +
495                               get_pci_device_details(d, True).items())
496
497             # check if updated information indicates that the device was bound
498             if "Driver_str" in devices[d]:
499                 unbind_one(d, force)
500
501
502 def display_devices(title, dev_list, extra_params=None):
503     '''Displays to the user the details of a list of devices given in
504     "dev_list". The "extra_params" parameter, if given, should contain a string
505      with %()s fields in it for replacement by the named fields in each
506      device's dictionary.'''
507     strings = []  # this holds the strings to print. We sort before printing
508     print("\n%s" % title)
509     print("="*len(title))
510     if len(dev_list) == 0:
511         strings.append("<none>")
512     else:
513         for dev in dev_list:
514             if extra_params is not None:
515                 strings.append("%s '%s %s' %s" % (dev["Slot"],
516                                                dev["Device_str"],
517                                                dev["Device"],
518                                                extra_params % dev))
519             else:
520                 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
521     # sort before printing, so that the entries appear in PCI order
522     strings.sort()
523     print("\n".join(strings))  # print one per line
524
525 def show_device_status(devices_type, device_name):
526     global dpdk_drivers
527     kernel_drv = []
528     dpdk_drv = []
529     no_drv = []
530
531     # split our list of network devices into the three categories above
532     for d in devices.keys():
533         if device_type_match(devices[d], devices_type):
534             if not has_driver(d):
535                 no_drv.append(devices[d])
536                 continue
537             if devices[d]["Driver_str"] in dpdk_drivers:
538                 dpdk_drv.append(devices[d])
539             else:
540                 kernel_drv.append(devices[d])
541
542     # print each category separately, so we can clearly see what's used by DPDK
543     display_devices("%s devices using DPDK-compatible driver" % device_name,
544                     dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
545     display_devices("%s devices using kernel driver" % device_name, kernel_drv,
546                     "if=%(Interface)s drv=%(Driver_str)s "
547                     "unused=%(Module_str)s %(Active)s")
548     display_devices("Other %s devices" % device_name, no_drv,
549                     "unused=%(Module_str)s")
550
551 def show_status():
552     '''Function called when the script is passed the "--status" option.
553     Displays to the user what devices are bound to the igb_uio driver, the
554     kernel driver or to no driver'''
555
556     if status_dev == "net" or status_dev == "all":
557         show_device_status(network_devices, "Network")
558
559     if status_dev == "crypto" or status_dev == "all":
560         show_device_status(crypto_devices, "Crypto")
561
562     if status_dev == "event" or status_dev == "all":
563         show_device_status(eventdev_devices, "Eventdev")
564
565     if status_dev == "mempool" or status_dev == "all":
566         show_device_status(mempool_devices, "Mempool")
567
568 def parse_args():
569     '''Parses the command-line arguments given by the user and takes the
570     appropriate action for each'''
571     global b_flag
572     global status_flag
573     global status_dev
574     global force_flag
575     global args
576     if len(sys.argv) <= 1:
577         usage()
578         sys.exit(0)
579
580     try:
581         opts, args = getopt.getopt(sys.argv[1:], "b:us",
582                                    ["help", "usage", "status", "status-dev=",
583                                     "force", "bind=", "unbind", ])
584     except getopt.GetoptError as error:
585         print(str(error))
586         print("Run '%s --usage' for further information" % sys.argv[0])
587         sys.exit(1)
588
589     for opt, arg in opts:
590         if opt == "--help" or opt == "--usage":
591             usage()
592             sys.exit(0)
593         if opt == "--status-dev":
594             status_flag = True
595             status_dev = arg
596         if opt == "--status" or opt == "-s":
597             status_flag = True
598             status_dev = "all"
599         if opt == "--force":
600             force_flag = True
601         if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
602             if b_flag is not None:
603                 print("Error - Only one bind or unbind may be specified\n")
604                 sys.exit(1)
605             if opt == "-u" or opt == "--unbind":
606                 b_flag = "none"
607             else:
608                 b_flag = arg
609
610
611 def do_arg_actions():
612     '''do the actual action requested by the user'''
613     global b_flag
614     global status_flag
615     global force_flag
616     global args
617
618     if b_flag is None and not status_flag:
619         print("Error: No action specified for devices."
620               "Please give a -b or -u option")
621         print("Run '%s --usage' for further information" % sys.argv[0])
622         sys.exit(1)
623
624     if b_flag is not None and len(args) == 0:
625         print("Error: No devices specified.")
626         print("Run '%s --usage' for further information" % sys.argv[0])
627         sys.exit(1)
628
629     if b_flag == "none" or b_flag == "None":
630         unbind_all(args, force_flag)
631     elif b_flag is not None:
632         bind_all(args, b_flag, force_flag)
633     if status_flag:
634         if b_flag is not None:
635             clear_data()
636             # refresh if we have changed anything
637             get_device_details(network_devices)
638             get_device_details(crypto_devices)
639             get_device_details(eventdev_devices)
640             get_device_details(mempool_devices)
641         show_status()
642
643
644 def main():
645     '''program main function'''
646     parse_args()
647     check_modules()
648     clear_data()
649     get_device_details(network_devices)
650     get_device_details(crypto_devices)
651     get_device_details(eventdev_devices)
652     get_device_details(mempool_devices)
653     do_arg_actions()
654
655 if __name__ == "__main__":
656     main()