Merge "Tools: Improve Stability."
[vswitchperf.git] / src / dpdk / dpdk.py
1 # Copyright 2015-2017 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*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 glob
27
28 from conf import settings as S
29 from tools import tasks
30 from tools.module_manager import ModuleManager
31
32 _LOGGER = logging.getLogger(__name__)
33
34 _DPDK_MODULE_MANAGER = ModuleManager()
35
36 # declare global NIC variables only as their content might not be known yet
37 _NICS = []
38 _NICS_PCI = []
39
40 #
41 # system management
42 #
43
44 def init():
45     """Setup system for DPDK.
46     """
47     # pylint: disable=global-statement
48     global _NICS
49     global _NICS_PCI
50     _NICS = S.getValue('NICS')
51     _NICS_PCI = list(nic['pci'] for nic in _NICS)
52     if not _is_linux():
53         _LOGGER.error('Not running on a compatible Linux version. Exiting...')
54         return
55     _insert_modules()
56     _remove_vhost_net()
57     _bind_nics()
58
59
60 def cleanup():
61     """Setup system for DPDK.
62     """
63     if not _is_linux():
64         _LOGGER.error('Not running on a compatible Linux version. Exiting...')
65         return
66
67     _unbind_nics()
68     _remove_modules()
69     _vhost_user_cleanup()
70
71 #
72 # basic compatibility test
73 #
74
75 def _is_linux():
76     """Check if running on Linux.
77
78     Many of the functions in this file rely on features commonly found
79     only on Linux (i.e. ``/proc`` is not present on FreeBSD). Hence, this
80     check is important to ensure someone doesn't run this on an incompatible
81     OS or distro.
82     """
83     return _platform.startswith('linux') and os.path.isdir('/proc')
84
85 #
86 # module management
87 #
88
89 def _insert_modules():
90     """Ensure required modules are inserted on system.
91     """
92
93     _DPDK_MODULE_MANAGER.insert_modules(S.getValue('TOOLS')['dpdk_modules'])
94
95 def _remove_modules():
96     """Ensure required modules are removed from system.
97     """
98     _DPDK_MODULE_MANAGER.remove_modules()
99
100 #
101 # 'vhost-net' module cleanup
102 #
103
104 def _remove_vhost_net():
105     """Remove vhost-net driver and file.
106     """
107     _DPDK_MODULE_MANAGER.remove_module('vhost-net')
108     try:
109         tasks.run_task(['sudo', 'rm', '-f', '/dev/vhost-net'], _LOGGER,
110                        'Removing \'/dev/vhost-net\' directory...', True)
111     except subprocess.CalledProcessError:
112         _LOGGER.error('Unable to remove directory \'/dev/vhost-net\'.')
113
114 #
115 # Vhost-user cleanup
116 #
117
118 def _vhost_user_cleanup():
119     """Remove files created by vhost-user tests.
120     """
121     for sock in glob.glob(os.path.join(S.getValue('TOOLS')['ovs_var_tmp'],
122                                        S.getValue('VHOST_USER_SOCKS'))):
123         if os.path.exists(sock):
124             try:
125                 tasks.run_task(['sudo', 'rm', sock],
126                                _LOGGER,
127                                'Deleting vhost-user socket \'%s\'...' %
128                                sock,
129                                True)
130
131             except subprocess.CalledProcessError:
132                 _LOGGER.error('Unable to delete vhost-user socket \'%s\'.',
133                               sock)
134                 continue
135 #
136 # NIC management
137 #
138
139
140 def _bind_nics():
141     """Bind NICs using the bind tool specified in the configuration.
142     """
143     if not _NICS_PCI:
144         _LOGGER.info('NICs are not configured - nothing to bind')
145         return
146     try:
147         _driver = 'igb_uio'
148         if 'vfio-pci' in S.getValue('TOOLS')['dpdk_modules']:
149             _driver = 'vfio-pci'
150             tasks.run_task(['sudo', 'chmod', 'a+x', '/dev/vfio'],
151                            _LOGGER, 'Setting VFIO permissions .. a+x',
152                            True)
153             tasks.run_task(['sudo', 'chmod', '-R', '666', '/dev/vfio/'],
154                            _LOGGER, 'Setting VFIO permissions .. 0666',
155                            True)
156         if 'driverctl' in S.getValue('TOOLS')['bind-tool'].lower():
157             for nic in _NICS_PCI:
158                 tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], '-v',
159                                 'set-override'] + [nic] + [_driver], _LOGGER,
160                                'Binding NIC %s...' % nic, True)
161         else:
162             tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'],
163                             '--bind=' + _driver] +
164                            _NICS_PCI, _LOGGER,
165                            'Binding NICs %s...' % _NICS_PCI,
166                            True)
167     except subprocess.CalledProcessError:
168         _LOGGER.error('Unable to bind NICs %s', str(_NICS_PCI))
169
170
171 def _unbind_nics():
172     """Unbind NICs using the bind tool specified in the configuration.
173     """
174     if not _NICS_PCI:
175         _LOGGER.info('NICs are not configured - nothing to unbind')
176         return
177     try:
178         if 'driverctl' in S.getValue('TOOLS')['bind-tool'].lower():
179             for nic in _NICS_PCI:
180                 tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], '-v',
181                                 'unset-override'] + [nic], _LOGGER,
182                                'Binding NIC %s...' % nic, True)
183         else:
184             tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'],
185                             '--unbind'] +
186                            _NICS_PCI, _LOGGER,
187                            'Unbinding NICs %s...' % str(_NICS_PCI),
188                            True)
189     except subprocess.CalledProcessError:
190         _LOGGER.error('Unable to unbind NICs %s', str(_NICS_PCI))
191     # Rebind NICs to their original drivers
192     # using the Intel DPDK ``dpdk*bind.py`` tool.
193     for nic in _NICS:
194         try:
195             if nic['driver']:
196                 if 'driverctl' in S.getValue('TOOLS')['bind-tool'].lower():
197                     # driverctl restores the driver automatically on unset
198                     break
199                 else:
200                     tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'],
201                                     '--bind',
202                                     nic['driver'], nic['pci']],
203                                    _LOGGER, 'Binding NIC %s to %s...' %
204                                    (nic['pci'], nic['driver']),
205                                    True)
206         except subprocess.CalledProcessError:
207             _LOGGER.error('Unable to bind NIC %s to driver %s',
208                           nic['pci'], nic['driver'])
209
210
211 class Dpdk(object):
212     """A context manager for the system init/cleanup.
213     """
214     def __enter__(self):
215         _LOGGER.info('Setting up DPDK')
216         init()
217         return self
218
219     def __exit__(self, type_, value, traceback):
220         _LOGGER.info('Cleaning up DPDK')
221         cleanup()