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