NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
devemu_pci_device_msix_host_sample.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024 NVIDIA CORPORATION AND AFFILIATES. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are permitted
5  * provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice, this list of
7  * conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright notice, this list of
9  * conditions and the following disclaimer in the documentation and/or other materials
10  * provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
12  * to endorse or promote products derived from this software without specific prior written
13  * permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21  * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 
26 #include <errno.h>
27 #include <signal.h>
28 #include <linux/vfio.h>
29 #include <sys/ioctl.h>
30 #include <sys/eventfd.h>
31 #include <unistd.h>
32 
33 #include <doca_error.h>
34 #include <doca_log.h>
35 
36 #include <common.h>
37 #include <devemu_pci_common.h>
38 #include <devemu_pci_host_common.h>
39 
40 DOCA_LOG_REGISTER(DEVEMU_PCI_DEVICE_MSIX_HOST);
41 
42 static bool force_quit; /* Shared variable to allow for a proper shutdown */
43 
44 /*
45  * Signal handler
46  *
47  * @signum [in]: Signal number to handle
48  */
49 static void signal_handler(int signum)
50 {
51  if (signum == SIGINT || signum == SIGTERM) {
52  DOCA_LOG_INFO("Signal %d received, preparing to exit", signum);
53  force_quit = true;
54  }
55 }
56 
57 /*
58  * Create an eventfd for each MSI-X vector
59  *
60  * @resources [in]: The sample resources
61  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
62  */
64 {
65  int *irq_set_data;
66  uint32_t idx;
68  struct vfio_irq_info irq_info = {
69  .argsz = sizeof(irq_info),
70  .index = VFIO_PCI_MSIX_IRQ_INDEX,
71  };
72  int status = ioctl(resources->device_fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
73  if (status != 0) {
74  DOCA_LOG_ERR("Failed to get IRQ set %u info. Status=%d, errno=%d",
75  VFIO_PCI_MSIX_IRQ_INDEX,
76  status,
77  errno);
78  return DOCA_ERROR_DRIVER;
79  }
80  if (irq_info.count == 0) {
81  DOCA_LOG_ERR("Device does not have MSI-X capability");
83  }
84 
85  const size_t fd_table_size = (sizeof(int) * irq_info.count);
86  resources->msix_vector_to_fd = malloc(fd_table_size);
87  if (resources->msix_vector_to_fd == NULL) {
88  DOCA_LOG_ERR("Failed to allocate msix to fd table, with %u entries", irq_info.count);
89  return DOCA_ERROR_NO_MEMORY;
90  }
91 
92  struct vfio_irq_set *irq_set = malloc(sizeof(struct vfio_irq_set) + fd_table_size);
93  if (irq_set == NULL) {
94  DOCA_LOG_ERR("Failed to allocate vfio_irq_set, with space for %u fds", irq_info.count);
96  goto cleanup_fd_table;
97  }
98  irq_set->argsz = sizeof(struct vfio_irq_set) + fd_table_size;
99  irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
100  irq_set->index = irq_info.index;
101  irq_set->start = 0;
102  irq_set->count = irq_info.count;
103 
104  irq_set_data = (int *)&irq_set->data[0];
105  for (idx = 0; idx < irq_set->count; ++idx) {
106  resources->msix_vector_to_fd[idx] = eventfd(0, EFD_NONBLOCK);
107  if (resources->msix_vector_to_fd[idx] == -1) {
108  DOCA_LOG_ERR("Failed to create eventfd for MSI-X index %u", idx);
110  goto cleanup_irq_set;
111  }
112 
113  irq_set_data[idx] = resources->msix_vector_to_fd[idx];
114  }
115 
116  status = ioctl(resources->device_fd, VFIO_DEVICE_SET_IRQS, irq_set);
117  if (status != 0) {
118  DOCA_LOG_ERR("Failed to set MSI-X IRQs. Status=%d, errno=%d", status, errno);
120  goto cleanup_irq_set;
121  }
122  free(irq_set);
123 
124  return DOCA_SUCCESS;
125 
126 cleanup_irq_set:
127  free(irq_set);
128 cleanup_fd_table:
129  free(resources->msix_vector_to_fd);
130  resources->msix_vector_to_fd = NULL;
131 
132  return result;
133 }
134 
135 /*
136  * Check if any interrupt was received on the MSI-X vector
137  *
138  * @resources [in]: The sample resources
139  * @msix_idx [in]: The MSI-X vector index
140  */
141 static void read_msix_events(struct devemu_host_resources *resources, uint16_t msix_idx)
142 {
143  uint64_t value;
144  ssize_t size = read(resources->msix_vector_to_fd[msix_idx], &value, sizeof(value));
145  if (size == -1) {
146  if (errno != EAGAIN)
147  DOCA_LOG_ERR("Received error while reading MSI-X vector index %u. errno %d", msix_idx, errno);
148  return;
149  }
150 
151  DOCA_LOG_INFO("Event received for MSI-X vector index %u new value %ld", msix_idx, value);
152 }
153 
154 /*
155  * Run DOCA Device Emulation MSI-X Host sample
156  *
157  * @pci_address [in]: Emulated device PCI address
158  * @vfio_group [in]: VFIO group ID
159  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
160  */
161 doca_error_t devemu_pci_device_msix_host(const char *pci_address, int vfio_group)
162 {
164  struct devemu_host_resources resources = {0};
165 
166  /* Signal the while loop to stop */
167  force_quit = false;
168  signal(SIGINT, signal_handler);
169  signal(SIGTERM, signal_handler);
170 
171  resources.container_fd = -1;
172  resources.group_fd = -1;
173  resources.device_fd = -1;
174 
175  result = init_vfio_device(&resources, vfio_group, pci_address);
176  if (result != DOCA_SUCCESS) {
178  return result;
179  }
180 
182  if (result != DOCA_SUCCESS) {
184  return result;
185  }
186 
187  DOCA_LOG_INFO("Listening on all MSI-X vectors");
188 
189  while (!force_quit)
190  for (uint16_t msix = 0; msix < PCI_TYPE_NUM_MSIX; msix++)
191  read_msix_events(&resources, msix);
192 
193  return DOCA_SUCCESS;
194 }
#define NULL
Definition: __stddef_null.h:26
int32_t result
DOCA_LOG_REGISTER(DEVEMU_PCI_DEVICE_MSIX_HOST)
doca_error_t devemu_pci_device_msix_host(const char *pci_address, int vfio_group)
static void read_msix_events(struct devemu_host_resources *resources, uint16_t msix_idx)
static bool force_quit
static void signal_handler(int signum)
static doca_error_t map_msix_to_fds(struct devemu_host_resources *resources)
void devemu_host_resources_cleanup(struct devemu_host_resources *resources)
doca_error_t init_vfio_device(struct devemu_host_resources *resources, int vfio_group, const char *pci_address)
struct rdma_resources resources
enum doca_error doca_error_t
DOCA API return codes.
@ DOCA_ERROR_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_NO_MEMORY
Definition: doca_error.h:45
@ DOCA_ERROR_DRIVER
Definition: doca_error.h:59
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
Definition: doca_log.h:466
#define DOCA_LOG_INFO(format,...)
Generates an INFO application log message.
Definition: doca_log.h:486
type value
#define PCI_TYPE_NUM_MSIX