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