Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 : : */
4 : :
5 : : #include "spdk/stdinc.h"
6 : :
7 : : #include "spdk_internal/cunit.h"
8 : : #include "common/lib/ut_multithread.c"
9 : :
10 : : static void ut_put_io_channel(struct spdk_io_channel *ch);
11 : :
12 : : #define spdk_put_io_channel(ch) ut_put_io_channel(ch);
13 : : #include "blob/bdev/blob_bdev.c"
14 : :
15 [ - + - + ]: 36 : DEFINE_STUB(spdk_bdev_io_type_supported, bool, (struct spdk_bdev *bdev,
16 : : enum spdk_bdev_io_type io_type), false);
17 : 0 : DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *g_bdev_io));
18 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_queue_io_wait, int,
19 : : (struct spdk_bdev *bdev, struct spdk_io_channel *ch,
20 : : struct spdk_bdev_io_wait_entry *entry), 0);
21 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_read_blocks, int,
22 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, void *buf,
23 : : uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
24 : : void *cb_arg), 0);
25 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_write_blocks, int,
26 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, void *buf,
27 : : uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
28 : : void *cb_arg), 0);
29 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_readv_blocks, int,
30 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt,
31 : : uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
32 : : void *cb_arg), 0);
33 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_writev_blocks, int,
34 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt,
35 : : uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
36 : : void *cb_arg), 0);
37 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_readv_blocks_ext, int,
38 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt,
39 : : uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
40 : : void *cb_arg, struct spdk_bdev_ext_io_opts *opts), 0);
41 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_writev_blocks_ext, int,
42 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt,
43 : : uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
44 : : void *cb_arg, struct spdk_bdev_ext_io_opts *opts), 0);
45 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_write_zeroes_blocks, int,
46 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks,
47 : : uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
48 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_unmap_blocks, int,
49 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks,
50 : : uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
51 [ # # # # ]: 0 : DEFINE_STUB(spdk_bdev_copy_blocks, int,
52 : : (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t dst_offset_blocks,
53 : : uint64_t src_offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
54 : : void *cb_arg), 0);
55 : :
56 : : struct spdk_bdev {
57 : : char name[16];
58 : : uint64_t blockcnt;
59 : : uint32_t blocklen;
60 : : uint32_t phys_blocklen;
61 : : uint32_t open_cnt;
62 : : enum spdk_bdev_claim_type claim_type;
63 : : struct spdk_bdev_module *claim_module;
64 : : struct spdk_bdev_desc *claim_desc;
65 : : };
66 : :
67 : : struct spdk_bdev_desc {
68 : : struct spdk_bdev *bdev;
69 : : bool write;
70 : : enum spdk_bdev_claim_type claim_type;
71 : : struct spdk_thread *thread;
72 : : };
73 : :
74 : : struct spdk_bdev *g_bdev;
75 : :
76 : : static struct spdk_bdev_module g_bdev_mod = {
77 : : .name = "blob_bdev_ut"
78 : : };
79 : :
80 : : struct spdk_io_channel *
81 : 32 : spdk_bdev_get_io_channel(struct spdk_bdev_desc *desc)
82 : : {
83 [ + + ]: 32 : if (desc != NULL) {
84 : 28 : return (struct spdk_io_channel *)0x1;
85 : : }
86 : 4 : return NULL;
87 : 8 : }
88 : :
89 : : static void
90 : 28 : ut_put_io_channel(struct spdk_io_channel *ch)
91 : : {
92 : 28 : }
93 : :
94 : : static struct spdk_bdev *
95 : 48 : get_bdev(const char *bdev_name)
96 : : {
97 [ + + ]: 48 : if (g_bdev == NULL) {
98 : 4 : return NULL;
99 : : }
100 : :
101 [ + + - + : 44 : if (strcmp(bdev_name, g_bdev->name) != 0) {
- + ]
102 : 0 : return NULL;
103 : : }
104 : :
105 : 44 : return g_bdev;
106 : 12 : }
107 : :
108 : : int
109 : 48 : spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event_cb,
110 : : void *event_ctx, struct spdk_bdev_desc **_desc)
111 : : {
112 : : struct spdk_bdev_desc *desc;
113 : 48 : struct spdk_bdev *bdev = get_bdev(bdev_name);
114 : :
115 [ + + ]: 48 : if (bdev == NULL) {
116 : 4 : return -ENODEV;
117 : : }
118 : :
119 [ + + + + ]: 44 : if (write && bdev->claim_module != NULL) {
120 : 8 : return -EPERM;
121 : : }
122 : :
123 : 36 : desc = calloc(1, sizeof(*desc));
124 : 36 : desc->bdev = g_bdev;
125 : 36 : desc->write = write;
126 : 36 : desc->thread = spdk_get_thread();
127 : 36 : *_desc = desc;
128 : 36 : bdev->open_cnt++;
129 : :
130 : 36 : return 0;
131 : 12 : }
132 : :
133 : : void
134 : 36 : spdk_bdev_close(struct spdk_bdev_desc *desc)
135 : : {
136 : 36 : struct spdk_bdev *bdev = desc->bdev;
137 : :
138 : 36 : CU_ASSERT(desc->thread == spdk_get_thread());
139 : :
140 : 36 : bdev->open_cnt--;
141 [ + + ]: 36 : if (bdev->claim_desc == desc) {
142 : 8 : bdev->claim_desc = NULL;
143 : 8 : bdev->claim_type = SPDK_BDEV_CLAIM_NONE;
144 : 8 : bdev->claim_module = NULL;
145 : 2 : }
146 : 36 : free(desc);
147 : 36 : }
148 : :
149 : : struct spdk_bdev *
150 : 36 : spdk_bdev_desc_get_bdev(struct spdk_bdev_desc *desc)
151 : : {
152 : 36 : return desc->bdev;
153 : : }
154 : :
155 : : uint64_t
156 : 36 : spdk_bdev_get_num_blocks(const struct spdk_bdev *bdev)
157 : : {
158 : 36 : return bdev->blockcnt;
159 : : }
160 : :
161 : : uint32_t
162 : 36 : spdk_bdev_get_block_size(const struct spdk_bdev *bdev)
163 : : {
164 : 36 : return bdev->blocklen;
165 : : }
166 : :
167 : : uint32_t
168 : 36 : spdk_bdev_get_physical_block_size(const struct spdk_bdev *bdev)
169 : : {
170 : 36 : return bdev->phys_blocklen;
171 : : }
172 : :
173 : : /* This is a simple approximation: it does not support shared claims */
174 : : int
175 : 12 : spdk_bdev_module_claim_bdev_desc(struct spdk_bdev_desc *desc, enum spdk_bdev_claim_type type,
176 : : struct spdk_bdev_claim_opts *opts,
177 : : struct spdk_bdev_module *module)
178 : : {
179 : 12 : struct spdk_bdev *bdev = desc->bdev;
180 : :
181 [ + + ]: 12 : if (bdev->claim_module != NULL) {
182 : 4 : return -EPERM;
183 : : }
184 : :
185 : 8 : bdev->claim_type = type;
186 : 8 : bdev->claim_module = module;
187 : 8 : bdev->claim_desc = desc;
188 : :
189 : 8 : desc->claim_type = type;
190 : :
191 : 8 : return 0;
192 : 3 : }
193 : :
194 : : static void
195 : 32 : init_bdev(struct spdk_bdev *bdev, const char *name, uint64_t num_blocks)
196 : : {
197 [ - + ]: 32 : memset(bdev, 0, sizeof(*bdev));
198 [ - + ]: 32 : snprintf(bdev->name, sizeof(bdev->name), "%s", name);
199 : 32 : bdev->blockcnt = num_blocks;
200 : 32 : }
201 : :
202 : : static void
203 : 4 : create_bs_dev(void)
204 : : {
205 : 3 : struct spdk_bdev bdev;
206 : 4 : struct spdk_bs_dev *bs_dev = NULL;
207 : : struct blob_bdev *blob_bdev;
208 : : int rc;
209 : :
210 : 4 : init_bdev(&bdev, "bdev0", 16);
211 : 4 : g_bdev = &bdev;
212 : :
213 : 4 : rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev);
214 : 4 : CU_ASSERT(rc == 0);
215 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
216 : 4 : CU_ASSERT(bdev.open_cnt == 1);
217 : :
218 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
219 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
220 [ - + ]: 4 : CU_ASSERT(blob_bdev->desc->write);
221 : 4 : CU_ASSERT(blob_bdev->desc->bdev == g_bdev);
222 : 4 : CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
223 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
224 : :
225 : 4 : bs_dev->destroy(bs_dev);
226 : 4 : CU_ASSERT(bdev.open_cnt == 0);
227 : 4 : g_bdev = NULL;
228 : 4 : }
229 : :
230 : : static void
231 : 4 : create_bs_dev_ro(void)
232 : : {
233 : 3 : struct spdk_bdev bdev;
234 : 4 : struct spdk_bs_dev *bs_dev = NULL;
235 : : struct blob_bdev *blob_bdev;
236 : 4 : struct spdk_bdev_bs_dev_opts opts = { 0 };
237 : : int rc;
238 : :
239 : : /* opts with the wrong size returns -EINVAL */
240 : 4 : rc = spdk_bdev_create_bs_dev("nope", false, &opts, sizeof(opts) + 8, NULL, NULL, &bs_dev);
241 : 4 : CU_ASSERT(rc == -EINVAL);
242 : :
243 : : /* opts with the right size is OK, but can still fail if the device doesn't exist. */
244 : 4 : opts.opts_size = sizeof(opts);
245 : 4 : rc = spdk_bdev_create_bs_dev("nope", false, &opts, sizeof(opts), NULL, NULL, &bs_dev);
246 : 4 : CU_ASSERT(rc == -ENODEV);
247 : :
248 : 4 : init_bdev(&bdev, "bdev0", 16);
249 : 4 : g_bdev = &bdev;
250 : :
251 : : /* The normal way to create a read-only device */
252 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
253 : 4 : CU_ASSERT(rc == 0);
254 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
255 : 4 : CU_ASSERT(bdev.open_cnt == 1);
256 : :
257 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
258 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
259 [ - + ]: 4 : CU_ASSERT(!blob_bdev->desc->write);
260 : 4 : CU_ASSERT(blob_bdev->desc->bdev == g_bdev);
261 : 4 : CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
262 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
263 : :
264 : 4 : bs_dev->destroy(bs_dev);
265 : 4 : CU_ASSERT(bdev.open_cnt == 0);
266 : 4 : g_bdev = NULL;
267 : 4 : }
268 : :
269 : : static void
270 : 4 : create_bs_dev_rw(void)
271 : : {
272 : 3 : struct spdk_bdev bdev;
273 : 4 : struct spdk_bs_dev *bs_dev = NULL;
274 : : struct blob_bdev *blob_bdev;
275 : : int rc;
276 : :
277 : 4 : init_bdev(&bdev, "bdev0", 16);
278 : 4 : g_bdev = &bdev;
279 : :
280 : : /* This is equivalent to spdk_bdev_create_bs_dev_ext() */
281 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", true, NULL, 0, NULL, NULL, &bs_dev);
282 : 4 : CU_ASSERT(rc == 0);
283 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
284 : 4 : CU_ASSERT(bdev.open_cnt == 1);
285 : :
286 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
287 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
288 [ - + ]: 4 : CU_ASSERT(blob_bdev->desc->write);
289 : 4 : CU_ASSERT(blob_bdev->desc->bdev == g_bdev);
290 : 4 : CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
291 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
292 : :
293 : 4 : bs_dev->destroy(bs_dev);
294 : 4 : CU_ASSERT(bdev.open_cnt == 0);
295 : 4 : g_bdev = NULL;
296 : 4 : }
297 : :
298 : : static void
299 : 4 : claim_bs_dev(void)
300 : : {
301 : 3 : struct spdk_bdev bdev;
302 : 4 : struct spdk_bs_dev *bs_dev = NULL, *bs_dev2 = NULL;
303 : : struct blob_bdev *blob_bdev;
304 : : int rc;
305 : :
306 : 4 : init_bdev(&bdev, "bdev0", 16);
307 : 4 : g_bdev = &bdev;
308 : :
309 : 4 : rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev);
310 : 4 : CU_ASSERT(rc == 0);
311 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
312 : :
313 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
314 : 4 : CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
315 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
316 [ - + ]: 4 : CU_ASSERT(blob_bdev->desc->write);
317 : :
318 : : /* Can get an exclusive write claim */
319 : 4 : rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod);
320 : 4 : CU_ASSERT(rc == 0);
321 [ - + ]: 4 : CU_ASSERT(blob_bdev->desc->write);
322 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE);
323 : 4 : CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
324 : :
325 : : /* Claim blocks a second writer without messing up the first one. */
326 : 4 : rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev2);
327 : 4 : CU_ASSERT(rc == -EPERM);
328 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE);
329 : 4 : CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
330 : :
331 : : /* Claim blocks a second claim without messing up the first one. */
332 : 4 : rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod);
333 : 4 : CU_ASSERT(rc == -EPERM);
334 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE);
335 : 4 : CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
336 : :
337 : 4 : bs_dev->destroy(bs_dev);
338 : 4 : CU_ASSERT(bdev.open_cnt == 0);
339 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
340 : 4 : CU_ASSERT(bdev.claim_module == NULL);
341 : 4 : CU_ASSERT(bdev.claim_desc == NULL);
342 : 4 : g_bdev = NULL;
343 : 4 : }
344 : :
345 : : static void
346 : 4 : claim_bs_dev_ro(void)
347 : : {
348 : 3 : struct spdk_bdev bdev;
349 : 4 : struct spdk_bs_dev *bs_dev = NULL, *bs_dev2 = NULL;
350 : : struct blob_bdev *blob_bdev;
351 : : int rc;
352 : :
353 : 4 : init_bdev(&bdev, "bdev0", 16);
354 : 4 : g_bdev = &bdev;
355 : :
356 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
357 : 4 : CU_ASSERT(rc == 0);
358 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
359 : :
360 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
361 : 4 : CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
362 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
363 [ - + ]: 4 : CU_ASSERT(!blob_bdev->desc->write);
364 : :
365 : : /* Can get an shared reader claim */
366 : 4 : rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod);
367 : 4 : CU_ASSERT(rc == 0);
368 [ - + ]: 4 : CU_ASSERT(!blob_bdev->desc->write);
369 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_NONE);
370 : 4 : CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
371 : :
372 : : /* Claim blocks a writer without messing up the claim. */
373 : 4 : rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev2);
374 : 4 : CU_ASSERT(rc == -EPERM);
375 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_NONE);
376 : 4 : CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
377 : :
378 : : /* Another reader is just fine */
379 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev2);
380 : 4 : CU_ASSERT(rc == 0);
381 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev2 != NULL);
382 : 4 : bs_dev2->destroy(bs_dev2);
383 : :
384 : 4 : bs_dev->destroy(bs_dev);
385 : 4 : CU_ASSERT(bdev.open_cnt == 0);
386 : 4 : CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
387 : 4 : CU_ASSERT(bdev.claim_module == NULL);
388 : 4 : CU_ASSERT(bdev.claim_desc == NULL);
389 : 4 : g_bdev = NULL;
390 : 4 : }
391 : :
392 : : /*
393 : : * Verify that create_channel() and destroy_channel() increment and decrement the blob_bdev->refs.
394 : : */
395 : : static void
396 : 4 : deferred_destroy_refs(void)
397 : : {
398 : 3 : struct spdk_bdev bdev;
399 : : struct spdk_io_channel *ch1, *ch2;
400 : 4 : struct spdk_bs_dev *bs_dev = NULL;
401 : : struct blob_bdev *blob_bdev;
402 : : int rc;
403 : :
404 : 4 : set_thread(0);
405 : 4 : init_bdev(&bdev, "bdev0", 16);
406 : 4 : g_bdev = &bdev;
407 : :
408 : : /* Open a blob_bdev, verify reference count is 1. */
409 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
410 : 4 : CU_ASSERT(rc == 0);
411 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
412 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
413 : 4 : CU_ASSERT(blob_bdev->refs == 1);
414 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
415 : :
416 : : /* Verify reference count increases with channels on the same thread. */
417 : 4 : ch1 = bs_dev->create_channel(bs_dev);
418 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(ch1 != NULL);
419 : 4 : CU_ASSERT(blob_bdev->refs == 2);
420 : 4 : ch2 = bs_dev->create_channel(bs_dev);
421 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(ch2 != NULL);
422 : 4 : CU_ASSERT(blob_bdev->refs == 3);
423 : 4 : bs_dev->destroy_channel(bs_dev, ch1);
424 : 4 : CU_ASSERT(blob_bdev->refs == 2);
425 : 4 : bs_dev->destroy_channel(bs_dev, ch2);
426 : 4 : CU_ASSERT(blob_bdev->refs == 1);
427 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
428 : :
429 : : /* Verify reference count increases with channels on different threads. */
430 : 4 : ch1 = bs_dev->create_channel(bs_dev);
431 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(ch1 != NULL);
432 : 4 : CU_ASSERT(blob_bdev->refs == 2);
433 : 4 : set_thread(1);
434 : 4 : ch2 = bs_dev->create_channel(bs_dev);
435 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(ch2 != NULL);
436 : 4 : CU_ASSERT(blob_bdev->refs == 3);
437 : 4 : bs_dev->destroy_channel(bs_dev, ch1);
438 : 4 : CU_ASSERT(blob_bdev->refs == 2);
439 : 4 : bs_dev->destroy_channel(bs_dev, ch2);
440 : 4 : CU_ASSERT(blob_bdev->refs == 1);
441 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
442 : :
443 : 4 : set_thread(0);
444 : 4 : bs_dev->destroy(bs_dev);
445 : 4 : g_bdev = NULL;
446 : 4 : }
447 : :
448 : : /*
449 : : * When a channel is open bs_dev->destroy() should not free bs_dev until after the last channel is
450 : : * closed. Further, destroy() prevents the creation of new channels.
451 : : */
452 : : static void
453 : 4 : deferred_destroy_channels(void)
454 : : {
455 : 3 : struct spdk_bdev bdev;
456 : : struct spdk_io_channel *ch1, *ch2;
457 : 4 : struct spdk_bs_dev *bs_dev = NULL;
458 : : struct blob_bdev *blob_bdev;
459 : : int rc;
460 : :
461 : 4 : set_thread(0);
462 : 4 : init_bdev(&bdev, "bdev0", 16);
463 : :
464 : : /* Open bs_dev and sanity check */
465 : 4 : g_bdev = &bdev;
466 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
467 : 4 : CU_ASSERT(rc == 0);
468 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
469 : 4 : CU_ASSERT(bdev.open_cnt == 1);
470 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
471 : 4 : CU_ASSERT(blob_bdev->refs == 1);
472 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
473 : :
474 : : /* Create a channel, destroy the bs_dev. It should not be freed yet. */
475 : 4 : ch1 = bs_dev->create_channel(bs_dev);
476 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(ch1 != NULL);
477 : 4 : CU_ASSERT(blob_bdev->refs == 2);
478 : 4 : bs_dev->destroy(bs_dev);
479 : :
480 : : /* Destroy closes the bdev and prevents desc from being used for creating more channels. */
481 : 4 : CU_ASSERT(blob_bdev->desc == NULL);
482 : 4 : CU_ASSERT(bdev.open_cnt == 0);
483 : 4 : CU_ASSERT(blob_bdev->refs == 1);
484 : 4 : ch2 = bs_dev->create_channel(bs_dev);
485 : 4 : CU_ASSERT(ch2 == NULL)
486 : 4 : CU_ASSERT(blob_bdev->refs == 1);
487 : 4 : bs_dev->destroy_channel(bs_dev, ch1);
488 : 4 : g_bdev = NULL;
489 : :
490 : : /* Now bs_dev should have been freed. Builds with asan will verify. */
491 : 4 : }
492 : :
493 : : /*
494 : : * Verify that deferred destroy copes well with the last channel destruction being on a thread other
495 : : * than the thread used to obtain the bdev descriptor.
496 : : */
497 : : static void
498 : 4 : deferred_destroy_threads(void)
499 : : {
500 : 3 : struct spdk_bdev bdev;
501 : : struct spdk_io_channel *ch1, *ch2;
502 : 4 : struct spdk_bs_dev *bs_dev = NULL;
503 : : struct blob_bdev *blob_bdev;
504 : : int rc;
505 : :
506 : 4 : set_thread(0);
507 : 4 : init_bdev(&bdev, "bdev0", 16);
508 : 4 : g_bdev = &bdev;
509 : :
510 : : /* Open bs_dev and sanity check */
511 : 4 : rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
512 : 4 : CU_ASSERT(rc == 0);
513 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
514 : 4 : CU_ASSERT(bdev.open_cnt == 1);
515 : 4 : blob_bdev = (struct blob_bdev *)bs_dev;
516 : 4 : CU_ASSERT(blob_bdev->refs == 1);
517 : 4 : CU_ASSERT(blob_bdev->desc != NULL);
518 : :
519 : : /* Create two channels, each on their own thread. */
520 : 4 : ch1 = bs_dev->create_channel(bs_dev);
521 [ + + ]: 4 : SPDK_CU_ASSERT_FATAL(ch1 != NULL);
522 : 4 : CU_ASSERT(blob_bdev->refs == 2);
523 : 4 : CU_ASSERT(spdk_get_thread() == blob_bdev->desc->thread);
524 : 4 : set_thread(1);
525 : 4 : ch2 = bs_dev->create_channel(bs_dev);
526 [ - + ]: 4 : SPDK_CU_ASSERT_FATAL(ch2 != NULL);
527 : 4 : CU_ASSERT(blob_bdev->refs == 3);
528 : :
529 : : /* Destroy the bs_dev on thread 0, the channel on thread 0, then the channel on thread 1. */
530 : 4 : set_thread(0);
531 : 4 : bs_dev->destroy(bs_dev);
532 : 4 : CU_ASSERT(blob_bdev->desc == NULL);
533 : 4 : CU_ASSERT(bdev.open_cnt == 0);
534 : 4 : CU_ASSERT(blob_bdev->refs == 2);
535 : 4 : bs_dev->destroy_channel(bs_dev, ch1);
536 : 4 : CU_ASSERT(blob_bdev->refs == 1);
537 : 4 : set_thread(1);
538 : 4 : bs_dev->destroy_channel(bs_dev, ch2);
539 : 4 : set_thread(0);
540 : 4 : g_bdev = NULL;
541 : :
542 : : /* Now bs_dev should have been freed. Builds with asan will verify. */
543 : 4 : }
544 : :
545 : : int
546 : 4 : main(int argc, char **argv)
547 : : {
548 : : CU_pSuite suite;
549 : : unsigned int num_failures;
550 : :
551 : 4 : CU_initialize_registry();
552 : :
553 : 4 : suite = CU_add_suite("blob_bdev", NULL, NULL);
554 : :
555 : 4 : CU_ADD_TEST(suite, create_bs_dev);
556 : 4 : CU_ADD_TEST(suite, create_bs_dev_ro);
557 : 4 : CU_ADD_TEST(suite, create_bs_dev_rw);
558 : 4 : CU_ADD_TEST(suite, claim_bs_dev);
559 : 4 : CU_ADD_TEST(suite, claim_bs_dev_ro);
560 : 4 : CU_ADD_TEST(suite, deferred_destroy_refs);
561 : 4 : CU_ADD_TEST(suite, deferred_destroy_channels);
562 : 4 : CU_ADD_TEST(suite, deferred_destroy_threads);
563 : :
564 : 4 : allocate_threads(2);
565 : 4 : set_thread(0);
566 : :
567 : 4 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
568 : 4 : CU_cleanup_registry();
569 : :
570 : 4 : free_threads();
571 : :
572 : 4 : return num_failures;
573 : : }
|