NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
devemu_pci_device_stateful_region_dpu_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 <devemu_pci_common.h>
27 
28 #include <signal.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <string.h>
32 #include <time.h>
33 
34 #include <doca_ctx.h>
35 #include <doca_devemu_pci.h>
36 #include <doca_dev.h>
37 #include <doca_error.h>
38 #include <doca_log.h>
39 #include <common.h>
40 
41 DOCA_LOG_REGISTER(DEVEMU_PCI_DEVICE_STATEFUL_REGION_DPU);
42 
43 static bool force_quit; /* Shared variable to allow for a proper shutdown */
44 
45 /*
46  * Signal handler
47  *
48  * @signum [in]: Signal number to handle
49  */
50 static void signal_handler(int signum)
51 {
52  if (signum == SIGINT || signum == SIGTERM) {
53  DOCA_LOG_INFO("Signal %d received, preparing to exit", signum);
54  force_quit = true;
55  }
56 }
57 
58 /*
59  * Callback that is triggered everytime the host writes to a stateful region
60  *
61  * @event [in]: The stateful region write event
62  * @user_data [in]: The user data that was provided along with callback during registration
63  */
65  struct doca_devemu_pci_dev_event_bar_stateful_region_driver_write *event,
66  union doca_data user_data)
67 {
68  struct doca_devemu_pci_dev *pci_dev;
69  union doca_data ctx_user_data;
71  doca_error_t res;
72  const struct bar_region_config *config = (const struct bar_region_config *)user_data.ptr;
73 
74  DOCA_LOG_INFO("Host wrote to stateful region of emulated device");
75 
77 
78  res = doca_ctx_get_user_data(doca_devemu_pci_dev_as_ctx(pci_dev), &ctx_user_data);
79  if (res != DOCA_SUCCESS) {
80  DOCA_LOG_ERR("Failed to get the context user data: %s", doca_error_get_descr(res));
81  return;
82  }
83 
84  resources = (struct devemu_resources *)ctx_user_data.ptr;
86  config->bar_id,
87  config->start_address,
88  resources->stateful_region_values,
89  config->size);
90  if (res != DOCA_SUCCESS) {
91  DOCA_LOG_ERR("Failed to query values of stateful region: %s", doca_error_get_descr(res));
92  return;
93  }
94 
95  char *dump = hex_dump(resources->stateful_region_values, config->size);
96  if (dump == NULL) {
97  DOCA_LOG_ERR("Failed to dump values of stateful region: Memory allocation failure");
98  return;
99  }
100 
101  DOCA_LOG_INFO("Printing values of stateful region [bar_id=%u, start_address=%lu, size=%lu]\n%s",
102  config->bar_id,
103  config->start_address,
104  config->size,
105  dump);
106 
107  free(dump);
108 }
109 
110 /*
111  * Register to the stateful region write event of the emulated device for all stateful regions of configured type
112  * After this the sample will be able to receive notification once the host writes to the stateful regions through the
113  * bar of the emulated device.
114  *
115  * @pci_dev [in]: The emulated device context
116  * @resources [in]: Sample resources, will be associated with context as user_data that can be fetched from context
117  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
118  */
120  struct devemu_resources *resources)
121 {
122  const struct bar_region_config *config;
123  uint64_t region_idx;
124  union doca_data user_data;
125  doca_error_t res;
126  uint64_t max_region_size = 0;
127 
128  for (region_idx = 0; region_idx < PCI_TYPE_NUM_BAR_STATEFUL_REGIONS; region_idx++) {
129  config = &stateful_configs[region_idx];
130  user_data.ptr = (void *)config;
132  pci_dev,
134  config->bar_id,
135  config->start_address,
136  user_data);
137  if (res != DOCA_SUCCESS) {
138  DOCA_LOG_ERR("Unable to register to emulated PCI device stateful region write event: %s",
139  doca_error_get_descr(res));
140  return res;
141  }
142 
143  max_region_size = max_region_size > config->size ? max_region_size : config->size;
144  }
145 
146  user_data.ptr = (void *)resources;
147  res = doca_ctx_set_user_data(doca_devemu_pci_dev_as_ctx(pci_dev), user_data);
148  if (res != DOCA_SUCCESS) {
149  DOCA_LOG_ERR("Unable to set context user data: %s", doca_error_get_descr(res));
150  return res;
151  }
152 
153  /* Setup a buffer that can be used to query stateful region values once event is triggered */
154  resources->stateful_region_values = calloc(1, max_region_size);
155  if (resources->stateful_region_values == NULL) {
156  DOCA_LOG_ERR("Unable to allocate buffer for storing stateful region values: out of memory");
157  return DOCA_ERROR_NO_MEMORY;
158  }
159 
160  return DOCA_SUCCESS;
161 }
162 
163 /*
164  * Run DOCA Device Emulation Stateful Region DPU sample
165  *
166  * @pci_address [in]: Device PCI address
167  * @emulated_dev_vuid [in]: VUID of the emulated device
168  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
169  */
170 doca_error_t devemu_pci_device_stateful_region_dpu(const char *pci_address, const char *emulated_dev_vuid)
171 {
173  struct timespec ts = {
174  .tv_sec = 0,
175  .tv_nsec = SLEEP_IN_NANOS,
176  };
177  struct devemu_resources resources = {0};
178  const char pci_type_name[DOCA_DEVEMU_PCI_TYPE_NAME_LEN] = PCI_TYPE_NAME;
179  bool destroy_rep = false;
180 
181  /* Signal the while loop to stop */
182  force_quit = false;
183  signal(SIGINT, signal_handler);
184  signal(SIGTERM, signal_handler);
185 
187  DOCA_LOG_ERR(
188  "No stateful region was configured for type. Please configure at least 1 stateful region to run this sample");
190  }
191 
193  if (result != DOCA_SUCCESS) {
194  DOCA_LOG_ERR("Unable to create progress engine: %s", doca_error_get_descr(result));
195  return result;
196  }
197 
198  result = doca_devemu_pci_type_create(pci_type_name, &resources.pci_type);
199  if (result != DOCA_SUCCESS) {
200  DOCA_LOG_ERR("Unable to create PCI type: %s", doca_error_get_descr(result));
201  devemu_resources_cleanup(&resources, destroy_rep);
202  return result;
203  }
204 
205  result = find_supported_device(pci_address,
206  resources.pci_type,
208  &resources.dev);
209  if (result != DOCA_SUCCESS) {
210  devemu_resources_cleanup(&resources, destroy_rep);
211  return result;
212  }
213 
214  /* Set PCIe configuration space values */
216  if (result != DOCA_SUCCESS) {
217  devemu_resources_cleanup(&resources, destroy_rep);
218  return result;
219  }
220 
221  /* Find existing emulated device */
222  result = find_emulated_device(resources.pci_type, emulated_dev_vuid, &resources.rep);
223  if (result != DOCA_SUCCESS) {
224  DOCA_LOG_ERR("Unable to find PCI emulated device representor: %s", doca_error_get_descr(result));
225  devemu_resources_cleanup(&resources, destroy_rep);
226  return result;
227  }
228 
229  /* Create emulated device context */
231  if (result != DOCA_SUCCESS) {
232  DOCA_LOG_ERR("Unable to create PCI emulated device context: %s", doca_error_get_descr(result));
233  devemu_resources_cleanup(&resources, destroy_rep);
234  return result;
235  }
236 
237  /* Register callback to be triggered once host writes to stateful regions */
239  if (result != DOCA_SUCCESS) {
240  devemu_resources_cleanup(&resources, destroy_rep);
241  return result;
242  }
243 
245  if (result != DOCA_SUCCESS) {
246  DOCA_LOG_ERR("Unable to start PCI emulated device context: %s", doca_error_get_descr(result));
247  devemu_resources_cleanup(&resources, destroy_rep);
248  return result;
249  }
250 
251  /* Defer assignment so that cleanup does not stop the context in case it was not started */
253 
255  if (result != DOCA_SUCCESS) {
256  DOCA_LOG_ERR("Unable to get hotplug state: %s", doca_error_get_descr(result));
257  devemu_resources_cleanup(&resources, destroy_rep);
258  return result;
259  }
260 
261  if (resources.hotplug_state != DOCA_DEVEMU_PCI_HP_STATE_POWER_ON) {
262  DOCA_LOG_ERR(
263  "Expected hotplug state to be DOCA_DEVEMU_PCI_HP_STATE_POWER_ON instead current state is %s",
264  hotplug_state_to_string(resources.hotplug_state));
265  devemu_resources_cleanup(&resources, destroy_rep);
266  return DOCA_ERROR_BAD_STATE;
267  }
268 
269  DOCA_LOG_INFO("Press ([ctrl] + c) to stop sample");
270 
271  /* Listen to any writes to the emulated device's stateful regions */
272  while (!force_quit) {
273  if (doca_pe_progress(resources.pe) == 0)
274  nanosleep(&ts, &ts);
275  }
276 
277  /* Clean and destroy all relevant objects */
278  devemu_resources_cleanup(&resources, destroy_rep);
279 
280  return result;
281 }
#define NULL
Definition: __stddef_null.h:26
int32_t result
#define SLEEP_IN_NANOS
Definition: comch_utils.c:40
void devemu_resources_cleanup(struct devemu_resources *resources, bool destroy_rep)
static void stateful_region_write_event_handler_cb(struct doca_devemu_pci_dev_event_bar_stateful_region_driver_write *event, union doca_data user_data)
DOCA_LOG_REGISTER(DEVEMU_PCI_DEVICE_STATEFUL_REGION_DPU)
doca_error_t devemu_pci_device_stateful_region_dpu(const char *pci_address, const char *emulated_dev_vuid)
static doca_error_t register_to_stateful_region_write_events(struct doca_devemu_pci_dev *pci_dev, struct devemu_resources *resources)
static void signal_handler(int signum)
#define PCI_TYPE_NAME
struct rdma_resources resources
DOCA_STABLE doca_error_t doca_ctx_start(struct doca_ctx *ctx)
Finalizes all configurations, and starts the DOCA CTX.
DOCA_STABLE doca_error_t doca_ctx_set_user_data(struct doca_ctx *ctx, union doca_data user_data)
set user data to context
DOCA_STABLE doca_error_t doca_ctx_get_user_data(const struct doca_ctx *ctx, union doca_data *user_data)
get user data from context
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_type_create(const char *name, struct doca_devemu_pci_type **pci_type)
Create a stopped DOCA devemu PCI type.
#define DOCA_DEVEMU_PCI_TYPE_NAME_LEN
Maximal length for the NULL terminated string that describe the name of the emulated PCI device type.
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_cap_type_is_hotplug_supported(const struct doca_devinfo *devinfo, const struct doca_devemu_pci_type *pci_type, uint8_t *supported)
Get the hotplug capability of the device for a given DOCA devemu PCI type.
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_get_hotplug_state(struct doca_devemu_pci_dev *pci_dev, enum doca_devemu_pci_hotplug_state *state)
Get the hotplug state of the DOCA devemu PCI device.
DOCA_EXPERIMENTAL struct doca_devemu_pci_dev * doca_devemu_pci_dev_event_bar_stateful_region_driver_write_get_pci_dev(struct doca_devemu_pci_dev_event_bar_stateful_region_driver_write *event)
Get DOCA devemu PCI device from BAR stateful region driver write event.
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_create(struct doca_devemu_pci_type *pci_type, struct doca_dev_rep *dev_rep, struct doca_pe *progress_engine, struct doca_devemu_pci_dev **pci_dev)
Allocate DOCA devemu PCI device.
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_query_bar_stateful_region_values(struct doca_devemu_pci_dev *pci_dev, uint8_t id, uint64_t offset, void *out_values, uint64_t size)
Query registers values of the stateful region in a DOCA devemu PCI device.
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_event_bar_stateful_region_driver_write_register(struct doca_devemu_pci_dev *pci_dev, doca_devemu_pci_dev_event_bar_stateful_region_driver_write_handler_cb_t handler, uint8_t bar_id, uint64_t bar_region_start_addr, union doca_data user_data)
Register to BAR stateful region driver write event.
DOCA_EXPERIMENTAL struct doca_ctx * doca_devemu_pci_dev_as_ctx(struct doca_devemu_pci_dev *pci_dev)
Convert DOCA devemu PCI device instance into DOCA context.
@ DOCA_DEVEMU_PCI_HP_STATE_POWER_ON
enum doca_error doca_error_t
DOCA API return codes.
DOCA_STABLE const char * doca_error_get_descr(doca_error_t error)
Returns the description string of an error code.
@ DOCA_ERROR_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_ERROR_BAD_STATE
Definition: doca_error.h:56
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_NO_MEMORY
Definition: doca_error.h:45
#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
DOCA_STABLE uint8_t doca_pe_progress(struct doca_pe *pe)
Run the progress engine.
DOCA_STABLE doca_error_t doca_pe_create(struct doca_pe **pe)
Creates DOCA progress engine.
char * hex_dump(const void *data, size_t size)
doca_error_t configure_and_start_pci_type(struct doca_devemu_pci_type *pci_type, struct doca_dev *dev)
const char * hotplug_state_to_string(enum doca_devemu_pci_hotplug_state hotplug_state)
doca_error_t find_emulated_device(struct doca_devemu_pci_type *pci_type, const char *vuid, struct doca_dev_rep **rep)
doca_error_t find_supported_device(const char *dev_name, const struct doca_devemu_pci_type *pci_type, emulation_supported_cb_t has_support, struct doca_dev **dev)
#define PCI_TYPE_NUM_BAR_STATEFUL_REGIONS
static const struct bar_region_config stateful_configs[PCI_TYPE_NUM_BAR_STATEFUL_REGIONS]
struct doca_devemu_pci_dev * pci_dev
struct doca_pe * pe
Definition: rdma_common.h:86
Convenience type for representing opaque data.
Definition: doca_types.h:56
void * ptr
Definition: doca_types.h:57