dpdk: enable vfio_pci support
[vswitchperf.git] / src / dpdk / dpdk.py
1 # Copyright 2015-2016 Intel Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """Automation of system configuration for DPDK use.
16
17 Parts of this based on ``tools/dpdk_nic_bind.py`` script from Intel(R)
18 DPDK.
19 """
20
21 from sys import platform as _platform
22
23 import os
24 import subprocess
25 import logging
26 import locale
27
28 from tools import tasks
29 from conf import settings
30 from tools.module_manager import ModuleManager
31
32 _LOGGER = logging.getLogger(__name__)
33 RTE_PCI_TOOL = os.path.join(
34     settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
35
36 _DPDK_MODULE_MANAGER = ModuleManager()
37 #
38 # system management
39 #
40
41
42 def init():
43     """Setup system for DPDK.
44     """
45     if not _is_linux():
46         _LOGGER.error('Not running on a compatible Linux version. Exiting...')
47         return
48     _insert_modules()
49     _remove_vhost_net()
50     _bind_nics()
51
52
53 def cleanup():
54     """Setup system for DPDK.
55     """
56     if not _is_linux():
57         _LOGGER.error('Not running on a compatible Linux version. Exiting...')
58         return
59
60     _unbind_nics()
61     _remove_modules()
62     _vhost_user_cleanup()
63
64 #
65 # basic compatibility test
66 #
67
68 def _is_linux():
69     """Check if running on Linux.
70
71     Many of the functions in this file rely on features commonly found
72     only on Linux (i.e. ``/proc`` is not present on FreeBSD). Hence, this
73     check is important to ensure someone doesn't run this on an incompatible
74     OS or distro.
75     """
76     return _platform.startswith('linux') and os.path.isdir('/proc')
77
78 #
79 # module management
80 #
81
82 def _insert_modules():
83     """Ensure required modules are inserted on system.
84     """
85
86     _DPDK_MODULE_MANAGER.insert_modules(settings.getValue('SYS_MODULES'))
87
88     mod_path_prefix = settings.getValue('OVS_DIR')
89     _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('OVS_MODULES'),
90                                              mod_path_prefix)
91     if 'vfio-pci' not in settings.getValue('DPDK_MODULES'):
92         mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
93                                        settings.getValue('RTE_TARGET'))
94         _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('DPDK_MODULES'),
95                                                  mod_path_prefix)
96     else:
97         _DPDK_MODULE_MANAGER.insert_modules(settings.getValue('DPDK_MODULES'))
98
99 def _remove_modules():
100     """Ensure required modules are removed from system.
101     """
102     _DPDK_MODULE_MANAGER.remove_modules()
103
104 #
105 # vhost specific modules management
106 #
107
108 def insert_vhost_modules():
109     """Inserts VHOST related kernel modules
110     """
111     mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
112                                    'lib',
113                                    'librte_vhost')
114     _DPDK_MODULE_MANAGER.insert_module_group('VHOST_MODULE', mod_path_prefix)
115
116
117 def remove_vhost_modules():
118     """Removes all VHOST related kernel modules
119     """
120     _DPDK_MODULE_MANAGER.remove_module_group(settings.getValue('VHOST_MODULE'))
121
122
123 #
124 # 'vhost-net' module cleanup
125 #
126
127 def _remove_vhost_net():
128     """Remove vhost-net driver and file.
129     """
130     _DPDK_MODULE_MANAGER.remove_module('vhost-net')
131     try:
132         tasks.run_task(['sudo', 'rm', '-f', '/dev/vhost-net'], _LOGGER,
133                        'Removing \'/dev/vhost-net\' directory...', True)
134     except subprocess.CalledProcessError:
135         _LOGGER.error('Unable to remove directory \'/dev/vhost-net\'.')
136
137 #
138 # Vhost-user cleanup
139 #
140
141 def _vhost_user_cleanup():
142     """Remove files created by vhost-user tests.
143     """
144     for sock in settings.getValue('VHOST_USER_SOCKS'):
145         if os.path.exists(sock):
146             try:
147                 tasks.run_task(['sudo', 'rm', sock],
148                                _LOGGER,
149                                'Deleting vhost-user socket \'%s\'...' %
150                                sock,
151                                True)
152
153             except subprocess.CalledProcessError:
154                 _LOGGER.error('Unable to delete vhost-user socket \'%s\'.',
155                               sock)
156                 continue
157 #
158 # NIC management
159 #
160
161
162 def _bind_nics():
163     """Bind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool.
164     """
165     try:
166         _driver = 'igb_uio'
167         if 'vfio-pci' in settings.getValue('DPDK_MODULES'):
168             _driver = 'vfio-pci'
169             tasks.run_task(['sudo', 'chmod', 'a+x', '/dev/vfio'],
170                            _LOGGER, 'Setting VFIO permissions .. a+x',
171                            True)
172             tasks.run_task(['sudo', 'chmod', '-R', '666', '/dev/vfio/'],
173                            _LOGGER, 'Setting VFIO permissions .. 0666',
174                            True)
175
176         tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind=' + _driver] +
177                        settings.getValue('WHITELIST_NICS'), _LOGGER,
178                        'Binding NICs %s...' %
179                        settings.getValue('WHITELIST_NICS'),
180                        True)
181     except subprocess.CalledProcessError:
182         _LOGGER.error('Unable to bind NICs %s',
183                       str(settings.getValue('WHITELIST_NICS')))
184
185 def _unbind_nics_get_driver():
186     """Check what driver the NICs should be bound to
187        after unbinding them from DPDK.
188     """
189     _driver_list = []
190     _output = subprocess.check_output([os.path.expanduser(RTE_PCI_TOOL), '--status'])
191     _my_encoding = locale.getdefaultlocale()[1]
192     for line in _output.decode(_my_encoding).split('\n'):
193         for nic in settings.getValue('WHITELIST_NICS'):
194             if nic in line:
195                 _driver_list.append((line.split("unused=", 1)[1]))
196     return _driver_list
197
198 def _unbind_nics():
199     """Unbind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool.
200     """
201     nic_drivers = _unbind_nics_get_driver()
202     try:
203         tasks.run_task(['sudo', RTE_PCI_TOOL, '--unbind'] +
204                        settings.getValue('WHITELIST_NICS'), _LOGGER,
205                        'Unbinding NICs %s...' %
206                        str(settings.getValue('WHITELIST_NICS')),
207                        True)
208     except subprocess.CalledProcessError:
209         _LOGGER.error('Unable to unbind NICs %s',
210                       str(settings.getValue('WHITELIST_NICS')))
211     # Rebind NICs to their original drivers
212     # using the Intel DPDK ``dpdk_nic_bind.py`` tool.
213     for i, nic in enumerate(settings.getValue('WHITELIST_NICS')):
214         try:
215             if nic_drivers[i] != '':
216                 tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind',
217                                 nic_drivers[i], nic],
218                                _LOGGER, 'Binding NIC %s...' %
219                                nic,
220                                True)
221         except subprocess.CalledProcessError:
222             _LOGGER.error('Unable to bind NICs %s to drivers %s',
223                           str(settings.getValue('WHITELIST_NICS')),
224                           nic_drivers)
225
226 class Dpdk(object):
227     """A context manager for the system init/cleanup.
228     """
229     def __enter__(self):
230         _LOGGER.info('Setting up DPDK')
231         init()
232         return self
233
234     def __exit__(self, type_, value, traceback):
235         _LOGGER.info('Cleaning up DPDK')
236         cleanup()