Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2017 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2021 Mellanox Technologies LTD. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "CUnit/Basic.h"
10 : : #include "spdk_internal/cunit.h"
11 : : #include "spdk/thread.h"
12 : : #include "spdk_internal/mock.h"
13 : : #include "common/lib/ut_multithread.c"
14 : : #include "unit/lib/json_mock.c"
15 : :
16 : : #include "vhost/vhost.c"
17 : : #include "vhost/vhost_blk.c"
18 : : #include <rte_version.h>
19 : : #include "vhost/rte_vhost_user.c"
20 : :
21 [ # # ]: 0 : DEFINE_STUB(rte_vhost_set_vring_base, int, (int vid, uint16_t queue_id,
22 : : uint16_t last_avail_idx, uint16_t last_used_idx), 0);
23 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_vring_base, int, (int vid, uint16_t queue_id,
24 : : uint16_t *last_avail_idx, uint16_t *last_used_idx), 0);
25 [ # # ]: 0 : DEFINE_STUB(spdk_mem_register, int, (void *vaddr, size_t len), 0);
26 [ # # ]: 0 : DEFINE_STUB(spdk_mem_unregister, int, (void *vaddr, size_t len), 0);
27 : : #if RTE_VERSION < RTE_VERSION_NUM(22, 11, 0, 0)
28 : : DEFINE_STUB(rte_vhost_vring_call, int, (int vid, uint16_t vring_idx), 0);
29 : : #else
30 [ # # ]: 0 : DEFINE_STUB(rte_vhost_vring_call_nonblock, int, (int vid, uint16_t vring_idx), 0);
31 : : #endif
32 : 0 : DEFINE_STUB_V(rte_vhost_log_used_vring, (int vid, uint16_t vring_idx,
33 : : uint64_t offset, uint64_t len));
34 : :
35 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_mem_table, int, (int vid, struct rte_vhost_memory **mem), 0);
36 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_negotiated_features, int, (int vid, uint64_t *features), 0);
37 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_vhost_vring, int,
38 : : (int vid, uint16_t vring_idx, struct rte_vhost_vring *vring), 0);
39 [ # # ]: 0 : DEFINE_STUB(rte_vhost_enable_guest_notification, int,
40 : : (int vid, uint16_t queue_id, int enable), 0);
41 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_ifname, int, (int vid, char *buf, size_t len), 0);
42 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_start, int, (const char *name), 0);
43 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_callback_register, int,
44 : : (const char *path, struct rte_vhost_device_ops const *const ops), 0);
45 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_disable_features, int, (const char *path, uint64_t features), 0);
46 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_set_features, int, (const char *path, uint64_t features), 0);
47 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_register, int, (const char *path, uint64_t flags), 0);
48 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_unregister, int, (const char *path), 0);
49 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_get_protocol_features, int,
50 : : (const char *path, uint64_t *protocol_features), 0);
51 [ - + ]: 36 : DEFINE_STUB(rte_vhost_driver_set_protocol_features, int,
52 : : (const char *path, uint64_t protocol_features), 0);
53 : :
54 [ # # ]: 0 : DEFINE_STUB(rte_vhost_set_last_inflight_io_split, int,
55 : : (int vid, uint16_t vring_idx, uint16_t idx), 0);
56 [ # # ]: 0 : DEFINE_STUB(rte_vhost_clr_inflight_desc_split, int,
57 : : (int vid, uint16_t vring_idx, uint16_t last_used_idx, uint16_t idx), 0);
58 [ - + ]: 28 : DEFINE_STUB(rte_vhost_set_last_inflight_io_packed, int,
59 : : (int vid, uint16_t vring_idx, uint16_t head), 0);
60 [ - + ]: 28 : DEFINE_STUB(rte_vhost_clr_inflight_desc_packed, int,
61 : : (int vid, uint16_t vring_idx, uint16_t head), 0);
62 : 0 : DEFINE_STUB_V(rte_vhost_log_write, (int vid, uint64_t addr, uint64_t len));
63 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_vhost_ring_inflight, int,
64 : : (int vid, uint16_t vring_idx, struct rte_vhost_ring_inflight *vring), 0);
65 [ # # ]: 0 : DEFINE_STUB(rte_vhost_get_vring_base_from_inflight, int,
66 : : (int vid, uint16_t queue_id, uint16_t *last_avail_idx, uint16_t *last_used_idx), 0);
67 [ # # ]: 0 : DEFINE_STUB(rte_vhost_extern_callback_register, int,
68 : : (int vid, struct rte_vhost_user_extern_ops const *const ops, void *ctx), 0);
69 [ - + - + ]: 36 : DEFINE_STUB(spdk_iommu_is_enabled, bool, (void), 0);
70 : :
71 : : /* rte_vhost_user.c shutdowns vhost_user sessions in a separate pthread */
72 : : DECLARE_WRAPPER(pthread_create, int, (pthread_t *thread, const pthread_attr_t *attr,
73 : : void *(*start_routine)(void *), void *arg));
74 : : int
75 : : pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),
76 : : void *arg)
77 : : {
78 : 4 : *thread = 0;
79 : 4 : start_routine(arg);
80 : 4 : return 0;
81 : : }
82 [ - + - - ]: 4 : DEFINE_STUB(pthread_detach, int, (pthread_t thread), 0);
83 : :
84 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_writev, int,
85 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
86 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t len,
87 : : spdk_bdev_io_completion_cb cb, void *cb_arg),
88 : : 0);
89 : :
90 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_unmap, int,
91 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
92 : : uint64_t offset, uint64_t nbytes,
93 : : spdk_bdev_io_completion_cb cb, void *cb_arg),
94 : : 0);
95 : :
96 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_write_zeroes, int,
97 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
98 : : uint64_t offset, uint64_t nbytes,
99 : : spdk_bdev_io_completion_cb cb, void *cb_arg),
100 : : 0);
101 : :
102 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_get_num_blocks, uint64_t, (const struct spdk_bdev *bdev), 0);
103 : :
104 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_get_block_size, uint32_t, (const struct spdk_bdev *bdev), 512);
105 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_get_name, const char *, (const struct spdk_bdev *bdev), "test");
106 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_get_buf_align, size_t, (const struct spdk_bdev *bdev), 64);
107 [ - + - + ]: 12 : DEFINE_STUB(spdk_bdev_io_type_supported, bool, (struct spdk_bdev *bdev,
108 : : enum spdk_bdev_io_type io_type), true);
109 [ - + ]: 4 : DEFINE_STUB(spdk_bdev_open_ext, int,
110 : : (const char *bdev_name, bool write, spdk_bdev_event_cb_t event_cb,
111 : : void *event_ctx, struct spdk_bdev_desc **desc), 0);
112 [ - + ]: 4 : DEFINE_STUB(spdk_bdev_desc_get_bdev, struct spdk_bdev *,
113 : : (struct spdk_bdev_desc *desc), NULL);
114 : 0 : DEFINE_STUB_V(spdk_bdev_close, (struct spdk_bdev_desc *desc));
115 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_queue_io_wait, int, (struct spdk_bdev *bdev, struct spdk_io_channel *ch,
116 : : struct spdk_bdev_io_wait_entry *entry), 0);
117 : 0 : DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *bdev_io));
118 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_get_io_channel, struct spdk_io_channel *, (struct spdk_bdev_desc *desc), 0);
119 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_readv, int,
120 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
121 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t nbytes,
122 : : spdk_bdev_io_completion_cb cb, void *cb_arg),
123 : : 0);
124 [ # # ]: 0 : DEFINE_STUB(spdk_bdev_flush, int,
125 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
126 : : uint64_t offset, uint64_t nbytes,
127 : : spdk_bdev_io_completion_cb cb, void *cb_arg),
128 : : 0);
129 [ # # ]: 0 : DEFINE_STUB(rte_vhost_set_inflight_desc_split, int, (int vid, uint16_t vring_idx, uint16_t idx), 0);
130 [ # # ]: 0 : DEFINE_STUB(rte_vhost_set_inflight_desc_packed, int, (int vid, uint16_t vring_idx, uint16_t head,
131 : : uint16_t last, uint16_t *inflight_entry), 0);
132 : : #if RTE_VERSION >= RTE_VERSION_NUM(23, 03, 0, 0)
133 [ # # ]: 0 : DEFINE_STUB(rte_vhost_backend_config_change, int, (int vid, bool need_reply), 0);
134 : : #else
135 : : DEFINE_STUB(rte_vhost_slave_config_change, int, (int vid, bool need_reply), 0);
136 : : #endif
137 [ # # ]: 0 : DEFINE_STUB(spdk_json_decode_bool, int, (const struct spdk_json_val *val, void *out), 0);
138 [ - + ]: 4 : DEFINE_STUB(spdk_json_decode_object_relaxed, int,
139 : : (const struct spdk_json_val *values, const struct spdk_json_object_decoder *decoders,
140 : : size_t num_decoders, void *out), 0);
141 : :
142 : : void *
143 : 0 : spdk_call_unaffinitized(void *cb(void *arg), void *arg)
144 : : {
145 : 0 : return cb(arg);
146 : : }
147 : :
148 : : static struct spdk_vhost_dev_backend g_vdev_backend = {.type = VHOST_BACKEND_SCSI};
149 : : static struct spdk_vhost_user_dev_backend g_vdev_user_backend;
150 : :
151 : : static bool g_init_fail;
152 : : static void
153 : 8 : init_cb(int rc)
154 : : {
155 : 8 : g_init_fail = rc;
156 : 8 : }
157 : :
158 : : static int
159 : 4 : test_setup(void)
160 : : {
161 : 4 : allocate_cores(1);
162 : 4 : allocate_threads(1);
163 : 4 : set_thread(0);
164 : :
165 : 4 : g_init_fail = true;
166 : 4 : spdk_vhost_scsi_init(init_cb);
167 [ - + - + ]: 4 : assert(g_init_fail == false);
168 : :
169 : 4 : g_init_fail = true;
170 : 4 : spdk_vhost_blk_init(init_cb);
171 [ - + - + ]: 4 : assert(g_init_fail == false);
172 : :
173 : 4 : return 0;
174 : : }
175 : :
176 : : static bool g_fini_fail;
177 : : static void
178 : 8 : fini_cb(void)
179 : : {
180 : 8 : g_fini_fail = false;
181 : 8 : }
182 : :
183 : : static int
184 : 4 : test_cleanup(void)
185 : : {
186 : 4 : g_fini_fail = true;
187 : 4 : spdk_vhost_scsi_fini(fini_cb);
188 : 4 : poll_threads();
189 [ - + - + ]: 4 : assert(g_fini_fail == false);
190 : :
191 : 4 : g_fini_fail = true;
192 : 4 : spdk_vhost_blk_fini(fini_cb);
193 : 4 : poll_threads();
194 [ - + - + ]: 4 : assert(g_fini_fail == false);
195 : :
196 : 4 : free_threads();
197 : 4 : free_cores();
198 : :
199 : 4 : return 0;
200 : : }
201 : :
202 : : static int
203 : 52 : alloc_vdev(struct spdk_vhost_dev **vdev_p, const char *name, const char *cpumask)
204 : : {
205 : 52 : struct spdk_vhost_dev *vdev = NULL;
206 : : int rc;
207 : :
208 : : /* spdk_vhost_dev must be allocated on a cache line boundary. */
209 : 52 : rc = posix_memalign((void **)&vdev, 64, sizeof(*vdev));
210 : 52 : CU_ASSERT(rc == 0);
211 [ - + ]: 52 : SPDK_CU_ASSERT_FATAL(vdev != NULL);
212 [ - + ]: 52 : memset(vdev, 0, sizeof(*vdev));
213 : 52 : rc = vhost_dev_register(vdev, name, cpumask, NULL, &g_vdev_backend, &g_vdev_user_backend, false);
214 [ + + ]: 52 : if (rc == 0) {
215 : 32 : *vdev_p = vdev;
216 : : } else {
217 : 20 : free(vdev);
218 : 20 : *vdev_p = NULL;
219 : : }
220 : :
221 : 52 : return rc;
222 : : }
223 : :
224 : : static void
225 : 12 : start_vdev(struct spdk_vhost_dev *vdev)
226 : : {
227 : 12 : struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
228 : : struct rte_vhost_memory *mem;
229 : 12 : struct spdk_vhost_session *vsession = NULL;
230 : : int rc;
231 : :
232 : 12 : mem = calloc(1, sizeof(*mem) + 2 * sizeof(struct rte_vhost_mem_region));
233 [ - + ]: 12 : SPDK_CU_ASSERT_FATAL(mem != NULL);
234 : 12 : mem->nregions = 2;
235 : 12 : mem->regions[0].guest_phys_addr = 0;
236 : 12 : mem->regions[0].size = 0x400000; /* 4 MB */
237 : 12 : mem->regions[0].host_user_addr = 0x1000000;
238 : 12 : mem->regions[1].guest_phys_addr = 0x400000;
239 : 12 : mem->regions[1].size = 0x400000; /* 4 MB */
240 : 12 : mem->regions[1].host_user_addr = 0x2000000;
241 : :
242 [ - + ]: 12 : assert(TAILQ_EMPTY(&user_dev->vsessions));
243 : : /* spdk_vhost_dev must be allocated on a cache line boundary. */
244 : 12 : rc = posix_memalign((void **)&vsession, 64, sizeof(*vsession));
245 : 12 : CU_ASSERT(rc == 0);
246 [ - + ]: 12 : SPDK_CU_ASSERT_FATAL(vsession != NULL);
247 : 12 : vsession->started = true;
248 : 12 : vsession->vid = 0;
249 : 12 : vsession->mem = mem;
250 : 12 : TAILQ_INSERT_TAIL(&user_dev->vsessions, vsession, tailq);
251 : 12 : }
252 : :
253 : : static void
254 : 12 : stop_vdev(struct spdk_vhost_dev *vdev)
255 : : {
256 : 12 : struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
257 : 12 : struct spdk_vhost_session *vsession = TAILQ_FIRST(&user_dev->vsessions);
258 : :
259 [ - + ]: 12 : TAILQ_REMOVE(&user_dev->vsessions, vsession, tailq);
260 : 12 : free(vsession->mem);
261 : 12 : free(vsession);
262 : 12 : }
263 : :
264 : : static void
265 : 32 : cleanup_vdev(struct spdk_vhost_dev *vdev)
266 : : {
267 : 32 : struct spdk_vhost_user_dev *user_dev = to_user_dev(vdev);
268 : :
269 [ + + ]: 32 : if (!TAILQ_EMPTY(&user_dev->vsessions)) {
270 : 12 : stop_vdev(vdev);
271 : : }
272 : 32 : vhost_dev_unregister(vdev);
273 : 32 : free(vdev);
274 : 32 : }
275 : :
276 : : static void
277 : 4 : desc_to_iov_test(void)
278 : : {
279 : 4 : struct spdk_vhost_dev *vdev;
280 : : struct spdk_vhost_session *vsession;
281 : 4 : struct iovec iov[SPDK_VHOST_IOVS_MAX];
282 : 4 : uint16_t iov_index;
283 : 4 : struct vring_desc desc;
284 : : int rc;
285 : :
286 : 4 : spdk_cpuset_set_cpu(&g_vhost_core_mask, 0, true);
287 : :
288 : 4 : rc = alloc_vdev(&vdev, "vdev_name_0", "0x1");
289 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(rc == 0 && vdev);
- + ]
290 : 4 : start_vdev(vdev);
291 : :
292 : 4 : vsession = TAILQ_FIRST(&to_user_dev(vdev)->vsessions);
293 : :
294 : : /* Test simple case where iov falls fully within a 2MB page. */
295 : 4 : desc.addr = 0x110000;
296 : 4 : desc.len = 0x1000;
297 : 4 : iov_index = 0;
298 : 4 : rc = vhost_vring_desc_to_iov(vsession, iov, &iov_index, &desc);
299 : 4 : CU_ASSERT(rc == 0);
300 : 4 : CU_ASSERT(iov_index == 1);
301 : 4 : CU_ASSERT(iov[0].iov_base == (void *)0x1110000);
302 : 4 : CU_ASSERT(iov[0].iov_len == 0x1000);
303 : : /*
304 : : * Always memset the iov to ensure each test validates data written by its call
305 : : * to the function under test.
306 : : */
307 : 4 : memset(iov, 0, sizeof(iov));
308 : :
309 : : /* Same test, but ensure it respects the non-zero starting iov_index. */
310 : 4 : iov_index = SPDK_VHOST_IOVS_MAX - 1;
311 : 4 : rc = vhost_vring_desc_to_iov(vsession, iov, &iov_index, &desc);
312 : 4 : CU_ASSERT(rc == 0);
313 : 4 : CU_ASSERT(iov_index == SPDK_VHOST_IOVS_MAX);
314 : 4 : CU_ASSERT(iov[SPDK_VHOST_IOVS_MAX - 1].iov_base == (void *)0x1110000);
315 : 4 : CU_ASSERT(iov[SPDK_VHOST_IOVS_MAX - 1].iov_len == 0x1000);
316 : 4 : memset(iov, 0, sizeof(iov));
317 : :
318 : : /* Test for failure if iov_index already equals SPDK_VHOST_IOVS_MAX. */
319 : 4 : iov_index = SPDK_VHOST_IOVS_MAX;
320 : 4 : rc = vhost_vring_desc_to_iov(vsession, iov, &iov_index, &desc);
321 : 4 : CU_ASSERT(rc != 0);
322 : 4 : memset(iov, 0, sizeof(iov));
323 : :
324 : : /* Test case where iov spans a 2MB boundary, but does not span a vhost memory region. */
325 : 4 : desc.addr = 0x1F0000;
326 : 4 : desc.len = 0x20000;
327 : 4 : iov_index = 0;
328 : 4 : rc = vhost_vring_desc_to_iov(vsession, iov, &iov_index, &desc);
329 : 4 : CU_ASSERT(rc == 0);
330 : 4 : CU_ASSERT(iov_index == 1);
331 : 4 : CU_ASSERT(iov[0].iov_base == (void *)0x11F0000);
332 : 4 : CU_ASSERT(iov[0].iov_len == 0x20000);
333 : 4 : memset(iov, 0, sizeof(iov));
334 : :
335 : : /* Same test, but ensure it respects the non-zero starting iov_index. */
336 : 4 : iov_index = SPDK_VHOST_IOVS_MAX - 1;
337 : 4 : rc = vhost_vring_desc_to_iov(vsession, iov, &iov_index, &desc);
338 : 4 : CU_ASSERT(rc == 0);
339 : 4 : CU_ASSERT(iov_index == SPDK_VHOST_IOVS_MAX);
340 : 4 : CU_ASSERT(iov[SPDK_VHOST_IOVS_MAX - 1].iov_base == (void *)0x11F0000);
341 : 4 : CU_ASSERT(iov[SPDK_VHOST_IOVS_MAX - 1].iov_len == 0x20000);
342 : 4 : memset(iov, 0, sizeof(iov));
343 : :
344 : : /* Test case where iov spans a vhost memory region. */
345 : 4 : desc.addr = 0x3F0000;
346 : 4 : desc.len = 0x20000;
347 : 4 : iov_index = 0;
348 : 4 : rc = vhost_vring_desc_to_iov(vsession, iov, &iov_index, &desc);
349 : 4 : CU_ASSERT(rc == 0);
350 : 4 : CU_ASSERT(iov_index == 2);
351 : 4 : CU_ASSERT(iov[0].iov_base == (void *)0x13F0000);
352 : 4 : CU_ASSERT(iov[0].iov_len == 0x10000);
353 : 4 : CU_ASSERT(iov[1].iov_base == (void *)0x2000000);
354 : 4 : CU_ASSERT(iov[1].iov_len == 0x10000);
355 : 4 : memset(iov, 0, sizeof(iov));
356 : :
357 : 4 : cleanup_vdev(vdev);
358 : :
359 : 4 : CU_ASSERT(true);
360 : 4 : }
361 : :
362 : : static void
363 : 4 : create_controller_test(void)
364 : : {
365 : 4 : struct spdk_vhost_dev *vdev, *vdev2;
366 : : int ret;
367 : 4 : char long_name[PATH_MAX];
368 : :
369 : 4 : spdk_cpuset_parse(&g_vhost_core_mask, "0xf");
370 : :
371 : : /* Create device with cpumask implicitly matching whole application */
372 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", NULL);
373 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(ret == 0 && vdev);
- + ]
374 [ - + - + ]: 4 : SPDK_CU_ASSERT_FATAL(!strcmp(spdk_cpuset_fmt(spdk_thread_get_cpumask(vdev->thread)), "f"));
375 : 4 : cleanup_vdev(vdev);
376 : :
377 : : /* Create device with cpumask matching whole application */
378 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", "0xf");
379 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(ret == 0 && vdev);
- + ]
380 [ - + - + ]: 4 : SPDK_CU_ASSERT_FATAL(!strcmp(spdk_cpuset_fmt(spdk_thread_get_cpumask(vdev->thread)), "f"));
381 : 4 : cleanup_vdev(vdev);
382 : :
383 : : /* Create device with single core in cpumask */
384 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", "0x2");
385 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(ret == 0 && vdev);
- + ]
386 [ - + - + ]: 4 : SPDK_CU_ASSERT_FATAL(!strcmp(spdk_cpuset_fmt(spdk_thread_get_cpumask(vdev->thread)), "2"));
387 : 4 : cleanup_vdev(vdev);
388 : :
389 : : /* Create device with cpumask spanning two cores */
390 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", "0x3");
391 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(ret == 0 && vdev);
- + ]
392 [ - + - + ]: 4 : SPDK_CU_ASSERT_FATAL(!strcmp(spdk_cpuset_fmt(spdk_thread_get_cpumask(vdev->thread)), "3"));
393 : 4 : cleanup_vdev(vdev);
394 : :
395 : : /* Create device with incorrect cpumask outside of application cpumask */
396 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", "0xf0");
397 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(ret != 0);
398 : :
399 : : /* Create device with incorrect cpumask partially outside of application cpumask */
400 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", "0xff");
401 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(ret != 0);
402 : :
403 : : /* Create device with no name */
404 : 4 : ret = alloc_vdev(&vdev, NULL, NULL);
405 : 4 : CU_ASSERT(ret != 0);
406 : :
407 : : /* Create device with too long name and path */
408 : 4 : memset(long_name, 'x', sizeof(long_name));
409 : 4 : long_name[PATH_MAX - 1] = 0;
410 : 4 : snprintf(g_vhost_user_dev_dirname, sizeof(g_vhost_user_dev_dirname), "some_path/");
411 : 4 : ret = alloc_vdev(&vdev, long_name, NULL);
412 : 4 : CU_ASSERT(ret != 0);
413 : 4 : g_vhost_user_dev_dirname[0] = 0;
414 : :
415 : : /* Create device when device name is already taken */
416 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", NULL);
417 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(ret == 0 && vdev);
- + ]
418 : 4 : ret = alloc_vdev(&vdev2, "vdev_name_0", NULL);
419 : 4 : CU_ASSERT(ret != 0);
420 : 4 : cleanup_vdev(vdev);
421 : 4 : }
422 : :
423 : : static void
424 : 4 : session_find_by_vid_test(void)
425 : : {
426 : 4 : struct spdk_vhost_dev *vdev;
427 : : struct spdk_vhost_session *vsession;
428 : : struct spdk_vhost_session *tmp;
429 : : int rc;
430 : :
431 : 4 : rc = alloc_vdev(&vdev, "vdev_name_0", "0x1");
432 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(rc == 0 && vdev);
- + ]
433 : 4 : start_vdev(vdev);
434 : :
435 : 4 : vsession = TAILQ_FIRST(&to_user_dev(vdev)->vsessions);
436 : :
437 : 4 : tmp = vhost_session_find_by_vid(vsession->vid);
438 : 4 : CU_ASSERT(tmp == vsession);
439 : :
440 : : /* Search for a device with incorrect vid */
441 : 4 : tmp = vhost_session_find_by_vid(vsession->vid + 0xFF);
442 : 4 : CU_ASSERT(tmp == NULL);
443 : :
444 : 4 : cleanup_vdev(vdev);
445 : 4 : }
446 : :
447 : : static void
448 : 4 : remove_controller_test(void)
449 : : {
450 : 4 : struct spdk_vhost_dev *vdev;
451 : : int ret;
452 : :
453 : 4 : ret = alloc_vdev(&vdev, "vdev_name_0", "0x1");
454 [ + - + - : 4 : SPDK_CU_ASSERT_FATAL(ret == 0 && vdev);
- + ]
455 : :
456 : : /* Remove device when controller is in use */
457 : 4 : start_vdev(vdev);
458 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&to_user_dev(vdev)->vsessions));
459 : 4 : ret = vhost_dev_unregister(vdev);
460 : 4 : CU_ASSERT(ret != 0);
461 : :
462 : 4 : cleanup_vdev(vdev);
463 : 4 : }
464 : :
465 : : static void
466 : 4 : vq_avail_ring_get_test(void)
467 : : {
468 : 4 : struct spdk_vhost_virtqueue vq = {};
469 : 4 : uint16_t avail_mem[34];
470 : 4 : uint16_t reqs[32];
471 : : uint16_t reqs_len, ret, i;
472 : :
473 : : /* Basic example reap all requests */
474 : 4 : vq.vring.avail = (struct vring_avail *)avail_mem;
475 : 4 : vq.vring.size = 32;
476 : 4 : vq.last_avail_idx = 24;
477 : 4 : vq.vring.avail->idx = 29;
478 : 4 : reqs_len = 6;
479 : :
480 [ + + ]: 132 : for (i = 0; i < 32; i++) {
481 : 128 : vq.vring.avail->ring[i] = i;
482 : : }
483 : :
484 : 4 : ret = vhost_vq_avail_ring_get(&vq, reqs, reqs_len);
485 : 4 : CU_ASSERT(ret == 5);
486 : 4 : CU_ASSERT(vq.last_avail_idx == 29);
487 [ + + ]: 24 : for (i = 0; i < ret; i++) {
488 : 20 : CU_ASSERT(reqs[i] == vq.vring.avail->ring[i + 24]);
489 : : }
490 : :
491 : : /* Basic example reap only some requests */
492 : 4 : vq.last_avail_idx = 20;
493 : 4 : vq.vring.avail->idx = 29;
494 : 4 : reqs_len = 6;
495 : :
496 : 4 : ret = vhost_vq_avail_ring_get(&vq, reqs, reqs_len);
497 : 4 : CU_ASSERT(ret == reqs_len);
498 : 4 : CU_ASSERT(vq.last_avail_idx == 26);
499 [ + + ]: 28 : for (i = 0; i < ret; i++) {
500 : 24 : CU_ASSERT(reqs[i] == vq.vring.avail->ring[i + 20]);
501 : : }
502 : :
503 : : /* Test invalid example */
504 : 4 : vq.last_avail_idx = 20;
505 : 4 : vq.vring.avail->idx = 156;
506 : 4 : reqs_len = 6;
507 : :
508 : 4 : ret = vhost_vq_avail_ring_get(&vq, reqs, reqs_len);
509 : 4 : CU_ASSERT(ret == 0);
510 : :
511 : : /* Test overflow in the avail->idx variable. */
512 : 4 : vq.last_avail_idx = 65535;
513 : 4 : vq.vring.avail->idx = 4;
514 : 4 : reqs_len = 6;
515 : 4 : ret = vhost_vq_avail_ring_get(&vq, reqs, reqs_len);
516 : 4 : CU_ASSERT(ret == 5);
517 : 4 : CU_ASSERT(vq.last_avail_idx == 4);
518 : 4 : CU_ASSERT(reqs[0] == vq.vring.avail->ring[31]);
519 [ + + ]: 20 : for (i = 1; i < ret; i++) {
520 : 16 : CU_ASSERT(reqs[i] == vq.vring.avail->ring[i - 1]);
521 : : }
522 : 4 : }
523 : :
524 : : static bool
525 : 28 : vq_desc_guest_is_used(struct spdk_vhost_virtqueue *vq, int16_t guest_last_used_idx,
526 : : int16_t guest_used_phase)
527 : : {
528 : 28 : return (!!(vq->vring.desc_packed[guest_last_used_idx].flags & VRING_DESC_F_USED) ==
529 : 28 : !!guest_used_phase);
530 : : }
531 : :
532 : : static void
533 : 28 : vq_desc_guest_set_avail(struct spdk_vhost_virtqueue *vq, int16_t *guest_last_avail_idx,
534 : : int16_t *guest_avail_phase)
535 : : {
536 [ + + ]: 28 : if (*guest_avail_phase) {
537 : 16 : vq->vring.desc_packed[*guest_last_avail_idx].flags |= VRING_DESC_F_AVAIL;
538 : 16 : vq->vring.desc_packed[*guest_last_avail_idx].flags &= ~VRING_DESC_F_USED;
539 : : } else {
540 : 12 : vq->vring.desc_packed[*guest_last_avail_idx].flags &= ~VRING_DESC_F_AVAIL;
541 : 12 : vq->vring.desc_packed[*guest_last_avail_idx].flags |= VRING_DESC_F_USED;
542 : : }
543 : :
544 [ + + ]: 28 : if (++(*guest_last_avail_idx) >= vq->vring.size) {
545 : 4 : *guest_last_avail_idx -= vq->vring.size;
546 : 4 : *guest_avail_phase = !(*guest_avail_phase);
547 : : }
548 : 28 : }
549 : :
550 : : static int16_t
551 : 28 : vq_desc_guest_handle_completed_desc(struct spdk_vhost_virtqueue *vq, int16_t *guest_last_used_idx,
552 : : int16_t *guest_used_phase)
553 : : {
554 : 28 : int16_t buffer_id = -1;
555 : :
556 [ + - ]: 28 : if (vq_desc_guest_is_used(vq, *guest_last_used_idx, *guest_used_phase)) {
557 : 28 : buffer_id = vq->vring.desc_packed[*guest_last_used_idx].id;
558 [ + + ]: 28 : if (++(*guest_last_used_idx) >= vq->vring.size) {
559 : 4 : *guest_last_used_idx -= vq->vring.size;
560 : 4 : *guest_used_phase = !(*guest_used_phase);
561 : : }
562 : :
563 : 28 : return buffer_id;
564 : : }
565 : :
566 : 0 : return -1;
567 : : }
568 : :
569 : : static void
570 : 4 : vq_packed_ring_test(void)
571 : : {
572 : :
573 : 4 : struct spdk_vhost_session *vs;
574 : 4 : struct spdk_vhost_virtqueue *vq;
575 : 4 : struct vring_packed_desc descs[4];
576 : 4 : uint16_t guest_last_avail_idx = 0, guest_last_used_idx = 0;
577 : 4 : uint16_t guest_avail_phase = 1, guest_used_phase = 1;
578 : : int i;
579 : : int rc;
580 : 4 : int16_t chain_num;
581 : :
582 : : /* See SPDK issue #3004 and #3310 Seems like a bug with gcc + asan on
583 : : * Fedora 38, so we need to explicitly align the variable here.
584 : : */
585 : 4 : rc = posix_memalign((void **)&vs, SPDK_CACHE_LINE_SIZE, sizeof(*vs));
586 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(rc == 0);
587 : 4 : rc = posix_memalign((void **)&vq, SPDK_CACHE_LINE_SIZE, sizeof(*vq));
588 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(rc == 0);
589 [ - + ]: 4 : memset(vs, 0, sizeof(*vs));
590 [ - + ]: 4 : memset(vq, 0, sizeof(*vq));
591 : :
592 : 4 : vq->vring.desc_packed = descs;
593 : 4 : vq->vring.size = 4;
594 : :
595 : : /* avail and used wrap counter are initialized to 1 */
596 : 4 : vq->packed.avail_phase = 1;
597 : 4 : vq->packed.used_phase = 1;
598 : 4 : vq->packed.packed_ring = true;
599 : 4 : memset(descs, 0, sizeof(descs));
600 : :
601 : 4 : CU_ASSERT(vhost_vq_packed_ring_is_avail(vq) == false);
602 : :
603 : : /* Guest send requests */
604 [ + + ]: 20 : for (i = 0; i < vq->vring.size; i++) {
605 : 16 : descs[guest_last_avail_idx].id = i;
606 : : /* Set the desc available */
607 : 16 : vq_desc_guest_set_avail(vq, &guest_last_avail_idx, &guest_avail_phase);
608 : : }
609 : 4 : CU_ASSERT(guest_last_avail_idx == 0);
610 : 4 : CU_ASSERT(guest_avail_phase == 0);
611 : :
612 : : /* Host handle available descs */
613 : 4 : CU_ASSERT(vhost_vq_packed_ring_is_avail(vq) == true);
614 : 4 : i = 0;
615 [ + + ]: 20 : while (vhost_vq_packed_ring_is_avail(vq)) {
616 : 16 : CU_ASSERT(vhost_vring_packed_desc_get_buffer_id(vq, vq->last_avail_idx, &chain_num) == i++);
617 : 16 : CU_ASSERT(chain_num == 1);
618 : : }
619 : :
620 : : /* Host complete them out of order: 1, 0, 2. */
621 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 1, 1, 0);
622 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 0, 1, 0);
623 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 2, 1, 0);
624 : :
625 : : /* Host has got all the available request but only complete three requests */
626 : 4 : CU_ASSERT(vq->last_avail_idx == 0);
627 : 4 : CU_ASSERT(vq->packed.avail_phase == 0);
628 : 4 : CU_ASSERT(vq->last_used_idx == 3);
629 : 4 : CU_ASSERT(vq->packed.used_phase == 1);
630 : :
631 : : /* Guest handle completed requests */
632 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 1);
633 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 0);
634 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 2);
635 : 4 : CU_ASSERT(guest_last_used_idx == 3);
636 : 4 : CU_ASSERT(guest_used_phase == 1);
637 : :
638 : : /* There are three descs available the guest can send three request again */
639 [ + + ]: 16 : for (i = 0; i < 3; i++) {
640 : 12 : descs[guest_last_avail_idx].id = 2 - i;
641 : : /* Set the desc available */
642 : 12 : vq_desc_guest_set_avail(vq, &guest_last_avail_idx, &guest_avail_phase);
643 : : }
644 : :
645 : : /* Host handle available descs */
646 : 4 : CU_ASSERT(vhost_vq_packed_ring_is_avail(vq) == true);
647 : 4 : i = 2;
648 [ + + ]: 16 : while (vhost_vq_packed_ring_is_avail(vq)) {
649 : 12 : CU_ASSERT(vhost_vring_packed_desc_get_buffer_id(vq, vq->last_avail_idx, &chain_num) == i--);
650 : 12 : CU_ASSERT(chain_num == 1);
651 : : }
652 : :
653 : : /* There are four requests in Host, the new three ones and left one */
654 : 4 : CU_ASSERT(vq->last_avail_idx == 3);
655 : : /* Available wrap conter should overturn */
656 : 4 : CU_ASSERT(vq->packed.avail_phase == 0);
657 : :
658 : : /* Host complete all the requests */
659 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 1, 1, 0);
660 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 0, 1, 0);
661 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 3, 1, 0);
662 : 4 : vhost_vq_packed_ring_enqueue(vs, vq, 1, 2, 1, 0);
663 : :
664 : 4 : CU_ASSERT(vq->last_used_idx == vq->last_avail_idx);
665 : 4 : CU_ASSERT(vq->packed.used_phase == vq->packed.avail_phase);
666 : :
667 : : /* Guest handle completed requests */
668 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 1);
669 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 0);
670 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 3);
671 : 4 : CU_ASSERT(vq_desc_guest_handle_completed_desc(vq, &guest_last_used_idx, &guest_used_phase) == 2);
672 : :
673 : 4 : CU_ASSERT(guest_last_avail_idx == guest_last_used_idx);
674 : 4 : CU_ASSERT(guest_avail_phase == guest_used_phase);
675 : 4 : free(vq);
676 : 4 : free(vs);
677 : 4 : }
678 : :
679 : : static void
680 : 4 : vhost_blk_construct_test(void)
681 : : {
682 : : int ret;
683 : 4 : struct spdk_vhost_dev *vdev = NULL;
684 : :
685 : 4 : ret = spdk_vhost_blk_construct("Malloc0", "0x1", "vhost.blk.0", NULL, NULL);
686 : 4 : CU_ASSERT(ret == 0);
687 : :
688 : 4 : vdev = spdk_vhost_dev_find("Malloc0");
689 : 4 : CU_ASSERT(vdev != NULL);
690 [ - + ]: 4 : CU_ASSERT(strcmp("Malloc0", spdk_vhost_dev_get_name(vdev)) == 0);
691 : :
692 : 4 : ret = spdk_vhost_dev_remove(vdev);
693 : 4 : CU_ASSERT(ret == 0);
694 : 4 : }
695 : :
696 : : int
697 : 4 : main(int argc, char **argv)
698 : : {
699 : 4 : CU_pSuite suite = NULL;
700 : : unsigned int num_failures;
701 : :
702 : 4 : CU_initialize_registry();
703 : :
704 : 4 : suite = CU_add_suite("vhost_suite", test_setup, test_cleanup);
705 : :
706 : 4 : CU_ADD_TEST(suite, desc_to_iov_test);
707 : 4 : CU_ADD_TEST(suite, create_controller_test);
708 : 4 : CU_ADD_TEST(suite, session_find_by_vid_test);
709 : 4 : CU_ADD_TEST(suite, remove_controller_test);
710 : 4 : CU_ADD_TEST(suite, vq_avail_ring_get_test);
711 : 4 : CU_ADD_TEST(suite, vq_packed_ring_test);
712 : 4 : CU_ADD_TEST(suite, vhost_blk_construct_test);
713 : :
714 : 4 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
715 : 4 : CU_cleanup_registry();
716 : :
717 : 4 : return num_failures;
718 : : }
|