blob: fe64c9d4b149fa8e705b75800d7509d077d3e356 [file] [log] [blame]
John DeNisco68b0ee32017-09-27 16:35:23 -04001# Copyright (c) 2016 Cisco and/or its affiliates.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at:
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13
14"""VPP PCI Utility libraries"""
15
16import re
jdenisco7c37a672018-11-20 11:25:17 -050017import logging
John DeNisco68b0ee32017-09-27 16:35:23 -040018
19from vpplib.VPPUtil import VPPUtil
20
21DPDK_SCRIPT = "/vpp/vpp-config/scripts/dpdk-devbind.py"
22
23# PCI Device id regular expresssion
24PCI_DEV_ID_REGEX = '[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+.[0-9A-Fa-f]+'
25
26
27class VppPCIUtil(object):
28 """
29 PCI Utilities
30
31 """
32
33 @staticmethod
34 def _create_device_list(device_string):
35 """
36 Returns a list of PCI devices
37
38 :param device_string: The devices string from dpdk_devbind
39 :returns: The device list
40 :rtype: dictionary
41 """
42
43 devices = {}
44
45 ids = re.findall(PCI_DEV_ID_REGEX, device_string)
46 descriptions = re.findall(r'\'([\s\S]*?)\'', device_string)
47 unused = re.findall(r'unused=[\w,]+', device_string)
48
49 for i, j in enumerate(ids):
50 device = {'description': descriptions[i]}
51 if unused:
52 device['unused'] = unused[i].split('=')[1].split(',')
53
54 cmd = 'ls /sys/bus/pci/devices/{}/driver/module/drivers'. \
55 format(ids[i])
56 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
57 if ret == 0:
58 device['driver'] = stdout.split(':')[1].rstrip('\n')
59
60 cmd = 'cat /sys/bus/pci/devices/{}/numa_node'.format(ids[i])
61 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
62 if ret != 0:
63 raise RuntimeError('{} failed {} {}'.
64 format(cmd, stderr, stdout))
65 numa_node = stdout.rstrip('\n')
66 if numa_node == '-1':
67 device['numa_node'] = '0'
68 else:
69 device['numa_node'] = numa_node
70
71 interfaces = []
72 device['interfaces'] = []
73 cmd = 'ls /sys/bus/pci/devices/{}/net'.format(ids[i])
74 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
75 if ret == 0:
76 interfaces = stdout.rstrip('\n').split()
77 device['interfaces'] = interfaces
78
79 l2_addrs = []
80 for intf in interfaces:
81 cmd = 'cat /sys/bus/pci/devices/{}/net/{}/address'.format(
82 ids[i], intf)
83 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
84 if ret != 0:
85 raise RuntimeError('{} failed {} {}'.
86 format(cmd, stderr, stdout))
87
88 l2_addrs.append(stdout.rstrip('\n'))
89
90 device['l2addr'] = l2_addrs
91
92 devices[ids[i]] = device
93
94 return devices
95
96 def __init__(self, node):
97 self._node = node
98 self._dpdk_devices = {}
99 self._kernel_devices = {}
100 self._other_devices = {}
101 self._crypto_dpdk_devices = {}
102 self._crypto_kernel_devices = {}
103 self._crypto_other_devices = {}
104 self._link_up_devices = {}
105
106 def get_all_devices(self):
107 """
108 Returns a list of all the devices
109
110 """
111
112 node = self._node
113 rootdir = node['rootdir']
114 dpdk_script = rootdir + DPDK_SCRIPT
115 cmd = dpdk_script + ' --status'
116 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
117 if ret != 0:
118 raise RuntimeError('{} failed on node {} {}'.format(
119 cmd,
120 node['host'],
121 stderr))
122
123 # Get the network devices using the DPDK
124 # First get everything after using DPDK
125 stda = stdout.split('Network devices using DPDK-compatible driver')[1]
126 # Then get everything before using kernel driver
127 using_dpdk = stda.split('Network devices using kernel driver')[0]
128 self._dpdk_devices = self._create_device_list(using_dpdk)
129
130 # Get the network devices using the kernel
131 stda = stdout.split('Network devices using kernel driver')[1]
132 using_kernel = stda.split('Other network devices')[0]
133 self._kernel_devices = self._create_device_list(using_kernel)
134
135 # Get the other network devices
136 stda = stdout.split('Other network devices')[1]
137 other = stda.split('Crypto devices using DPDK-compatible driver')[0]
138 self._other_devices = self._create_device_list(other)
139
140 # Get the crypto devices using the DPDK
141 stda = stdout.split('Crypto devices using DPDK-compatible driver')[1]
142 crypto_using_dpdk = stda.split('Crypto devices using kernel driver')[0]
143 self._crypto_dpdk_devices = self._create_device_list(
144 crypto_using_dpdk)
145
146 # Get the network devices using the kernel
147 stda = stdout.split('Crypto devices using kernel driver')[1]
148 crypto_using_kernel = stda.split('Other crypto devices')[0]
149 self._crypto_kernel_devices = self._create_device_list(
150 crypto_using_kernel)
151
152 # Get the other network devices
153 crypto_other = stdout.split('Other crypto devices')[1]
154 self._crypto_other_devices = self._create_device_list(crypto_other)
155
156 # Get the devices used by the kernel
157 for devk in self._kernel_devices.items():
158 dvid = devk[0]
159 device = devk[1]
160 for i in device['interfaces']:
161 cmd = "ip addr show " + i
162 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
163 if ret != 0:
164 raise RuntimeError('{} failed on node {} {}'.format(
165 cmd,
166 node['host'],
167 stderr))
168 lstate = re.findall(r'state \w+', stdout)[0].split(' ')[1]
169
170 # Take care of the links that are UP
171 if lstate == 'UP':
172 device['linkup'] = True
173 self._link_up_devices[dvid] = device
174
175 for devl in self._link_up_devices.items():
176 dvid = devl[0]
177 del self._kernel_devices[dvid]
178
179 def get_dpdk_devices(self):
180 """
181 Returns a list the dpdk devices
182
183 """
184 return self._dpdk_devices
185
186 def get_kernel_devices(self):
187 """
188 Returns a list the kernel devices
189
190 """
191 return self._kernel_devices
192
193 def get_other_devices(self):
194 """
195 Returns a list the other devices
196
197 """
198 return self._other_devices
199
200 def get_crypto_dpdk_devices(self):
201 """
202 Returns a list the crypto dpdk devices
203
204 """
205 return self._crypto_dpdk_devices
206
207 def get_crypto_kernel_devices(self):
208 """
209 Returns a list the crypto kernel devices
210
211 """
212 return self._crypto_kernel_devices
213
214 def get_crypto_other_devices(self):
215 """
216 Returns a list the crypto other devices
217
218 """
219 return self._crypto_other_devices
220
221 def get_link_up_devices(self):
222 """
223 Returns a list the link up devices
224
225 """
226 return self._link_up_devices
227
228 @staticmethod
229 def vpp_create_interface(interfaces, device_id, device):
230 """
231 Create an interface using the device is and device
232
233 """
234
235 name = 'port' + str(len(interfaces))
236 interfaces[name] = {}
237 interfaces[name]['pci_address'] = device_id
238 interfaces[name]['numa_node'] = device['numa_node']
239 if 'l2addr' in device:
240 l2_addrs = device['l2addr']
241 for i, j in enumerate(l2_addrs):
242 if i > 0:
243 mname = 'mac_address' + str(i + 1)
244 interfaces[name][mname] = l2_addrs[i]
245 else:
246 interfaces[name]['mac_address'] = l2_addrs[i]
247
248 @staticmethod
249 def show_vpp_devices(devices, show_interfaces=True, show_header=True):
250 """
251 show the vpp devices specified in the argument
252
253 :param devices: A list of devices
254 :param show_interfaces: show the kernel information
John DeNiscoc6b2a202017-11-01 12:37:47 -0400255 :param show_header: Display the header if true
John DeNisco68b0ee32017-09-27 16:35:23 -0400256 :type devices: dict
257 :type show_interfaces: bool
John DeNiscoc6b2a202017-11-01 12:37:47 -0400258 :type show_header: bool
John DeNisco68b0ee32017-09-27 16:35:23 -0400259 """
260
John DeNisco68b0ee32017-09-27 16:35:23 -0400261 if show_interfaces:
262 header = "{:15} {:25} {:50}".format("PCI ID",
263 "Kernel Interface(s)",
264 "Description")
265 else:
266 header = "{:15} {:50}".format("PCI ID",
267 "Description")
268 dashseparator = ("-" * (len(header) - 2))
269
John DeNiscoc6b2a202017-11-01 12:37:47 -0400270 if show_header is True:
John DeNisco68b0ee32017-09-27 16:35:23 -0400271 print header
272 print dashseparator
273 for dit in devices.items():
274 dvid = dit[0]
275 device = dit[1]
276 if show_interfaces:
277 interfaces = device['interfaces']
278 interface = ''
279 for i, j in enumerate(interfaces):
280 if i > 0:
281 interface += ',' + interfaces[i]
282 else:
283 interface = interfaces[i]
284
285 print "{:15} {:25} {:50}".format(
286 dvid, interface, device['description'])
287 else:
288 print "{:15} {:50}".format(
289 dvid, device['description'])
290
291 @staticmethod
292 def unbind_vpp_device(node, device_id):
293 """
294 unbind the device specified
295
296 :param node: Node dictionary with cpuinfo.
297 :param device_id: The device id
298 :type node: dict
299 :type device_id: string
300 """
301
John DeNisco68b0ee32017-09-27 16:35:23 -0400302 rootdir = node['rootdir']
303 dpdk_script = rootdir + DPDK_SCRIPT
304 cmd = dpdk_script + ' -u ' + ' ' + device_id
305 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
306 if ret != 0:
307 raise RuntimeError('{} failed on node {} {} {}'.format(
308 cmd, node['host'],
309 stdout, stderr))
310
311 @staticmethod
312 def bind_vpp_device(node, driver, device_id):
313 """
314 bind the device specified
315
316 :param node: Node dictionary with cpuinfo.
317 :param driver: The driver
318 :param device_id: The device id
319 :type node: dict
320 :type driver: string
321 :type device_id: string
jdenisco7c37a672018-11-20 11:25:17 -0500322 :returns ret: Command return code
John DeNisco68b0ee32017-09-27 16:35:23 -0400323 """
324
325 rootdir = node['rootdir']
326 dpdk_script = rootdir + DPDK_SCRIPT
327 cmd = dpdk_script + ' -b ' + driver + ' ' + device_id
328 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
329 if ret != 0:
jdenisco7c37a672018-11-20 11:25:17 -0500330 logging.error('{} failed on node {}'.format(
John DeNisco68b0ee32017-09-27 16:35:23 -0400331 cmd, node['host'], stdout, stderr))
jdenisco7c37a672018-11-20 11:25:17 -0500332 logging.error('{} {}'.format(
333 stdout, stderr))
334
335 return ret