blob: d35c5f9824f47fea37f6766d06b545a79679aff6 [file] [log] [blame]
Damjan Marion38c61912023-10-17 16:06:26 +00001/* SPDX-License-Identifier: Apache-2.0
2 * Copyright (c) 2023 Cisco Systems, Inc.
3 */
4
Damjan Marion38c61912023-10-17 16:06:26 +00005#include <vnet/vnet.h>
6#include <vnet/dev/dev.h>
7#include <vnet/dev/pci.h>
8#include <vnet/dev/log.h>
Damjan Marionb8dd9812023-11-03 13:47:05 +00009#include <vlib/unix/unix.h>
Damjan Marion38c61912023-10-17 16:06:26 +000010
11VLIB_REGISTER_LOG_CLASS (dev_log, static) = {
12 .class_name = "dev",
13 .subclass_name = "pci",
Damjan Marion38c61912023-10-17 16:06:26 +000014};
15
16static int
17vnet_dev_bus_pci_device_id_to_pci_addr (vlib_pci_addr_t *addr, char *str)
18{
19 unformat_input_t input;
20 uword rv;
21 unformat_init_string (&input, str, strlen (str));
22 rv = unformat (&input, "pci" VNET_DEV_DEVICE_ID_PREFIX_DELIMITER "%U",
23 unformat_vlib_pci_addr, addr);
24 unformat_free (&input);
25 return rv;
26}
27
28static void *
29vnet_dev_bus_pci_get_device_info (vlib_main_t *vm, char *device_id)
30{
31 vnet_dev_bus_pci_device_info_t *info;
32 vlib_pci_addr_t addr = {};
33 clib_error_t *err = 0;
34 vlib_pci_device_info_t *di = 0;
35
36 vlib_log_debug (dev_log.class, "device %s", device_id);
37
38 if (vnet_dev_bus_pci_device_id_to_pci_addr (&addr, device_id) == 0)
39 return 0;
40
41 di = vlib_pci_get_device_info (vm, &addr, &err);
42 if (err)
43 {
44 vlib_log_err (dev_log.class, "get_device_info: %U", format_clib_error,
45 err);
46 clib_error_free (err);
47 return 0;
48 }
49
50 info = clib_mem_alloc (sizeof (vnet_dev_bus_pci_device_info_t));
51 info->addr = addr;
52 info->vendor_id = di->vendor_id;
53 info->device_id = di->device_id;
54 info->revision = di->revision;
55
56 vlib_pci_free_device_info (di);
57 return info;
58}
59
60static void
61vnet_dev_bus_pci_free_device_info (vlib_main_t *vm, void *dev_info)
62{
63 clib_mem_free (dev_info);
64}
65
66static vnet_dev_rv_t
67vnet_dev_bus_pci_open (vlib_main_t *vm, vnet_dev_t *dev)
68{
69 clib_error_t *err = 0;
70 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
71
72 if (vnet_dev_bus_pci_device_id_to_pci_addr (&pdd->addr, dev->device_id) == 0)
73 return VNET_DEV_ERR_INVALID_DEVICE_ID;
74
75 if ((err = vlib_pci_device_open (vm, &pdd->addr, 0, &pdd->handle)))
76 {
77 log_err (dev, "device_open: %U", format_clib_error, err);
78 clib_error_free (err);
79 return VNET_DEV_ERR_BUS;
80 }
81
82 dev->numa_node = vlib_pci_get_numa_node (vm, pdd->handle);
83
84 if (vlib_pci_supports_virtual_addr_dma (vm, pdd->handle))
85 {
86 dev->va_dma = 1;
87 log_debug (dev, "device supports VA DMA");
88 }
89
90 vlib_pci_set_private_data (vm, pdd->handle, (uword) dev);
91
92 pdd->n_msix_int = vlib_pci_get_num_msix_interrupts (vm, pdd->handle);
93 if (pdd->n_msix_int)
94 {
95 u32 sz = sizeof (pdd->msix_handlers[0]) * pdd->n_msix_int;
96 sz = round_pow2 (sz, CLIB_CACHE_LINE_BYTES);
97 pdd->msix_handlers = clib_mem_alloc_aligned (sz, CLIB_CACHE_LINE_BYTES);
98 clib_memset (pdd->msix_handlers, 0, sz);
99 }
100
101 return VNET_DEV_OK;
102}
103
104static void
105vnet_dev_bus_pci_close (vlib_main_t *vm, vnet_dev_t *dev)
106{
107 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
108
109 if (pdd->intx_handler)
110 vnet_dev_pci_intx_remove_handler (vm, dev);
111
112 if (pdd->msix_handlers)
113 {
114 for (u16 i = 0; i < pdd->n_msix_int; i++)
115 if (pdd->msix_handlers[i])
116 vnet_dev_pci_msix_remove_handler (vm, dev, i, 1);
117 clib_mem_free (pdd->msix_handlers);
118 pdd->msix_handlers = 0;
119 }
120
121 if (pdd->pci_handle_valid)
122 vlib_pci_device_close (vm, pdd->handle);
123}
124
125static vnet_dev_rv_t
126vnet_dev_bus_pci_dma_mem_alloc (vlib_main_t *vm, vnet_dev_t *dev, u32 size,
127 u32 align, void **pp)
128{
129 clib_error_t *err;
130 void *p;
131
132 align = align ? align : CLIB_CACHE_LINE_BYTES;
133 size = round_pow2 (size, align);
134
135 p = vlib_physmem_alloc_aligned_on_numa (vm, size, align, dev->numa_node);
136
137 if (p == 0)
138 {
139 err = vlib_physmem_last_error (vm);
140 log_err (dev, "dev_dma_mem_alloc: physmem_alloc_aligned error %U",
141 format_clib_error, err);
142 clib_error_free (err);
143 return VNET_DEV_ERR_DMA_MEM_ALLOC_FAIL;
144 }
145
146 if ((err = vlib_pci_map_dma (vm, vnet_dev_get_pci_handle (dev), p)))
147 {
148 log_err (dev, "dev_dma_mem_alloc: pci_map_dma: %U", format_clib_error,
149 err);
150 clib_error_free (err);
151 return VNET_DEV_ERR_DMA_MEM_ALLOC_FAIL;
152 }
153
154 clib_memset (p, 0, size);
155 pp[0] = p;
156 return VNET_DEV_OK;
157}
158
159static void
160vnet_dev_bus_pci_dma_mem_free (vlib_main_t *vm, vnet_dev_t *dev, void *p)
161{
162 if (p)
163 vlib_physmem_free (vm, p);
164}
165
166vnet_dev_rv_t
167vnet_dev_pci_read_config_header (vlib_main_t *vm, vnet_dev_t *dev,
168 vlib_pci_config_hdr_t *hdr)
169{
170 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
171 clib_error_t *err;
172
173 err = vlib_pci_read_write_config (vm, h, VLIB_READ, 0, hdr, sizeof (*hdr));
174 if (err)
175 {
176 log_err (dev, "pci_read_config_header: %U", format_clib_error, err);
177 clib_error_free (err);
178 return VNET_DEV_ERR_BUS;
179 }
180 return VNET_DEV_OK;
181}
182
183vnet_dev_rv_t
184vnet_dev_pci_map_region (vlib_main_t *vm, vnet_dev_t *dev, u8 region,
185 void **pp)
186{
187 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
188 clib_error_t *err;
189
190 if ((err = vlib_pci_map_region (vm, h, region, pp)))
191 {
192 log_err (dev, "pci_map_region: %U", format_clib_error, err);
193 clib_error_free (err);
194 return VNET_DEV_ERR_BUS;
195 }
196
197 return VNET_DEV_OK;
198}
199
200vnet_dev_rv_t
201vnet_dev_pci_function_level_reset (vlib_main_t *vm, vnet_dev_t *dev)
202{
203 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
204 clib_error_t *err;
205
206 if ((err = vlib_pci_function_level_reset (vm, h)))
207 {
208 log_err (dev, "pci_function_level_reset: %U", format_clib_error, err);
209 clib_error_free (err);
210 return VNET_DEV_ERR_BUS;
211 }
212
213 return VNET_DEV_OK;
214}
215
216vnet_dev_rv_t
217vnet_dev_pci_bus_master_enable (vlib_main_t *vm, vnet_dev_t *dev)
218{
219 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
220 clib_error_t *err;
221
222 if ((err = vlib_pci_bus_master_enable (vm, h)))
223 {
224 log_err (dev, "pci_bus_master_enable: %U", format_clib_error, err);
225 clib_error_free (err);
226 return VNET_DEV_ERR_BUS;
227 }
228 return VNET_DEV_OK;
229}
230
231static void
232vnet_dev_pci_intx_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h)
233{
234 vnet_dev_t *dev = (vnet_dev_t *) vlib_pci_get_private_data (vm, h);
235 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
236
237 if (pdd->intx_handler)
238 pdd->intx_handler (vm, dev);
239}
240
241vnet_dev_rv_t
242vnet_dev_pci_intx_add_handler (vlib_main_t *vm, vnet_dev_t *dev,
243 vnet_dev_pci_intx_handler_fn_t *fn)
244{
245 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
246 clib_error_t *err;
247
248 err = vlib_pci_register_intx_handler (vm, h, vnet_dev_pci_intx_handler);
249
250 if (err)
251 {
252 log_err (dev, "pci_register_intx_handler: %U", format_clib_error, err);
253 clib_error_free (err);
254 return VNET_DEV_ERR_BUS;
255 }
256
257 return VNET_DEV_OK;
258}
259
260vnet_dev_rv_t
261vnet_dev_pci_intx_remove_handler (vlib_main_t *vm, vnet_dev_t *dev)
262{
263 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
264 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
265 clib_error_t *err;
266
267 err = vlib_pci_unregister_intx_handler (vm, h);
268
269 if (err)
270 {
271 log_err (dev, "pci_unregister_intx_handler: %U", format_clib_error, err);
272 clib_error_free (err);
273 return VNET_DEV_ERR_BUS;
274 }
275
276 pdd->intx_handler = 0;
277
278 return VNET_DEV_OK;
279}
280
281static void
282vnet_dev_pci_msix_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h, u16 line)
283{
284 vnet_dev_t *dev = (vnet_dev_t *) vlib_pci_get_private_data (vm, h);
285 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
286
287 if (line < vec_len (pdd->msix_handlers) && pdd->msix_handlers[line])
288 pdd->msix_handlers[line](vm, dev, line);
289}
290
291vnet_dev_rv_t
292vnet_dev_pci_msix_add_handler (vlib_main_t *vm, vnet_dev_t *dev,
293 vnet_dev_pci_msix_handler_fn_t *fn, u16 first,
294 u16 count)
295{
296 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
297 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
298 clib_error_t *err;
299
300 err = vlib_pci_register_msix_handler (vm, h, first, count,
301 vnet_dev_pci_msix_handler);
302
303 if (err)
304 {
305 log_err (dev, "pci_register_msix_handler: %U", format_clib_error, err);
306 clib_error_free (err);
307 return VNET_DEV_ERR_BUS;
308 }
309
310 for (u16 i = first; i < first + count; i++)
311 {
312 ASSERT (pdd->msix_handlers[i] == 0);
313 pdd->msix_handlers[i] = fn;
314 }
315
316 return VNET_DEV_OK;
317}
318
Damjan Marionb8dd9812023-11-03 13:47:05 +0000319void
320vnet_dev_pci_msix_set_polling_thread (vlib_main_t *vm, vnet_dev_t *dev,
321 u16 line, u16 thread_index)
322{
323 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
324 u32 index;
325
326 index = vlib_pci_get_msix_file_index (vm, h, line);
327
328 clib_file_set_polling_thread (&file_main, index, thread_index);
329}
330
Damjan Marion38c61912023-10-17 16:06:26 +0000331vnet_dev_rv_t
332vnet_dev_pci_msix_remove_handler (vlib_main_t *vm, vnet_dev_t *dev, u16 first,
333 u16 count)
334{
335 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
336 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
337 clib_error_t *err;
338
339 err = vlib_pci_unregister_msix_handler (vm, h, first, count);
340
341 if (err)
342 {
343 log_err (dev, "pci_unregister_msix_handler: %U", format_clib_error, err);
344 clib_error_free (err);
345 return VNET_DEV_ERR_BUS;
346 }
347
348 for (u16 i = first; i < first + count; i++)
349 {
350 ASSERT (pdd->msix_handlers[i] != 0);
351 pdd->msix_handlers[i] = 0;
352 }
353
354 return VNET_DEV_OK;
355}
356
357vnet_dev_rv_t
358vnet_dev_pci_msix_enable (vlib_main_t *vm, vnet_dev_t *dev, u16 first,
359 u16 count)
360{
361 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
362 clib_error_t *err;
363
364 err = vlib_pci_enable_msix_irq (vm, h, first, count);
365
366 if (err)
367 {
368 log_err (dev, "pci_enable_msix_irq: %U", format_clib_error, err);
369 clib_error_free (err);
370 return VNET_DEV_ERR_BUS;
371 }
372
373 return VNET_DEV_OK;
374}
375
376vnet_dev_rv_t
377vnet_dev_pci_msix_disable (vlib_main_t *vm, vnet_dev_t *dev, u16 first,
378 u16 count)
379{
380 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
381 clib_error_t *err;
382
383 err = vlib_pci_disable_msix_irq (vm, h, first, count);
384
385 if (err)
386 {
387 log_err (dev, "pci_disble_msix_irq: %U", format_clib_error, err);
388 clib_error_free (err);
389 return VNET_DEV_ERR_BUS;
390 }
391
392 return VNET_DEV_OK;
393}
394
395vnet_dev_rv_t
396vnet_dev_pci_bus_master_disable (vlib_main_t *vm, vnet_dev_t *dev)
397{
398 vlib_pci_dev_handle_t h = vnet_dev_get_pci_handle (dev);
399 clib_error_t *err;
400
401 if ((err = vlib_pci_bus_master_disable (vm, h)))
402 {
403 log_err (dev, "pci_bus_master_disable: %U", format_clib_error, err);
404 clib_error_free (err);
405 return VNET_DEV_ERR_BUS;
406 }
407 return VNET_DEV_OK;
408}
409
410static u8 *
411format_dev_pci_device_info (u8 *s, va_list *args)
412{
413 vnet_dev_format_args_t __clib_unused *a =
414 va_arg (*args, vnet_dev_format_args_t *);
415 vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
416 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
417 vlib_main_t *vm = vlib_get_main ();
418 vlib_pci_config_t cfg = {};
419 clib_error_t *err;
420
421 s = format (s, "PCIe address is %U", format_vlib_pci_addr, &pdd->addr);
422
423 err = vlib_pci_read_write_config (vm, pdd->handle, VLIB_READ, 0, &cfg,
424 sizeof (cfg));
425 if (!err)
426 {
427 s = format (s, ", port is %U, speed is %U (max %U)",
428 format_vlib_pci_link_port, &cfg, format_vlib_pci_link_speed,
429 &cfg, format_vlib_pci_link_speed_cap, &cfg);
430 }
431 else
432 clib_error_free (err);
433
434 return s;
435}
436
437static u8 *
438format_dev_pci_device_addr (u8 *s, va_list *args)
439{
440 vnet_dev_t *dev = va_arg (*args, vnet_dev_t *);
441 vnet_dev_bus_pci_device_data_t *pdd = vnet_dev_get_bus_pci_device_data (dev);
442 return format (s, "%U", format_vlib_pci_addr, &pdd->addr);
443}
444
445VNET_DEV_REGISTER_BUS (pci) = {
446 .name = "pci",
447 .device_data_size = sizeof (vnet_dev_bus_pci_device_info_t),
448 .ops = {
449 .device_open = vnet_dev_bus_pci_open,
450 .device_close = vnet_dev_bus_pci_close,
451 .get_device_info = vnet_dev_bus_pci_get_device_info,
452 .free_device_info = vnet_dev_bus_pci_free_device_info,
453 .dma_mem_alloc_fn = vnet_dev_bus_pci_dma_mem_alloc,
454 .dma_mem_free_fn = vnet_dev_bus_pci_dma_mem_free,
455 .format_device_info = format_dev_pci_device_info,
456 .format_device_addr = format_dev_pci_device_addr,
457 },
458};