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