955e984932cccd340f73d2d3cee819f723b2559c
[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                 # use dict to make copy of dev
220                 devices[dev["Slot"]] = dict(dev)
221             # Clear previous device's data
222             dev = {}
223         else:
224             name, value = dev_line.decode().split("\t", 1)
225             value_list = value.rsplit(' ', 1)
226             if len(value_list) > 1:
227                 # String stored in <name>_str
228                 dev[name.rstrip(":") + '_str'] = value_list[0]
229             # Numeric IDs
230             dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
231                 .rstrip("]").lstrip("[")
232
233     if devices_type == network_devices:
234         # check what is the interface if any for an ssh connection if
235         # any to this host, so we can mark it later.
236         ssh_if = []
237         route = check_output(["ip", "-o", "route"])
238         # filter out all lines for 169.254 routes
239         route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
240                              route.decode().splitlines()))
241         rt_info = route.split()
242         for i in range(len(rt_info) - 1):
243             if rt_info[i] == "dev":
244                 ssh_if.append(rt_info[i+1])
245
246     # based on the basic info, get extended text details
247     for d in devices.keys():
248         if not device_type_match(devices[d], devices_type):
249             continue
250
251         # get additional info and add it to existing data
252         devices[d] = devices[d].copy()
253         # No need to probe lspci
254         devices[d].update(get_pci_device_details(d, False).items())
255
256         if devices_type == network_devices:
257             for _if in ssh_if:
258                 if _if in devices[d]["Interface"].split(","):
259                     devices[d]["Ssh_if"] = True
260                     devices[d]["Active"] = "*Active*"
261                     break
262
263         # add igb_uio to list of supporting modules if needed
264         if "Module_str" in devices[d]:
265             for driver in dpdk_drivers:
266                 if driver not in devices[d]["Module_str"]:
267                     devices[d]["Module_str"] = \
268                         devices[d]["Module_str"] + ",%s" % driver
269         else:
270             devices[d]["Module_str"] = ",".join(dpdk_drivers)
271
272         # make sure the driver and module strings do not have any duplicates
273         if has_driver(d):
274             modules = devices[d]["Module_str"].split(",")
275             if devices[d]["Driver_str"] in modules:
276                 modules.remove(devices[d]["Driver_str"])
277                 devices[d]["Module_str"] = ",".join(modules)
278
279
280 def device_type_match(dev, devices_type):
281     for i in range(len(devices_type)):
282         param_count = len(
283             [x for x in devices_type[i].values() if x is not None])
284         match_count = 0
285         if dev["Class"][0:2] == devices_type[i]["Class"]:
286             match_count = match_count + 1
287             for key in devices_type[i].keys():
288                 if key != 'Class' and devices_type[i][key]:
289                     value_list = devices_type[i][key].split(',')
290                     for value in value_list:
291                         if value.strip(' ') == dev[key]:
292                             match_count = match_count + 1
293             # count must be the number of non None parameters to match
294             if match_count == param_count:
295                 return True
296     return False
297
298 def dev_id_from_dev_name(dev_name):
299     '''Take a device "name" - a string passed in by user to identify a NIC
300     device, and determine the device id - i.e. the domain:bus:slot.func - for
301     it, which can then be used to index into the devices array'''
302
303     # check if it's already a suitable index
304     if dev_name in devices:
305         return dev_name
306     # check if it's an index just missing the domain part
307     elif "0000:" + dev_name in devices:
308         return "0000:" + dev_name
309     else:
310         # check if it's an interface name, e.g. eth1
311         for d in devices.keys():
312             if dev_name in devices[d]["Interface"].split(","):
313                 return devices[d]["Slot"]
314     # if nothing else matches - error
315     print("Unknown device: %s. "
316           "Please specify device in \"bus:slot.func\" format" % dev_name)
317     sys.exit(1)
318
319
320 def unbind_one(dev_id, force):
321     '''Unbind the device identified by "dev_id" from its current driver'''
322     dev = devices[dev_id]
323     if not has_driver(dev_id):
324         print("%s %s %s is not currently managed by any driver\n" %
325               (dev["Slot"], dev["Device_str"], dev["Interface"]))
326         return
327
328     # prevent us disconnecting ourselves
329     if dev["Ssh_if"] and not force:
330         print("Routing table indicates that interface %s is active. "
331               "Skipping unbind" % (dev_id))
332         return
333
334     # write to /sys to unbind
335     filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
336     try:
337         f = open(filename, "a")
338     except:
339         print("Error: unbind failed for %s - Cannot open %s"
340               % (dev_id, filename))
341         sys.exit(1)
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("Routing table indicates that interface %s is active. "
355               "Not modifying" % (dev_id))
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("%s already bound to driver %s, skipping\n"
362                   % (dev_id, driver))
363             return
364         else:
365             saved_driver = dev["Driver_str"]
366             unbind_one(dev_id, force)
367             dev["Driver_str"] = ""  # clear driver string
368
369     # For kernels >= 3.15 driver_override can be used to specify the driver
370     # for a device rather than relying on the driver to provide a positive
371     # match of the device.  The existing process of looking up
372     # the vendor and device ID, adding them to the driver new_id,
373     # will erroneously bind other devices too which has the additional burden
374     # of unbinding those devices
375     if driver in dpdk_drivers:
376         filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
377         if os.path.exists(filename):
378             try:
379                 f = open(filename, "w")
380             except:
381                 print("Error: bind failed for %s - Cannot open %s"
382                       % (dev_id, filename))
383                 return
384             try:
385                 f.write("%s" % driver)
386                 f.close()
387             except:
388                 print("Error: bind failed for %s - Cannot write driver %s to "
389                       "PCI ID " % (dev_id, driver))
390                 return
391         # For kernels < 3.15 use new_id to add PCI id's to the driver
392         else:
393             filename = "/sys/bus/pci/drivers/%s/new_id" % driver
394             try:
395                 f = open(filename, "w")
396             except:
397                 print("Error: bind failed for %s - Cannot open %s"
398                       % (dev_id, filename))
399                 return
400             try:
401                 # Convert Device and Vendor Id to int to write to new_id
402                 f.write("%04x %04x" % (int(dev["Vendor"],16),
403                         int(dev["Device"], 16)))
404                 f.close()
405             except:
406                 print("Error: bind failed for %s - Cannot write new PCI ID to "
407                       "driver %s" % (dev_id, driver))
408                 return
409
410     # do the bind by writing to /sys
411     filename = "/sys/bus/pci/drivers/%s/bind" % driver
412     try:
413         f = open(filename, "a")
414     except:
415         print("Error: bind failed for %s - Cannot open %s"
416               % (dev_id, filename))
417         if saved_driver is not None:  # restore any previous driver
418             bind_one(dev_id, saved_driver, force)
419         return
420     try:
421         f.write(dev_id)
422         f.close()
423     except:
424         # for some reason, closing dev_id after adding a new PCI ID to new_id
425         # results in IOError. however, if the device was successfully bound,
426         # we don't care for any errors and can safely ignore IOError
427         tmp = get_pci_device_details(dev_id, True)
428         if "Driver_str" in tmp and tmp["Driver_str"] == driver:
429             return
430         print("Error: bind failed for %s - Cannot bind to driver %s"
431               % (dev_id, driver))
432         if saved_driver is not None:  # restore any previous driver
433             bind_one(dev_id, saved_driver, force)
434         return
435
436     # For kernels > 3.15 driver_override is used to bind a device to a driver.
437     # Before unbinding it, overwrite driver_override with empty string so that
438     # the device can be bound to any other driver
439     filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
440     if os.path.exists(filename):
441         try:
442             f = open(filename, "w")
443         except:
444             print("Error: unbind failed for %s - Cannot open %s"
445                   % (dev_id, filename))
446             sys.exit(1)
447         try:
448             f.write("\00")
449             f.close()
450         except:
451             print("Error: unbind failed for %s - Cannot open %s"
452                   % (dev_id, filename))
453             sys.exit(1)
454
455
456 def unbind_all(dev_list, force=False):
457     """Unbind method, takes a list of device locations"""
458
459     if dev_list[0] == "dpdk":
460         for d in devices.keys():
461             if "Driver_str" in devices[d]:
462                 if devices[d]["Driver_str"] in dpdk_drivers:
463                     unbind_one(devices[d]["Slot"], force)
464         return
465
466     dev_list = map(dev_id_from_dev_name, dev_list)
467     for d in dev_list:
468         unbind_one(d, force)
469
470
471 def bind_all(dev_list, driver, force=False):
472     """Bind method, takes a list of device locations"""
473     global devices
474
475     dev_list = map(dev_id_from_dev_name, dev_list)
476
477     for d in dev_list:
478         bind_one(d, driver, force)
479
480     # For kernels < 3.15 when binding devices to a generic driver
481     # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
482     # that are not bound to any other driver could be bound even if no one has
483     # asked them to. hence, we check the list of drivers again, and see if
484     # some of the previously-unbound devices were erroneously bound.
485     if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
486         for d in devices.keys():
487             # skip devices that were already bound or that we know should be bound
488             if "Driver_str" in devices[d] or d in dev_list:
489                 continue
490
491             # update information about this device
492             devices[d] = dict(devices[d].items() +
493                               get_pci_device_details(d, True).items())
494
495             # check if updated information indicates that the device was bound
496             if "Driver_str" in devices[d]:
497                 unbind_one(d, force)
498
499
500 def display_devices(title, dev_list, extra_params=None):
501     '''Displays to the user the details of a list of devices given in
502     "dev_list". The "extra_params" parameter, if given, should contain a string
503      with %()s fields in it for replacement by the named fields in each
504      device's dictionary.'''
505     strings = []  # this holds the strings to print. We sort before printing
506     print("\n%s" % title)
507     print("="*len(title))
508     if len(dev_list) == 0:
509         strings.append("<none>")
510     else:
511         for dev in dev_list:
512             if extra_params is not None:
513                 strings.append("%s '%s %s' %s" % (dev["Slot"],
514                                                dev["Device_str"],
515                                                dev["Device"],
516                                                extra_params % dev))
517             else:
518                 strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
519     # sort before printing, so that the entries appear in PCI order
520     strings.sort()
521     print("\n".join(strings))  # print one per line
522
523 def show_device_status(devices_type, device_name):
524     global dpdk_drivers
525     kernel_drv = []
526     dpdk_drv = []
527     no_drv = []
528
529     # split our list of network devices into the three categories above
530     for d in devices.keys():
531         if device_type_match(devices[d], devices_type):
532             if not has_driver(d):
533                 no_drv.append(devices[d])
534                 continue
535             if devices[d]["Driver_str"] in dpdk_drivers:
536                 dpdk_drv.append(devices[d])
537             else:
538                 kernel_drv.append(devices[d])
539
540     # print each category separately, so we can clearly see what's used by DPDK
541     display_devices("%s devices using DPDK-compatible driver" % device_name,
542                     dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
543     display_devices("%s devices using kernel driver" % device_name, kernel_drv,
544                     "if=%(Interface)s drv=%(Driver_str)s "
545                     "unused=%(Module_str)s %(Active)s")
546     display_devices("Other %s devices" % device_name, no_drv,
547                     "unused=%(Module_str)s")
548
549 def show_status():
550     '''Function called when the script is passed the "--status" option.
551     Displays to the user what devices are bound to the igb_uio driver, the
552     kernel driver or to no driver'''
553
554     if status_dev == "net" or status_dev == "all":
555         show_device_status(network_devices, "Network")
556
557     if status_dev == "crypto" or status_dev == "all":
558         show_device_status(crypto_devices, "Crypto")
559
560     if status_dev == "event" or status_dev == "all":
561         show_device_status(eventdev_devices, "Eventdev")
562
563     if status_dev == "mempool" or status_dev == "all":
564         show_device_status(mempool_devices, "Mempool")
565
566 def parse_args():
567     '''Parses the command-line arguments given by the user and takes the
568     appropriate action for each'''
569     global b_flag
570     global status_flag
571     global status_dev
572     global force_flag
573     global args
574     if len(sys.argv) <= 1:
575         usage()
576         sys.exit(0)
577
578     try:
579         opts, args = getopt.getopt(sys.argv[1:], "b:us",
580                                    ["help", "usage", "status", "status-dev=",
581                                     "force", "bind=", "unbind", ])
582     except getopt.GetoptError as error:
583         print(str(error))
584         print("Run '%s --usage' for further information" % sys.argv[0])
585         sys.exit(1)
586
587     for opt, arg in opts:
588         if opt == "--help" or opt == "--usage":
589             usage()
590             sys.exit(0)
591         if opt == "--status-dev":
592             status_flag = True
593             status_dev = arg
594         if opt == "--status" or opt == "-s":
595             status_flag = True
596             status_dev = "all"
597         if opt == "--force":
598             force_flag = True
599         if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
600             if b_flag is not None:
601                 print("Error - Only one bind or unbind may be specified\n")
602                 sys.exit(1)
603             if opt == "-u" or opt == "--unbind":
604                 b_flag = "none"
605             else:
606                 b_flag = arg
607
608
609 def do_arg_actions():
610     '''do the actual action requested by the user'''
611     global b_flag
612     global status_flag
613     global force_flag
614     global args
615
616     if b_flag is None and not status_flag:
617         print("Error: No action specified for devices."
618               "Please give a -b or -u option")
619         print("Run '%s --usage' for further information" % sys.argv[0])
620         sys.exit(1)
621
622     if b_flag is not None and len(args) == 0:
623         print("Error: No devices specified.")
624         print("Run '%s --usage' for further information" % sys.argv[0])
625         sys.exit(1)
626
627     if b_flag == "none" or b_flag == "None":
628         unbind_all(args, force_flag)
629     elif b_flag is not None:
630         bind_all(args, b_flag, force_flag)
631     if status_flag:
632         if b_flag is not None:
633             clear_data()
634             # refresh if we have changed anything
635             get_device_details(network_devices)
636             get_device_details(crypto_devices)
637             get_device_details(eventdev_devices)
638             get_device_details(mempool_devices)
639         show_status()
640
641
642 def main():
643     '''program main function'''
644     parse_args()
645     check_modules()
646     clear_data()
647     get_device_details(network_devices)
648     get_device_details(crypto_devices)
649     get_device_details(eventdev_devices)
650     get_device_details(mempool_devices)
651     do_arg_actions()
652
653 if __name__ == "__main__":
654     main()