Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2022 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk/env.h"
8 : : #include "spdk/thread.h"
9 : : #include "spdk/log.h"
10 : : #include "spdk/util.h"
11 : : #include "spdk/memory.h"
12 : : #include "spdk/cpuset.h"
13 : : #include "spdk/likely.h"
14 : : #include "spdk/vfu_target.h"
15 : :
16 : : #include "tgt_internal.h"
17 : :
18 : : struct tgt_pci_device_ops {
19 : : struct spdk_vfu_endpoint_ops ops;
20 : : TAILQ_ENTRY(tgt_pci_device_ops) link;
21 : : };
22 : :
23 : : static struct spdk_cpuset g_tgt_core_mask;
24 : : static pthread_mutex_t g_endpoint_lock = PTHREAD_MUTEX_INITIALIZER;
25 : : static TAILQ_HEAD(, spdk_vfu_endpoint) g_endpoint = TAILQ_HEAD_INITIALIZER(g_endpoint);
26 : : static TAILQ_HEAD(, tgt_pci_device_ops) g_pci_device_ops = TAILQ_HEAD_INITIALIZER(g_pci_device_ops);
27 : : static char g_endpoint_path_dirname[PATH_MAX] = "";
28 : :
29 : : static struct spdk_vfu_endpoint_ops *
30 : 408 : tgt_get_pci_device_ops(const char *device_type_name)
31 : : {
32 : : struct tgt_pci_device_ops *pci_ops, *tmp;
33 : 408 : bool exist = false;
34 : :
35 : 408 : pthread_mutex_lock(&g_endpoint_lock);
36 [ + + ]: 612 : TAILQ_FOREACH_SAFE(pci_ops, &g_pci_device_ops, link, tmp) {
37 [ - + - + : 208 : if (!strncmp(device_type_name, pci_ops->ops.name, SPDK_VFU_MAX_NAME_LEN)) {
+ + ]
38 : 4 : exist = true;
39 : 4 : break;
40 : : }
41 : : }
42 : 408 : pthread_mutex_unlock(&g_endpoint_lock);
43 : :
44 [ + + ]: 408 : if (exist) {
45 : 4 : return &pci_ops->ops;
46 : : }
47 : 404 : return NULL;
48 : : }
49 : :
50 : : int
51 : 404 : spdk_vfu_register_endpoint_ops(struct spdk_vfu_endpoint_ops *ops)
52 : : {
53 : : struct tgt_pci_device_ops *pci_ops;
54 : : struct spdk_vfu_endpoint_ops *tmp;
55 : :
56 : 404 : tmp = tgt_get_pci_device_ops(ops->name);
57 [ - + ]: 404 : if (tmp) {
58 : 0 : return -EEXIST;
59 : : }
60 : :
61 : 404 : pci_ops = calloc(1, sizeof(*pci_ops));
62 [ - + ]: 404 : if (!pci_ops) {
63 : 0 : return -ENOMEM;
64 : : }
65 : 404 : pci_ops->ops = *ops;
66 : :
67 [ - + ]: 404 : pthread_mutex_lock(&g_endpoint_lock);
68 : 404 : TAILQ_INSERT_TAIL(&g_pci_device_ops, pci_ops, link);
69 [ - + ]: 404 : pthread_mutex_unlock(&g_endpoint_lock);
70 : :
71 : 404 : return 0;
72 : : }
73 : :
74 : : static char *
75 : 4 : tgt_get_base_path(void)
76 : : {
77 : 4 : return g_endpoint_path_dirname;
78 : : }
79 : :
80 : : int
81 : 3 : spdk_vfu_set_socket_path(const char *basename)
82 : : {
83 : : int ret;
84 : :
85 [ + - + - ]: 3 : if (basename && strlen(basename) > 0) {
86 [ - + ]: 3 : ret = snprintf(g_endpoint_path_dirname, sizeof(g_endpoint_path_dirname) - 2, "%s", basename);
87 [ - + ]: 3 : if (ret <= 0) {
88 : 0 : return -EINVAL;
89 : : }
90 [ - + ]: 3 : if ((size_t)ret >= sizeof(g_endpoint_path_dirname) - 2) {
91 : 0 : SPDK_ERRLOG("Char dev dir path length %d is too long\n", ret);
92 : 0 : return -EINVAL;
93 : : }
94 : :
95 [ + - ]: 3 : if (g_endpoint_path_dirname[ret - 1] != '/') {
96 : 3 : g_endpoint_path_dirname[ret] = '/';
97 : 3 : g_endpoint_path_dirname[ret + 1] = '\0';
98 : : }
99 : : }
100 : :
101 : 3 : return 0;
102 : : }
103 : :
104 : : struct spdk_vfu_endpoint *
105 : 13 : spdk_vfu_get_endpoint_by_name(const char *name)
106 : : {
107 : : struct spdk_vfu_endpoint *endpoint, *tmp;
108 : 13 : bool exist = false;
109 : :
110 : 13 : pthread_mutex_lock(&g_endpoint_lock);
111 [ + + ]: 17 : TAILQ_FOREACH_SAFE(endpoint, &g_endpoint, link, tmp) {
112 [ - + - + : 13 : if (!strncmp(name, endpoint->name, SPDK_VFU_MAX_NAME_LEN)) {
+ + ]
113 : 9 : exist = true;
114 : 9 : break;
115 : : }
116 : : }
117 : 13 : pthread_mutex_unlock(&g_endpoint_lock);
118 : :
119 [ + + ]: 13 : if (exist) {
120 : 9 : return endpoint;
121 : : }
122 : 4 : return NULL;
123 : : }
124 : :
125 : : static int
126 : 170331 : tgt_vfu_ctx_poller(void *ctx)
127 : : {
128 : 170331 : struct spdk_vfu_endpoint *endpoint = ctx;
129 : 170331 : vfu_ctx_t *vfu_ctx = endpoint->vfu_ctx;
130 : : int ret;
131 : :
132 : 170331 : ret = vfu_run_ctx(vfu_ctx);
133 [ + + ]: 170331 : if (spdk_unlikely(ret == -1)) {
134 [ - + ]: 6 : if (errno == EBUSY) {
135 : 0 : return SPDK_POLLER_IDLE;
136 : : }
137 : :
138 [ + - ]: 6 : if (errno == ENOTCONN) {
139 : 6 : spdk_poller_unregister(&endpoint->vfu_ctx_poller);
140 [ + - ]: 6 : if (endpoint->ops.detach_device) {
141 : 6 : endpoint->ops.detach_device(endpoint);
142 : : }
143 : 6 : endpoint->is_attached = false;
144 : 6 : return SPDK_POLLER_BUSY;
145 : : }
146 : : }
147 : :
148 : 170325 : return ret != 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
149 : : }
150 : :
151 : : static int
152 : 187298 : tgt_accept_poller(void *ctx)
153 : : {
154 : 187298 : struct spdk_vfu_endpoint *endpoint = ctx;
155 : : int ret;
156 : :
157 [ - + + + ]: 187298 : if (endpoint->is_attached) {
158 : 170331 : return SPDK_POLLER_IDLE;
159 : : }
160 : :
161 : 16967 : ret = vfu_attach_ctx(endpoint->vfu_ctx);
162 [ + + ]: 16967 : if (ret == 0) {
163 : 6 : ret = endpoint->ops.attach_device(endpoint);
164 [ + - ]: 6 : if (!ret) {
165 : 6 : SPDK_NOTICELOG("%s: attached successfully\n", spdk_vfu_get_endpoint_id(endpoint));
166 : : /* Polling socket too frequently will cause performance issue */
167 : 6 : endpoint->vfu_ctx_poller = SPDK_POLLER_REGISTER(tgt_vfu_ctx_poller, endpoint, 1000);
168 : 6 : endpoint->is_attached = true;
169 : : }
170 : 6 : return SPDK_POLLER_BUSY;
171 : : }
172 : :
173 [ - + - - ]: 16961 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
174 : 16961 : return SPDK_POLLER_IDLE;
175 : : }
176 : :
177 : 0 : return SPDK_POLLER_BUSY;
178 : : }
179 : :
180 : : static void
181 : 0 : tgt_log_cb(vfu_ctx_t *vfu_ctx, int level, char const *msg)
182 : : {
183 : 0 : struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
184 : :
185 [ # # ]: 0 : if (level >= LOG_DEBUG) {
186 [ # # # # ]: 0 : SPDK_DEBUGLOG(vfu, "%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
187 [ # # ]: 0 : } else if (level >= LOG_INFO) {
188 [ # # # # ]: 0 : SPDK_INFOLOG(vfu, "%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
189 [ # # ]: 0 : } else if (level >= LOG_NOTICE) {
190 : 0 : SPDK_NOTICELOG("%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
191 [ # # ]: 0 : } else if (level >= LOG_WARNING) {
192 : 0 : SPDK_WARNLOG("%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
193 : : } else {
194 : 0 : SPDK_ERRLOG("%s: %s\n", spdk_vfu_get_endpoint_id(endpoint), msg);
195 : : }
196 : 0 : }
197 : :
198 : : static int
199 : 4 : tgt_get_log_level(void)
200 : : {
201 : : int level;
202 : :
203 [ - + ]: 4 : if (SPDK_DEBUGLOG_FLAG_ENABLED("vfu")) {
204 : 0 : return LOG_DEBUG;
205 : : }
206 : :
207 : 4 : level = spdk_log_to_syslog_level(spdk_log_get_level());
208 [ - + ]: 4 : if (level < 0) {
209 : 0 : return LOG_ERR;
210 : : }
211 : :
212 : 4 : return level;
213 : : }
214 : :
215 : : static void
216 : 4 : init_pci_config_space(vfu_pci_config_space_t *p, uint16_t ipin)
217 : : {
218 : : /* MLBAR */
219 : 4 : p->hdr.bars[0].raw = 0x0;
220 : : /* MUBAR */
221 : 4 : p->hdr.bars[1].raw = 0x0;
222 : :
223 : : /* vendor specific, let's set them to zero for now */
224 : 4 : p->hdr.bars[3].raw = 0x0;
225 : 4 : p->hdr.bars[4].raw = 0x0;
226 : 4 : p->hdr.bars[5].raw = 0x0;
227 : :
228 : : /* enable INTx */
229 : 4 : p->hdr.intr.ipin = ipin;
230 : 4 : }
231 : :
232 : : static void
233 : 162 : tgt_memory_region_add_cb(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
234 : : {
235 : 162 : struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
236 : : void *map_start, *map_end;
237 : : int ret;
238 : :
239 [ + + ]: 162 : if (!info->vaddr) {
240 : 96 : return;
241 : : }
242 : :
243 : 66 : map_start = info->mapping.iov_base;
244 : 66 : map_end = info->mapping.iov_base + info->mapping.iov_len;
245 : :
246 [ + - ]: 66 : if (((uintptr_t)info->mapping.iov_base & MASK_2MB) ||
247 [ - + ]: 66 : (info->mapping.iov_len & MASK_2MB)) {
248 [ # # # # ]: 0 : SPDK_DEBUGLOG(vfu, "Invalid memory region vaddr %p, IOVA %p-%p\n",
249 : : info->vaddr, map_start, map_end);
250 : 0 : return;
251 : : }
252 : :
253 [ + + ]: 66 : if (info->prot == (PROT_WRITE | PROT_READ)) {
254 : 50 : ret = spdk_mem_register(info->mapping.iov_base, info->mapping.iov_len);
255 [ - + ]: 50 : if (ret) {
256 : 0 : SPDK_ERRLOG("Memory region register %p-%p failed, ret=%d\n",
257 : : map_start, map_end, ret);
258 : : }
259 : : }
260 : :
261 [ + - ]: 66 : if (endpoint->ops.post_memory_add) {
262 : 66 : endpoint->ops.post_memory_add(endpoint, map_start, map_end);
263 : : }
264 : : }
265 : :
266 : : static void
267 : 162 : tgt_memory_region_remove_cb(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
268 : : {
269 : 162 : struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
270 : : void *map_start, *map_end;
271 : 162 : int ret = 0;
272 : :
273 [ + + ]: 162 : if (!info->vaddr) {
274 : 96 : return;
275 : : }
276 : :
277 : 66 : map_start = info->mapping.iov_base;
278 : 66 : map_end = info->mapping.iov_base + info->mapping.iov_len;
279 : :
280 [ + - ]: 66 : if (((uintptr_t)info->mapping.iov_base & MASK_2MB) ||
281 [ - + ]: 66 : (info->mapping.iov_len & MASK_2MB)) {
282 [ # # # # ]: 0 : SPDK_DEBUGLOG(vfu, "Invalid memory region vaddr %p, IOVA %p-%p\n",
283 : : info->vaddr, map_start, map_end);
284 : 0 : return;
285 : : }
286 : :
287 [ + - ]: 66 : if (endpoint->ops.pre_memory_remove) {
288 : 66 : endpoint->ops.pre_memory_remove(endpoint, map_start, map_end);
289 : : }
290 : :
291 [ + + ]: 66 : if (info->prot == (PROT_WRITE | PROT_READ)) {
292 : 50 : ret = spdk_mem_unregister(info->mapping.iov_base, info->mapping.iov_len);
293 [ - + ]: 50 : if (ret) {
294 : 0 : SPDK_ERRLOG("Memory region unregister %p-%p failed, ret=%d\n",
295 : : map_start, map_end, ret);
296 : : }
297 : : }
298 : : }
299 : :
300 : : static int
301 : 290 : tgt_device_quiesce_cb(vfu_ctx_t *vfu_ctx)
302 : : {
303 : 290 : struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
304 : : int ret;
305 : :
306 [ - + ]: 290 : assert(endpoint->ops.quiesce_device);
307 : 290 : ret = endpoint->ops.quiesce_device(endpoint);
308 [ - + ]: 290 : if (ret) {
309 : 0 : errno = EBUSY;
310 : 0 : ret = -1;
311 : : }
312 : :
313 : 290 : return ret;
314 : : }
315 : :
316 : : static int
317 : 14 : tgt_device_reset_cb(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type)
318 : : {
319 : 14 : struct spdk_vfu_endpoint *endpoint = vfu_get_private(vfu_ctx);
320 : :
321 [ - + - + ]: 14 : SPDK_DEBUGLOG(vfu, "Device reset type %u\n", type);
322 : :
323 [ - + ]: 14 : assert(endpoint->ops.reset_device);
324 : 14 : return endpoint->ops.reset_device(endpoint);
325 : : }
326 : :
327 : : static int
328 : 4 : tgt_endpoint_realize(struct spdk_vfu_endpoint *endpoint)
329 : : {
330 : : int ret;
331 : 4 : uint8_t buf[512];
332 : : struct vsc *vendor_cap;
333 : : ssize_t cap_offset;
334 : : uint16_t vendor_cap_idx, cap_size, sparse_mmap_idx;
335 : 4 : struct spdk_vfu_pci_device pci_dev;
336 : : uint8_t region_idx;
337 : :
338 [ - + ]: 4 : assert(endpoint->ops.get_device_info);
339 : 4 : ret = endpoint->ops.get_device_info(endpoint, &pci_dev);
340 [ - + ]: 4 : if (ret) {
341 : 0 : SPDK_ERRLOG("%s: failed to get pci device info\n", spdk_vfu_get_endpoint_id(endpoint));
342 : 0 : return ret;
343 : : }
344 : :
345 : 4 : endpoint->vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, endpoint->uuid, LIBVFIO_USER_FLAG_ATTACH_NB,
346 : : endpoint, VFU_DEV_TYPE_PCI);
347 [ - + ]: 4 : if (endpoint->vfu_ctx == NULL) {
348 : 0 : SPDK_ERRLOG("%s: error creating libvfio-user context\n", spdk_vfu_get_endpoint_id(endpoint));
349 : 0 : return -EFAULT;
350 : : }
351 : 4 : vfu_setup_log(endpoint->vfu_ctx, tgt_log_cb, tgt_get_log_level());
352 : :
353 : 4 : ret = vfu_pci_init(endpoint->vfu_ctx, VFU_PCI_TYPE_EXPRESS, PCI_HEADER_TYPE_NORMAL, 0);
354 [ - + ]: 4 : if (ret < 0) {
355 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to initialize PCI\n", endpoint->vfu_ctx);
356 : 0 : goto error;
357 : : }
358 : :
359 : 4 : vfu_pci_set_id(endpoint->vfu_ctx, pci_dev.id.vid, pci_dev.id.did, pci_dev.id.ssvid,
360 : 4 : pci_dev.id.ssid);
361 : 4 : vfu_pci_set_class(endpoint->vfu_ctx, pci_dev.class.bcc, pci_dev.class.scc, pci_dev.class.pi);
362 : :
363 : : /* Add Vendor Capabilities */
364 [ + + ]: 20 : for (vendor_cap_idx = 0; vendor_cap_idx < pci_dev.nr_vendor_caps; vendor_cap_idx++) {
365 [ - + ]: 16 : memset(buf, 0, sizeof(buf));
366 : 16 : cap_size = endpoint->ops.get_vendor_capability(endpoint, buf, 256, vendor_cap_idx);
367 [ + - ]: 16 : if (cap_size) {
368 : 16 : vendor_cap = (struct vsc *)buf;
369 [ - + ]: 16 : assert(vendor_cap->hdr.id == PCI_CAP_ID_VNDR);
370 [ - + ]: 16 : assert(vendor_cap->size == cap_size);
371 : :
372 : 16 : cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, vendor_cap);
373 [ - + ]: 16 : if (cap_offset < 0) {
374 : 0 : SPDK_ERRLOG("vfu_ctx %p failed add vendor capability\n", endpoint->vfu_ctx);
375 : 0 : ret = -EFAULT;
376 : 0 : goto error;
377 : : }
378 : : }
379 : : }
380 : :
381 : : /* Add Standard PCI Capabilities */
382 : 4 : cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, &pci_dev.pmcap);
383 [ - + ]: 4 : if (cap_offset < 0) {
384 : 0 : SPDK_ERRLOG("vfu_ctx %p failed add pmcap\n", endpoint->vfu_ctx);
385 : 0 : ret = -EFAULT;
386 : 0 : goto error;
387 : : }
388 [ - + - + ]: 4 : SPDK_DEBUGLOG(vfu, "%s PM cap_offset %ld\n", spdk_vfu_get_endpoint_id(endpoint), cap_offset);
389 : :
390 : 4 : cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, &pci_dev.pxcap);
391 [ - + ]: 4 : if (cap_offset < 0) {
392 : 0 : SPDK_ERRLOG("vfu_ctx %p failed add pxcap\n", endpoint->vfu_ctx);
393 : 0 : ret = -EFAULT;
394 : 0 : goto error;
395 : : }
396 [ - + - + ]: 4 : SPDK_DEBUGLOG(vfu, "%s PX cap_offset %ld\n", spdk_vfu_get_endpoint_id(endpoint), cap_offset);
397 : :
398 : 4 : cap_offset = vfu_pci_add_capability(endpoint->vfu_ctx, 0, 0, &pci_dev.msixcap);
399 [ - + ]: 4 : if (cap_offset < 0) {
400 : 0 : SPDK_ERRLOG("vfu_ctx %p failed add msixcap\n", endpoint->vfu_ctx);
401 : 0 : ret = -EFAULT;
402 : 0 : goto error;
403 : : }
404 [ - + - + ]: 4 : SPDK_DEBUGLOG(vfu, "%s MSIX cap_offset %ld\n", spdk_vfu_get_endpoint_id(endpoint), cap_offset);
405 : :
406 : : /* Setup PCI Regions */
407 [ + + ]: 44 : for (region_idx = 0; region_idx < VFU_PCI_DEV_NUM_REGIONS; region_idx++) {
408 : 40 : struct spdk_vfu_pci_region *region = &pci_dev.regions[region_idx];
409 : 40 : struct iovec sparse_mmap[SPDK_VFU_MAXIMUM_SPARSE_MMAP_REGIONS];
410 [ + + ]: 40 : if (!region->len) {
411 : 24 : continue;
412 : : }
413 : :
414 [ + + ]: 16 : if (region->nr_sparse_mmaps) {
415 [ - + ]: 4 : assert(region->nr_sparse_mmaps <= SPDK_VFU_MAXIMUM_SPARSE_MMAP_REGIONS);
416 [ + + ]: 8 : for (sparse_mmap_idx = 0; sparse_mmap_idx < region->nr_sparse_mmaps; sparse_mmap_idx++) {
417 : 4 : sparse_mmap[sparse_mmap_idx].iov_base = (void *)region->mmaps[sparse_mmap_idx].offset;
418 : 4 : sparse_mmap[sparse_mmap_idx].iov_len = region->mmaps[sparse_mmap_idx].len;
419 : : }
420 : : }
421 : :
422 : 32 : ret = vfu_setup_region(endpoint->vfu_ctx, region_idx, region->len, region->access_cb, region->flags,
423 [ + + ]: 16 : region->nr_sparse_mmaps ? sparse_mmap : NULL, region->nr_sparse_mmaps,
424 : : region->fd, region->offset);
425 [ - + ]: 16 : if (ret) {
426 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to setup region %u\n", endpoint->vfu_ctx, region_idx);
427 : 0 : goto error;
428 : : }
429 [ - + - + ]: 16 : SPDK_DEBUGLOG(vfu, "%s: region %u, len 0x%"PRIx64", callback %p, nr sparse mmaps %u, fd %d\n",
430 : : spdk_vfu_get_endpoint_id(endpoint), region_idx, region->len, region->access_cb,
431 : : region->nr_sparse_mmaps, region->fd);
432 : : }
433 : :
434 : 4 : ret = vfu_setup_device_dma(endpoint->vfu_ctx, tgt_memory_region_add_cb,
435 : : tgt_memory_region_remove_cb);
436 [ - + ]: 4 : if (ret < 0) {
437 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to setup dma callback\n", endpoint->vfu_ctx);
438 : 0 : goto error;
439 : : }
440 : :
441 [ + - ]: 4 : if (endpoint->ops.reset_device) {
442 : 4 : ret = vfu_setup_device_reset_cb(endpoint->vfu_ctx, tgt_device_reset_cb);
443 [ - + ]: 4 : if (ret < 0) {
444 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to setup reset callback\n", endpoint->vfu_ctx);
445 : 0 : goto error;
446 : : }
447 : : }
448 : :
449 [ + - ]: 4 : if (endpoint->ops.quiesce_device) {
450 : 4 : vfu_setup_device_quiesce_cb(endpoint->vfu_ctx, tgt_device_quiesce_cb);
451 : : }
452 : :
453 : 4 : ret = vfu_setup_device_nr_irqs(endpoint->vfu_ctx, VFU_DEV_INTX_IRQ, pci_dev.nr_int_irqs);
454 [ - + ]: 4 : if (ret < 0) {
455 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to setup INTX\n", endpoint->vfu_ctx);
456 : 0 : goto error;
457 : : }
458 : :
459 : 4 : ret = vfu_setup_device_nr_irqs(endpoint->vfu_ctx, VFU_DEV_MSIX_IRQ, pci_dev.nr_msix_irqs);
460 [ - + ]: 4 : if (ret < 0) {
461 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to setup MSIX\n", endpoint->vfu_ctx);
462 : 0 : goto error;
463 : : }
464 : :
465 : 4 : ret = vfu_realize_ctx(endpoint->vfu_ctx);
466 [ - + ]: 4 : if (ret < 0) {
467 : 0 : SPDK_ERRLOG("vfu_ctx %p failed to realize\n", endpoint->vfu_ctx);
468 : 0 : goto error;
469 : : }
470 : :
471 : 4 : endpoint->pci_config_space = vfu_pci_get_config_space(endpoint->vfu_ctx);
472 [ - + ]: 4 : assert(endpoint->pci_config_space != NULL);
473 : 4 : init_pci_config_space(endpoint->pci_config_space, pci_dev.intr_ipin);
474 : :
475 [ - + ]: 4 : assert(cap_offset != 0);
476 : 4 : endpoint->msix = (struct msixcap *)((uint8_t *)endpoint->pci_config_space + cap_offset);
477 : :
478 : 4 : return 0;
479 : :
480 : 0 : error:
481 [ # # ]: 0 : if (endpoint->vfu_ctx) {
482 : 0 : vfu_destroy_ctx(endpoint->vfu_ctx);
483 : : }
484 : 0 : return ret;
485 : : }
486 : :
487 : : static int
488 : 4 : vfu_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
489 : : {
490 : : int rc;
491 : 4 : struct spdk_cpuset negative_vfu_mask;
492 : :
493 [ - + ]: 4 : if (cpumask == NULL) {
494 : 0 : return -1;
495 : : }
496 : :
497 [ + + ]: 4 : if (mask == NULL) {
498 : 2 : spdk_cpuset_copy(cpumask, &g_tgt_core_mask);
499 : 2 : return 0;
500 : : }
501 : :
502 : 2 : rc = spdk_cpuset_parse(cpumask, mask);
503 [ - + ]: 2 : if (rc < 0) {
504 : 0 : SPDK_ERRLOG("invalid cpumask %s\n", mask);
505 : 0 : return -1;
506 : : }
507 : :
508 : 2 : spdk_cpuset_copy(&negative_vfu_mask, &g_tgt_core_mask);
509 : 2 : spdk_cpuset_negate(&negative_vfu_mask);
510 : 2 : spdk_cpuset_and(&negative_vfu_mask, cpumask);
511 : :
512 [ - + ]: 2 : if (spdk_cpuset_count(&negative_vfu_mask) != 0) {
513 : 0 : SPDK_ERRLOG("one of selected cpu is outside of core mask(=%s)\n",
514 : : spdk_cpuset_fmt(&g_tgt_core_mask));
515 : 0 : return -1;
516 : : }
517 : :
518 : 2 : spdk_cpuset_and(cpumask, &g_tgt_core_mask);
519 : :
520 [ - + ]: 2 : if (spdk_cpuset_count(cpumask) == 0) {
521 : 0 : SPDK_ERRLOG("no cpu is selected among core mask(=%s)\n",
522 : : spdk_cpuset_fmt(&g_tgt_core_mask));
523 : 0 : return -1;
524 : : }
525 : :
526 : 2 : return 0;
527 : : }
528 : :
529 : : static void
530 : 4 : tgt_endpoint_start_thread(void *arg1)
531 : : {
532 : 4 : struct spdk_vfu_endpoint *endpoint = arg1;
533 : :
534 : 4 : endpoint->accept_poller = SPDK_POLLER_REGISTER(tgt_accept_poller, endpoint, 1000);
535 [ - + ]: 4 : assert(endpoint->accept_poller != NULL);
536 : 4 : }
537 : :
538 : : static void
539 : 4 : tgt_endpoint_thread_exit(void *arg1)
540 : : {
541 : 4 : struct spdk_vfu_endpoint *endpoint = arg1;
542 : :
543 : 4 : spdk_poller_unregister(&endpoint->accept_poller);
544 : 4 : spdk_poller_unregister(&endpoint->vfu_ctx_poller);
545 : :
546 : : /* Ensure the attached device is stopped before destorying the vfu context */
547 [ + - ]: 4 : if (endpoint->ops.detach_device) {
548 : 4 : endpoint->ops.detach_device(endpoint);
549 : : }
550 : :
551 [ + - ]: 4 : if (endpoint->vfu_ctx) {
552 : 4 : vfu_destroy_ctx(endpoint->vfu_ctx);
553 : : }
554 : :
555 : 4 : endpoint->ops.destruct(endpoint);
556 : 4 : free(endpoint);
557 : :
558 : 4 : spdk_thread_exit(spdk_get_thread());
559 : 4 : }
560 : :
561 : : int
562 : 4 : spdk_vfu_create_endpoint(const char *endpoint_name, const char *cpumask_str,
563 : : const char *dev_type_name)
564 : : {
565 : : char *basename;
566 : 4 : char uuid[PATH_MAX] = "";
567 : 4 : struct spdk_cpuset cpumask = {};
568 : : struct spdk_vfu_endpoint *endpoint;
569 : : struct spdk_vfu_endpoint_ops *ops;
570 : 4 : int ret = 0;
571 : :
572 : 4 : ret = vfu_parse_core_mask(cpumask_str, &cpumask);
573 [ - + ]: 4 : if (ret) {
574 : 0 : return ret;
575 : : }
576 : :
577 [ - + - + ]: 4 : if (strlen(endpoint_name) >= SPDK_VFU_MAX_NAME_LEN - 1) {
578 : 0 : return -ENAMETOOLONG;
579 : : }
580 : :
581 [ - + ]: 4 : if (spdk_vfu_get_endpoint_by_name(endpoint_name)) {
582 : 0 : SPDK_ERRLOG("%s already exist\n", endpoint_name);
583 : 0 : return -EEXIST;
584 : : }
585 : :
586 : : /* Find supported PCI device type */
587 : 4 : ops = tgt_get_pci_device_ops(dev_type_name);
588 [ - + ]: 4 : if (!ops) {
589 : 0 : SPDK_ERRLOG("Request %s device type isn't registered\n", dev_type_name);
590 : 0 : return -ENOTSUP;
591 : : }
592 : :
593 : 4 : basename = tgt_get_base_path();
594 [ - + ]: 4 : if (snprintf(uuid, sizeof(uuid), "%s%s", basename, endpoint_name) >= (int)sizeof(uuid)) {
595 : 0 : SPDK_ERRLOG("Resulting socket path for endpoint %s is too long: %s%s\n",
596 : : endpoint_name, basename, endpoint_name);
597 : 0 : return -EINVAL;
598 : : }
599 : :
600 : 4 : endpoint = calloc(1, sizeof(*endpoint));
601 [ - + ]: 4 : if (!endpoint) {
602 : 0 : return -ENOMEM;
603 : : }
604 : :
605 : 4 : endpoint->endpoint_ctx = ops->init(endpoint, basename, endpoint_name);
606 [ - + ]: 4 : if (!endpoint->endpoint_ctx) {
607 : 0 : free(endpoint);
608 : 0 : return -EINVAL;
609 : : }
610 : 4 : endpoint->ops = *ops;
611 : 4 : snprintf(endpoint->name, SPDK_VFU_MAX_NAME_LEN, "%s", endpoint_name);
612 : 4 : snprintf(endpoint->uuid, sizeof(uuid), "%s", uuid);
613 : :
614 [ - + - + ]: 4 : SPDK_DEBUGLOG(vfu, "Construct endpoint %s\n", endpoint_name);
615 : : /* Endpoint realize */
616 : 4 : ret = tgt_endpoint_realize(endpoint);
617 [ - + ]: 4 : if (ret) {
618 : 0 : endpoint->ops.destruct(endpoint);
619 : 0 : free(endpoint);
620 : 0 : return ret;
621 : : }
622 : :
623 : 4 : endpoint->thread = spdk_thread_create(endpoint_name, &cpumask);
624 [ - + ]: 4 : if (!endpoint->thread) {
625 : 0 : endpoint->ops.destruct(endpoint);
626 : 0 : vfu_destroy_ctx(endpoint->vfu_ctx);
627 : 0 : free(endpoint);
628 : 0 : return -EFAULT;
629 : : }
630 : :
631 : 4 : pthread_mutex_lock(&g_endpoint_lock);
632 : 4 : TAILQ_INSERT_TAIL(&g_endpoint, endpoint, link);
633 : 4 : pthread_mutex_unlock(&g_endpoint_lock);
634 : :
635 : 4 : spdk_thread_send_msg(endpoint->thread, tgt_endpoint_start_thread, endpoint);
636 : :
637 : 4 : return 0;
638 : : }
639 : :
640 : : int
641 : 2 : spdk_vfu_delete_endpoint(const char *endpoint_name)
642 : : {
643 : : struct spdk_vfu_endpoint *endpoint;
644 : :
645 : 2 : endpoint = spdk_vfu_get_endpoint_by_name(endpoint_name);
646 [ - + ]: 2 : if (!endpoint) {
647 : 0 : SPDK_ERRLOG("%s doesn't exist\n", endpoint_name);
648 : 0 : return -ENOENT;
649 : : }
650 : :
651 : 2 : SPDK_NOTICELOG("Destruct endpoint %s\n", endpoint_name);
652 : :
653 [ - + ]: 2 : pthread_mutex_lock(&g_endpoint_lock);
654 [ + + ]: 2 : TAILQ_REMOVE(&g_endpoint, endpoint, link);
655 [ - + ]: 2 : pthread_mutex_unlock(&g_endpoint_lock);
656 : 2 : spdk_thread_send_msg(endpoint->thread, tgt_endpoint_thread_exit, endpoint);
657 : :
658 : 2 : return 0;
659 : : }
660 : :
661 : : const char *
662 : 275 : spdk_vfu_get_endpoint_id(struct spdk_vfu_endpoint *endpoint)
663 : : {
664 : 275 : return endpoint->uuid;
665 : : }
666 : :
667 : : const char *
668 : 6 : spdk_vfu_get_endpoint_name(struct spdk_vfu_endpoint *endpoint)
669 : : {
670 : 6 : return endpoint->name;
671 : : }
672 : :
673 : : vfu_ctx_t *
674 : 3532843 : spdk_vfu_get_vfu_ctx(struct spdk_vfu_endpoint *endpoint)
675 : : {
676 : 3532843 : return endpoint->vfu_ctx;
677 : : }
678 : :
679 : : void *
680 : 1634 : spdk_vfu_get_endpoint_private(struct spdk_vfu_endpoint *endpoint)
681 : : {
682 : 1634 : return endpoint->endpoint_ctx;
683 : : }
684 : :
685 : : bool
686 : 27026 : spdk_vfu_endpoint_msix_enabled(struct spdk_vfu_endpoint *endpoint)
687 : : {
688 : 27026 : return endpoint->msix->mxc.mxe;
689 : : }
690 : :
691 : : bool
692 : 0 : spdk_vfu_endpoint_intx_enabled(struct spdk_vfu_endpoint *endpoint)
693 : : {
694 : 0 : return !endpoint->pci_config_space->hdr.cmd.id;
695 : : }
696 : :
697 : : void *
698 : 0 : spdk_vfu_endpoint_get_pci_config(struct spdk_vfu_endpoint *endpoint)
699 : : {
700 : 0 : return (void *)endpoint->pci_config_space;
701 : : }
702 : :
703 : : void
704 : 64 : spdk_vfu_init(spdk_vfu_init_cb init_cb)
705 : : {
706 : : uint32_t i;
707 : : size_t len;
708 : :
709 [ + - ]: 64 : if (g_endpoint_path_dirname[0] == '\0') {
710 [ - + ]: 64 : if (getcwd(g_endpoint_path_dirname, sizeof(g_endpoint_path_dirname) - 2) == NULL) {
711 : 0 : SPDK_ERRLOG("getcwd failed\n");
712 : 0 : return;
713 : : }
714 : :
715 : 64 : len = strlen(g_endpoint_path_dirname);
716 [ + - ]: 64 : if (g_endpoint_path_dirname[len - 1] != '/') {
717 : 64 : g_endpoint_path_dirname[len] = '/';
718 : 64 : g_endpoint_path_dirname[len + 1] = '\0';
719 : : }
720 : : }
721 : :
722 : 64 : spdk_cpuset_zero(&g_tgt_core_mask);
723 [ + + ]: 151 : SPDK_ENV_FOREACH_CORE(i) {
724 : 87 : spdk_cpuset_set_cpu(&g_tgt_core_mask, i, true);
725 : : }
726 : :
727 : 64 : init_cb(0);
728 : : }
729 : :
730 : : void *
731 : 10658160 : spdk_vfu_map_one(struct spdk_vfu_endpoint *endpoint, uint64_t addr, uint64_t len, dma_sg_t *sg,
732 : : struct iovec *iov,
733 : : int prot)
734 : : {
735 : : int ret;
736 : :
737 [ - + ]: 10658160 : assert(endpoint != NULL);
738 [ - + ]: 10658160 : assert(endpoint->vfu_ctx != NULL);
739 [ - + ]: 10658160 : assert(sg != NULL);
740 [ - + ]: 10658160 : assert(iov != NULL);
741 : :
742 : 10658160 : ret = vfu_addr_to_sgl(endpoint->vfu_ctx, (void *)(uintptr_t)addr, len, sg, 1, prot);
743 [ + + ]: 10658160 : if (ret < 0) {
744 : 20 : return NULL;
745 : : }
746 : :
747 : 10658140 : ret = vfu_sgl_get(endpoint->vfu_ctx, sg, iov, 1, 0);
748 [ - + ]: 10658140 : if (ret != 0) {
749 : 0 : return NULL;
750 : : }
751 : :
752 [ - + ]: 10658140 : assert(iov->iov_base != NULL);
753 : 10658140 : return iov->iov_base;
754 : : }
755 : :
756 : : void
757 : 78 : spdk_vfu_unmap_sg(struct spdk_vfu_endpoint *endpoint, dma_sg_t *sg, struct iovec *iov, int iovcnt)
758 : : {
759 [ - + ]: 78 : assert(endpoint != NULL);
760 [ - + ]: 78 : assert(endpoint->vfu_ctx != NULL);
761 [ - + ]: 78 : assert(sg != NULL);
762 [ - + ]: 78 : assert(iov != NULL);
763 : :
764 : 78 : vfu_sgl_put(endpoint->vfu_ctx, sg, iov, iovcnt);
765 : 78 : }
766 : :
767 : : void
768 : 64 : spdk_vfu_fini(spdk_vfu_fini_cb fini_cb)
769 : : {
770 : : struct spdk_vfu_endpoint *endpoint, *tmp;
771 : : struct tgt_pci_device_ops *ops, *ops_tmp;
772 : :
773 [ - + ]: 64 : pthread_mutex_lock(&g_endpoint_lock);
774 [ + + ]: 192 : TAILQ_FOREACH_SAFE(ops, &g_pci_device_ops, link, ops_tmp) {
775 [ + + ]: 128 : TAILQ_REMOVE(&g_pci_device_ops, ops, link);
776 : 128 : free(ops);
777 : : }
778 : :
779 [ + + ]: 66 : TAILQ_FOREACH_SAFE(endpoint, &g_endpoint, link, tmp) {
780 [ - + ]: 2 : TAILQ_REMOVE(&g_endpoint, endpoint, link);
781 : 2 : spdk_thread_send_msg(endpoint->thread, tgt_endpoint_thread_exit, endpoint);
782 : : }
783 [ - + ]: 64 : pthread_mutex_unlock(&g_endpoint_lock);
784 : :
785 : 64 : fini_cb();
786 : 64 : }
787 : 202 : SPDK_LOG_REGISTER_COMPONENT(vfu)
|