xref: /spdk/test/unit/lib/blob/blob_bdev.c/blob_bdev_ut.c (revision a0d24145bf3d795cf89adc414320b138fae480ab)
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_cunit.h"
8 #include "common/lib/test_env.c"
9 #include "blob/bdev/blob_bdev.c"
10 
11 DEFINE_STUB(spdk_bdev_io_type_supported, bool, (struct spdk_bdev *bdev,
12 		enum spdk_bdev_io_type io_type), false);
13 DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *g_bdev_io));
14 DEFINE_STUB(spdk_bdev_queue_io_wait, int,
15 	    (struct spdk_bdev *bdev, struct spdk_io_channel *ch,
16 	     struct spdk_bdev_io_wait_entry *entry), 0);
17 DEFINE_STUB(spdk_bdev_read_blocks, int,
18 	    (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, void *buf,
19 	     uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
20 	     void *cb_arg), 0);
21 DEFINE_STUB(spdk_bdev_write_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 DEFINE_STUB(spdk_bdev_readv_blocks, int,
26 	    (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt,
27 	     uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
28 	     void *cb_arg), 0);
29 DEFINE_STUB(spdk_bdev_writev_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 DEFINE_STUB(spdk_bdev_readv_blocks_ext, 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, struct spdk_bdev_ext_io_opts *opts), 0);
37 DEFINE_STUB(spdk_bdev_writev_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 DEFINE_STUB(spdk_bdev_write_zeroes_blocks, int,
42 	    (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks,
43 	     uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
44 DEFINE_STUB(spdk_bdev_unmap_blocks, int,
45 	    (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks,
46 	     uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
47 DEFINE_STUB(spdk_bdev_copy_blocks, int,
48 	    (struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t dst_offset_blocks,
49 	     uint64_t src_offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb,
50 	     void *cb_arg), 0);
51 DEFINE_STUB(spdk_bdev_get_io_channel, struct spdk_io_channel *,
52 	    (struct spdk_bdev_desc *desc), NULL);
53 
54 struct spdk_bdev {
55 	char name[16];
56 	uint64_t blockcnt;
57 	uint32_t blocklen;
58 	uint32_t open_cnt;
59 	enum spdk_bdev_claim_type claim_type;
60 	struct spdk_bdev_module *claim_module;
61 	struct spdk_bdev_desc *claim_desc;
62 };
63 
64 struct spdk_bdev_desc {
65 	struct spdk_bdev *bdev;
66 	bool write;
67 	enum spdk_bdev_claim_type claim_type;
68 };
69 
70 struct spdk_bdev *g_bdev;
71 
72 static struct spdk_bdev_module g_bdev_mod = {
73 	.name = "blob_bdev_ut"
74 };
75 
76 static struct spdk_bdev *
77 get_bdev(const char *bdev_name)
78 {
79 	if (g_bdev == NULL) {
80 		return NULL;
81 	}
82 
83 	if (strcmp(bdev_name, g_bdev->name) != 0) {
84 		return NULL;
85 	}
86 
87 	return g_bdev;
88 }
89 
90 int
91 spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event_cb,
92 		   void *event_ctx, struct spdk_bdev_desc **_desc)
93 {
94 	struct spdk_bdev_desc *desc;
95 	struct spdk_bdev *bdev = get_bdev(bdev_name);
96 
97 	if (bdev == NULL) {
98 		return -ENODEV;
99 	}
100 
101 	if (write && bdev->claim_module != NULL) {
102 		return -EPERM;
103 	}
104 
105 	desc = calloc(1, sizeof(*desc));
106 	desc->bdev = g_bdev;
107 	desc->write = write;
108 	*_desc = desc;
109 	bdev->open_cnt++;
110 
111 	return 0;
112 }
113 
114 void
115 spdk_bdev_close(struct spdk_bdev_desc *desc)
116 {
117 	struct spdk_bdev *bdev = desc->bdev;
118 
119 	bdev->open_cnt--;
120 	if (bdev->claim_desc == desc) {
121 		bdev->claim_desc = NULL;
122 		bdev->claim_type = SPDK_BDEV_CLAIM_NONE;
123 		bdev->claim_module = NULL;
124 	}
125 	free(desc);
126 }
127 
128 struct spdk_bdev *
129 spdk_bdev_desc_get_bdev(struct spdk_bdev_desc *desc)
130 {
131 	return desc->bdev;
132 }
133 
134 uint64_t
135 spdk_bdev_get_num_blocks(const struct spdk_bdev *bdev)
136 {
137 	return bdev->blockcnt;
138 }
139 
140 uint32_t
141 spdk_bdev_get_block_size(const struct spdk_bdev *bdev)
142 {
143 	return bdev->blocklen;
144 }
145 
146 /* This is a simple approximation: it does not support shared claims */
147 int
148 spdk_bdev_module_claim_bdev_desc(struct spdk_bdev_desc *desc, enum spdk_bdev_claim_type type,
149 				 struct spdk_bdev_claim_opts *opts,
150 				 struct spdk_bdev_module *module)
151 {
152 	struct spdk_bdev *bdev = desc->bdev;
153 
154 	if (bdev->claim_module != NULL) {
155 		return -EPERM;
156 	}
157 
158 	bdev->claim_type = type;
159 	bdev->claim_module = module;
160 	bdev->claim_desc = desc;
161 
162 	desc->claim_type = type;
163 
164 	return 0;
165 }
166 
167 static void
168 init_bdev(struct spdk_bdev *bdev, const char *name, uint64_t num_blocks)
169 {
170 	memset(bdev, 0, sizeof(*bdev));
171 	snprintf(bdev->name, sizeof(bdev->name), "%s", name);
172 	bdev->blockcnt = num_blocks;
173 }
174 
175 static void
176 create_bs_dev(void)
177 {
178 	struct spdk_bdev bdev;
179 	struct spdk_bs_dev *bs_dev = NULL;
180 	struct blob_bdev *blob_bdev;
181 	int rc;
182 
183 	init_bdev(&bdev, "bdev0", 16);
184 	g_bdev = &bdev;
185 
186 	rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev);
187 	CU_ASSERT(rc == 0);
188 	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
189 	CU_ASSERT(bdev.open_cnt == 1);
190 
191 	blob_bdev = (struct blob_bdev *)bs_dev;
192 	CU_ASSERT(blob_bdev->desc != NULL);
193 	CU_ASSERT(blob_bdev->desc->write);
194 	CU_ASSERT(blob_bdev->desc->bdev == g_bdev);
195 	CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
196 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
197 
198 	bs_dev->destroy(bs_dev);
199 	CU_ASSERT(bdev.open_cnt == 0);
200 	g_bdev = NULL;
201 }
202 
203 static void
204 create_bs_dev_ro(void)
205 {
206 	struct spdk_bdev bdev;
207 	struct spdk_bs_dev *bs_dev = NULL;
208 	struct blob_bdev *blob_bdev;
209 	struct spdk_bdev_bs_dev_opts opts = { 0 };
210 	int rc;
211 
212 	/* opts with the wrong size returns -EINVAL */
213 	rc = spdk_bdev_create_bs_dev("nope", false, &opts, sizeof(opts) + 8, NULL, NULL, &bs_dev);
214 	CU_ASSERT(rc == -EINVAL);
215 
216 	/* opts with the right size is OK, but can still fail if the device doesn't exist. */
217 	opts.opts_size = sizeof(opts);
218 	rc = spdk_bdev_create_bs_dev("nope", false, &opts, sizeof(opts), NULL, NULL, &bs_dev);
219 	CU_ASSERT(rc == -ENODEV);
220 
221 	init_bdev(&bdev, "bdev0", 16);
222 	g_bdev = &bdev;
223 
224 	/* The normal way to create a read-only device */
225 	rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
226 	CU_ASSERT(rc == 0);
227 	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
228 	CU_ASSERT(bdev.open_cnt == 1);
229 
230 	blob_bdev = (struct blob_bdev *)bs_dev;
231 	CU_ASSERT(blob_bdev->desc != NULL);
232 	CU_ASSERT(!blob_bdev->desc->write);
233 	CU_ASSERT(blob_bdev->desc->bdev == g_bdev);
234 	CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
235 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
236 
237 	bs_dev->destroy(bs_dev);
238 	CU_ASSERT(bdev.open_cnt == 0);
239 	g_bdev = NULL;
240 }
241 
242 static void
243 create_bs_dev_rw(void)
244 {
245 	struct spdk_bdev bdev;
246 	struct spdk_bs_dev *bs_dev = NULL;
247 	struct blob_bdev *blob_bdev;
248 	int rc;
249 
250 	init_bdev(&bdev, "bdev0", 16);
251 	g_bdev = &bdev;
252 
253 	/* This is equivalent to spdk_bdev_create_bs_dev_ext() */
254 	rc = spdk_bdev_create_bs_dev("bdev0", true, NULL, 0, NULL, NULL, &bs_dev);
255 	CU_ASSERT(rc == 0);
256 	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
257 	CU_ASSERT(bdev.open_cnt == 1);
258 
259 	blob_bdev = (struct blob_bdev *)bs_dev;
260 	CU_ASSERT(blob_bdev->desc != NULL);
261 	CU_ASSERT(blob_bdev->desc->write);
262 	CU_ASSERT(blob_bdev->desc->bdev == g_bdev);
263 	CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
264 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
265 
266 	bs_dev->destroy(bs_dev);
267 	CU_ASSERT(bdev.open_cnt == 0);
268 	g_bdev = NULL;
269 }
270 
271 static void
272 claim_bs_dev(void)
273 {
274 	struct spdk_bdev bdev;
275 	struct spdk_bs_dev *bs_dev = NULL, *bs_dev2 = NULL;
276 	struct blob_bdev *blob_bdev;
277 	int rc;
278 
279 	init_bdev(&bdev, "bdev0", 16);
280 	g_bdev = &bdev;
281 
282 	rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev);
283 	CU_ASSERT(rc == 0);
284 	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
285 
286 	blob_bdev = (struct blob_bdev *)bs_dev;
287 	CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
288 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
289 	CU_ASSERT(blob_bdev->desc->write);
290 
291 	/* Can get an exclusive write claim */
292 	rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod);
293 	CU_ASSERT(rc == 0);
294 	CU_ASSERT(blob_bdev->desc->write);
295 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE);
296 	CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
297 
298 	/* Claim blocks a second writer without messing up the first one. */
299 	rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev2);
300 	CU_ASSERT(rc == -EPERM);
301 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE);
302 	CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
303 
304 	/* Claim blocks a second claim without messing up the first one. */
305 	rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod);
306 	CU_ASSERT(rc == -EPERM);
307 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE);
308 	CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
309 
310 	bs_dev->destroy(bs_dev);
311 	CU_ASSERT(bdev.open_cnt == 0);
312 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
313 	CU_ASSERT(bdev.claim_module == NULL);
314 	CU_ASSERT(bdev.claim_desc == NULL);
315 	g_bdev = NULL;
316 }
317 
318 static void
319 claim_bs_dev_ro(void)
320 {
321 	struct spdk_bdev bdev;
322 	struct spdk_bs_dev *bs_dev = NULL, *bs_dev2 = NULL;
323 	struct blob_bdev *blob_bdev;
324 	int rc;
325 
326 	init_bdev(&bdev, "bdev0", 16);
327 	g_bdev = &bdev;
328 
329 	rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev);
330 	CU_ASSERT(rc == 0);
331 	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
332 
333 	blob_bdev = (struct blob_bdev *)bs_dev;
334 	CU_ASSERT(blob_bdev->desc->claim_type == SPDK_BDEV_CLAIM_NONE);
335 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
336 	CU_ASSERT(!blob_bdev->desc->write);
337 
338 	/* Can get an shared reader claim */
339 	rc = spdk_bs_bdev_claim(bs_dev, &g_bdev_mod);
340 	CU_ASSERT(rc == 0);
341 	CU_ASSERT(!blob_bdev->desc->write);
342 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_NONE);
343 	CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
344 
345 	/* Claim blocks a writer without messing up the claim. */
346 	rc = spdk_bdev_create_bs_dev_ext("bdev0", NULL, NULL, &bs_dev2);
347 	CU_ASSERT(rc == -EPERM);
348 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_READ_MANY_WRITE_NONE);
349 	CU_ASSERT(bdev.claim_desc == blob_bdev->desc);
350 
351 	/* Another reader is just fine */
352 	rc = spdk_bdev_create_bs_dev("bdev0", false, NULL, 0, NULL, NULL, &bs_dev2);
353 	CU_ASSERT(rc == 0);
354 	SPDK_CU_ASSERT_FATAL(bs_dev2 != NULL);
355 	bs_dev2->destroy(bs_dev2);
356 
357 	bs_dev->destroy(bs_dev);
358 	CU_ASSERT(bdev.open_cnt == 0);
359 	CU_ASSERT(bdev.claim_type == SPDK_BDEV_CLAIM_NONE);
360 	CU_ASSERT(bdev.claim_module == NULL);
361 	CU_ASSERT(bdev.claim_desc == NULL);
362 	g_bdev = NULL;
363 }
364 
365 int
366 main(int argc, char **argv)
367 {
368 	CU_pSuite	suite;
369 	unsigned int	num_failures;
370 
371 	CU_set_error_action(CUEA_ABORT);
372 	CU_initialize_registry();
373 
374 	suite = CU_add_suite("blob_bdev", NULL, NULL);
375 
376 	CU_ADD_TEST(suite, create_bs_dev);
377 	CU_ADD_TEST(suite, create_bs_dev_ro);
378 	CU_ADD_TEST(suite, create_bs_dev_rw);
379 	CU_ADD_TEST(suite, claim_bs_dev);
380 	CU_ADD_TEST(suite, claim_bs_dev_ro);
381 
382 	CU_basic_set_mode(CU_BRM_VERBOSE);
383 	CU_basic_run_tests();
384 	num_failures = CU_get_number_of_failures();
385 	CU_cleanup_registry();
386 
387 	return num_failures;
388 }
389