NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
devemu_pci_device_hotplug_sample.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023-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 
40 DOCA_LOG_REGISTER(DPU_DEVEMU_PCI_DEVICE_HOTPLUG);
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  * Callback that is triggered everytime the hotplug state is changed for the associated emulated PCI device
59  *
60  * @pci_dev [in]: The associated emulated PCI device
61  * @user_data [in]: The user data that was previously provided along with callback
62  */
63 static void hotplug_event_handler_cb(struct doca_devemu_pci_dev *pci_dev, union doca_data user_data)
64 {
65  enum doca_devemu_pci_hotplug_state hotplug_state;
66  doca_error_t res;
67  struct devemu_resources *resources = (struct devemu_resources *)user_data.ptr;
68 
69  DOCA_LOG_INFO("Emulated device's hotplug state has changed");
70 
72  if (res != DOCA_SUCCESS) {
73  DOCA_LOG_ERR("Unable to get hotplug state: %s", doca_error_get_descr(res));
74  return;
75  }
76 
77  resources->hotplug_state = hotplug_state;
78 
79  DOCA_LOG_INFO("Hotplug state changed to %s", hotplug_state_to_string(hotplug_state));
80 }
81 
82 /*
83  * Register to the hotplug state change event of the emulated device
84  * After this the sample will be able to receive notification once the hotplug state of the emulated device
85  * has been changed. For possible states check out enum doca_devemu_pci_hotplug_state
86  *
87  * @pci_dev [in]: The emulated device context
88  * @cookie [in]: User data that is associated with the event, can be retrieved from event once callback is triggered
89  * @event [out]: The newly registered event object
90  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
91  */
92 static doca_error_t register_to_hotplug_state_change_events(struct doca_devemu_pci_dev *pci_dev, void *cookie)
93 {
94  union doca_data user_data;
95  doca_error_t res;
96 
97  user_data.ptr = cookie;
99  if (res != DOCA_SUCCESS) {
100  DOCA_LOG_ERR("Unable to allocate emulated PCI device hotplug state change event : %s",
101  doca_error_get_descr(res));
102  return res;
103  }
104 
105  return DOCA_SUCCESS;
106 }
107 
108 /*
109  * Run DOCA Device Emulation Hotplug sample
110  *
111  * @pci_address [in]: Device PCI address
112  * @unplug_vuid [in]: VUID of emulated device to unplug. Can be NULL
113  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
114  */
115 doca_error_t devemu_pci_device_hotplug(const char *pci_address, const char *unplug_vuid)
116 {
117  char rep_vuid[DOCA_DEVINFO_REP_VUID_SIZE];
119  struct timespec ts = {
120  .tv_sec = 0,
121  .tv_nsec = SLEEP_IN_NANOS,
122  };
123  struct devemu_resources resources = {0};
124  const char pci_type_name[DOCA_DEVEMU_PCI_TYPE_NAME_LEN] = PCI_TYPE_NAME;
125  bool destroy_rep = false;
126  enum doca_devemu_pci_hotplug_state expected_hotplug_state;
127 
128  /* Signal the while loop to stop */
129  force_quit = false;
130  signal(SIGINT, signal_handler);
131  signal(SIGTERM, signal_handler);
132 
134  if (result != DOCA_SUCCESS) {
135  DOCA_LOG_ERR("Unable to create progress engine: %s", doca_error_get_descr(result));
136  return result;
137  }
138 
139  result = doca_devemu_pci_type_create(pci_type_name, &resources.pci_type);
140  if (result != DOCA_SUCCESS) {
141  DOCA_LOG_ERR("Unable to create PCI type: %s", doca_error_get_descr(result));
142  devemu_resources_cleanup(&resources, destroy_rep);
143  return result;
144  }
145 
146  result = find_supported_device(pci_address,
147  resources.pci_type,
149  &resources.dev);
150  if (result != DOCA_SUCCESS) {
151  devemu_resources_cleanup(&resources, destroy_rep);
152  return result;
153  }
154 
155  /* Set PCIe configuration space values */
157  if (result != DOCA_SUCCESS) {
158  devemu_resources_cleanup(&resources, destroy_rep);
159  return result;
160  }
161 
162  /* If unplug address was given then sample will hotunplug device */
163  if (unplug_vuid != NULL) {
164  /* Find existing emulated device */
165  result = find_emulated_device(resources.pci_type, unplug_vuid, &resources.rep);
166  if (result != DOCA_SUCCESS) {
167  DOCA_LOG_ERR("Unable to find PCI emulated device representor: %s",
169  devemu_resources_cleanup(&resources, destroy_rep);
170  return result;
171  }
172  } else {
173  /* Prepare emulated device before plugging it towards the host */
175  if (result != DOCA_SUCCESS) {
176  DOCA_LOG_ERR("Unable to create PCI emulated device representor: %s",
178  devemu_resources_cleanup(&resources, destroy_rep);
179  return result;
180  }
181 
182  /* Print VUID of new device */
184  rep_vuid,
186  DOCA_LOG_INFO("The new emulated device VUID: %s",
187  result == DOCA_SUCCESS ? rep_vuid : doca_error_get_name(result));
188  }
189 
190  /* Create emulated device context */
192  if (result != DOCA_SUCCESS) {
193  DOCA_LOG_ERR("Unable to create PCI emulated device context: %s", doca_error_get_descr(result));
194  devemu_resources_cleanup(&resources, destroy_rep);
195  return result;
196  }
197 
198  /* Register callback to be triggered once hotplug state changes */
200  if (result != DOCA_SUCCESS) {
201  devemu_resources_cleanup(&resources, destroy_rep);
202  return result;
203  }
204 
206  if (result != DOCA_SUCCESS) {
207  DOCA_LOG_ERR("Unable to start PCI emulated device context: %s", doca_error_get_descr(result));
208  devemu_resources_cleanup(&resources, destroy_rep);
209  return result;
210  }
211 
212  /* Defer assignment so that cleanup does not stop the context in case it was not started */
214 
216  if (result != DOCA_SUCCESS) {
217  DOCA_LOG_ERR("Unable to get hotplug state: %s", doca_error_get_descr(result));
218  devemu_resources_cleanup(&resources, destroy_rep);
219  return result;
220  }
221 
222  DOCA_LOG_INFO("Current hotplug state of emulated device is %s",
223  hotplug_state_to_string(resources.hotplug_state));
224 
225  if (unplug_vuid != NULL) {
227  expected_hotplug_state = DOCA_DEVEMU_PCI_HP_STATE_POWER_OFF;
228  DOCA_LOG_INFO("Hotunplug initiated waiting for host to release device");
229  } else {
231  expected_hotplug_state = DOCA_DEVEMU_PCI_HP_STATE_POWER_ON;
232  DOCA_LOG_INFO("Hotplug initiated waiting for host to notice new device");
233  }
234  if (result != DOCA_SUCCESS) {
235  devemu_resources_cleanup(&resources, destroy_rep);
236  return result;
237  }
238 
239  DOCA_LOG_INFO("Press ([ctrl] + c) to force quit");
240 
241  /* Wait for emulated device to be hotplugged */
242  while (resources.hotplug_state != expected_hotplug_state && !force_quit) {
243  if (doca_pe_progress(resources.pe) == 0)
244  nanosleep(&ts, &ts);
245  }
246 
247  destroy_rep = (resources.hotplug_state == DOCA_DEVEMU_PCI_HP_STATE_POWER_OFF);
248 
249  /* Clean and destroy all relevant objects */
250  devemu_resources_cleanup(&resources, destroy_rep);
251 
252  return result;
253 }
#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 doca_error_t register_to_hotplug_state_change_events(struct doca_devemu_pci_dev *pci_dev, void *cookie)
doca_error_t devemu_pci_device_hotplug(const char *pci_address, const char *unplug_vuid)
DOCA_LOG_REGISTER(DPU_DEVEMU_PCI_DEVICE_HOTPLUG)
static void hotplug_event_handler_cb(struct doca_devemu_pci_dev *pci_dev, union doca_data user_data)
static bool force_quit
static void signal_handler(int signum)
#define PCI_TYPE_NAME
uint64_t cookie
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_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_create_rep(const struct doca_devemu_pci_type *pci_type, struct doca_dev_rep **dev_rep)
Create a new representor device for a given DOCA devemu PCI type.
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_hotplug(struct doca_devemu_pci_dev *pci_dev)
Issue hotplug procedure of the DOCA devemu PCI device.
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 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_devemu_pci_hotplug_state
DOCA devemu pci hotplug state.
DOCA_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_event_hotplug_state_change_register(struct doca_devemu_pci_dev *pci_dev, doca_devemu_pci_dev_event_hotplug_state_change_handler_cb_t handler, union doca_data user_data)
Register to hotplug state changes.
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_EXPERIMENTAL doca_error_t doca_devemu_pci_dev_hotunplug(struct doca_devemu_pci_dev *pci_dev)
Issue hot unplug procedure of the DOCA devemu PCI device.
@ DOCA_DEVEMU_PCI_HP_STATE_POWER_OFF
@ DOCA_DEVEMU_PCI_HP_STATE_POWER_ON
DOCA_STABLE doca_error_t doca_devinfo_rep_get_vuid(const struct doca_devinfo_rep *devinfo_rep, char *rep_vuid, uint32_t size)
Get the Vendor Unique ID of a representor DOCA devinfo.
#define DOCA_DEVINFO_REP_VUID_SIZE
Buffer size to hold VUID. Including a null terminator.
Definition: doca_dev.h:661
DOCA_STABLE struct doca_devinfo_rep * doca_dev_rep_as_devinfo(struct doca_dev_rep *dev_rep)
Get representor device info from device. This should be useful when wanting to query information abou...
enum doca_error doca_error_t
DOCA API return codes.
DOCA_STABLE const char * doca_error_get_name(doca_error_t error)
Returns the string representation of an error code name.
DOCA_STABLE const char * doca_error_get_descr(doca_error_t error)
Returns the description string of an error code.
@ DOCA_SUCCESS
Definition: doca_error.h:38
#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.
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)
struct doca_devemu_pci_dev * pci_dev
enum doca_devemu_pci_hotplug_state hotplug_state
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