Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2022 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk_internal/cunit.h"
8 : : #include "spdk/env.h"
9 : :
10 : : #include "common/lib/ut_multithread.c"
11 : :
12 : : #include "bdev/raid/concat.c"
13 : : #include "../common.c"
14 : :
15 : 0 : DEFINE_STUB(spdk_bdev_readv_blocks_with_md, int, (struct spdk_bdev_desc *desc,
16 : : struct spdk_io_channel *ch,
17 : : struct iovec *iov, int iovcnt, void *md,
18 : : uint64_t offset_blocks, uint64_t num_blocks,
19 : : spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
20 : 0 : DEFINE_STUB(spdk_bdev_writev_blocks_with_md, int, (struct spdk_bdev_desc *desc,
21 : : struct spdk_io_channel *ch,
22 : : struct iovec *iov, int iovcnt, void *md,
23 : : uint64_t offset_blocks, uint64_t num_blocks,
24 : : spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
25 : :
26 : : #define BLOCK_LEN (4096)
27 : :
28 : : enum CONCAT_IO_TYPE {
29 : : CONCAT_NONE = 0,
30 : : CONCAT_WRITEV,
31 : : CONCAT_READV,
32 : : CONCAT_FLUSH,
33 : : CONCAT_UNMAP,
34 : : };
35 : :
36 : : #define MAX_RECORDS (10)
37 : : /*
38 : : * Store the information of io requests sent to the underlying bdevs.
39 : : * For a single null payload request to the concat bdev,
40 : : * we may send multiple requests to the underling bdevs,
41 : : * so we store the io request information to arrays.
42 : : */
43 : : struct req_records {
44 : : uint64_t offset_blocks[MAX_RECORDS];
45 : : uint64_t num_blocks[MAX_RECORDS];
46 : : enum CONCAT_IO_TYPE io_type[MAX_RECORDS];
47 : : int count;
48 : : void *md;
49 : : } g_req_records;
50 : :
51 : : /*
52 : : * g_succeed is true means the spdk_bdev_readv/writev/unmap/flush_blocks
53 : : * functions will return 0.
54 : : * g_succeed is false means the spdk_bdev_readv/writev/unmap/flush_blocks
55 : : * functions will return -ENOMEM.
56 : : * We always set it to false before an IO request, then the raid_bdev_queue_io_wait
57 : : * function will re-submit the request, and the raid_bdev_queue_io_wait function will
58 : : * set g_succeed to true, then the IO will succeed next time.
59 : : */
60 : : bool g_succeed;
61 : :
62 : 3 : DEFINE_STUB_V(raid_bdev_module_list_add, (struct raid_bdev_module *raid_module));
63 : 1182 : DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *bdev_io));
64 : :
65 : : int
66 : 792 : spdk_bdev_readv_blocks_ext(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
67 : : struct iovec *iov, int iovcnt, uint64_t offset_blocks, uint64_t num_blocks,
68 : : spdk_bdev_io_completion_cb cb, void *cb_arg, struct spdk_bdev_ext_io_opts *opts)
69 : : {
70 [ + + + + ]: 792 : if (g_succeed) {
71 : 396 : int i = g_req_records.count;
72 : :
73 : 396 : g_req_records.offset_blocks[i] = offset_blocks;
74 : 396 : g_req_records.num_blocks[i] = num_blocks;
75 : 396 : g_req_records.io_type[i] = CONCAT_READV;
76 : 396 : g_req_records.count++;
77 : 396 : cb(NULL, true, cb_arg);
78 : 396 : g_req_records.md = opts->metadata;
79 : 396 : return 0;
80 : : } else {
81 : 396 : return -ENOMEM;
82 : : }
83 : : }
84 : :
85 : : int
86 : 792 : spdk_bdev_writev_blocks_ext(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
87 : : struct iovec *iov, int iovcnt, uint64_t offset_blocks, uint64_t num_blocks,
88 : : spdk_bdev_io_completion_cb cb, void *cb_arg, struct spdk_bdev_ext_io_opts *opts)
89 : : {
90 [ + + + + ]: 792 : if (g_succeed) {
91 : 396 : int i = g_req_records.count;
92 : :
93 : 396 : g_req_records.offset_blocks[i] = offset_blocks;
94 : 396 : g_req_records.num_blocks[i] = num_blocks;
95 : 396 : g_req_records.io_type[i] = CONCAT_WRITEV;
96 : 396 : g_req_records.count++;
97 : 396 : cb(NULL, true, cb_arg);
98 : 396 : g_req_records.md = opts->metadata;
99 : 396 : return 0;
100 : : } else {
101 : 396 : return -ENOMEM;
102 : : }
103 : : }
104 : :
105 : : int
106 : 294 : spdk_bdev_unmap_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
107 : : uint64_t offset_blocks, uint64_t num_blocks,
108 : : spdk_bdev_io_completion_cb cb, void *cb_arg)
109 : : {
110 [ + + + + ]: 294 : if (g_succeed) {
111 : 195 : int i = g_req_records.count;
112 : :
113 : 195 : g_req_records.offset_blocks[i] = offset_blocks;
114 : 195 : g_req_records.num_blocks[i] = num_blocks;
115 : 195 : g_req_records.io_type[i] = CONCAT_UNMAP;
116 : 195 : g_req_records.count++;
117 : 195 : cb(NULL, true, cb_arg);
118 : 195 : return 0;
119 : : } else {
120 : 99 : return -ENOMEM;
121 : : }
122 : : }
123 : :
124 : : int
125 : 294 : spdk_bdev_flush_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
126 : : uint64_t offset_blocks, uint64_t num_blocks,
127 : : spdk_bdev_io_completion_cb cb, void *cb_arg)
128 : : {
129 [ + + + + ]: 294 : if (g_succeed) {
130 : 195 : int i = g_req_records.count;
131 : :
132 : 195 : g_req_records.offset_blocks[i] = offset_blocks;
133 : 195 : g_req_records.num_blocks[i] = num_blocks;
134 : 195 : g_req_records.io_type[i] = CONCAT_FLUSH;
135 : 195 : g_req_records.count++;
136 : 195 : cb(NULL, true, cb_arg);
137 : 195 : return 0;
138 : : } else {
139 : 99 : return -ENOMEM;
140 : : }
141 : : }
142 : :
143 : : void
144 : 990 : raid_bdev_queue_io_wait(struct raid_bdev_io *raid_io, struct spdk_bdev *bdev,
145 : : struct spdk_io_channel *ch, spdk_bdev_io_wait_cb cb_fn)
146 : : {
147 : 990 : g_succeed = true;
148 : 990 : cb_fn(raid_io);
149 : 990 : }
150 : :
151 : : static void
152 : 990 : raid_test_bdev_io_complete(struct raid_bdev_io *raid_io, enum spdk_bdev_io_status status)
153 : : {
154 : 990 : CU_ASSERT(status == SPDK_BDEV_IO_STATUS_SUCCESS);
155 : 990 : }
156 : :
157 : : static void
158 : 990 : init_globals(void)
159 : : {
160 : : int i;
161 : :
162 [ + + ]: 10890 : for (i = 0; i < MAX_RECORDS; i++) {
163 : 9900 : g_req_records.offset_blocks[i] = 0;
164 : 9900 : g_req_records.num_blocks[i] = 0;
165 : 9900 : g_req_records.io_type[i] = CONCAT_NONE;
166 : : }
167 : 990 : g_req_records.count = 0;
168 : 990 : g_succeed = false;
169 : 990 : }
170 : :
171 : : static int
172 : 3 : test_setup(void)
173 : : {
174 : 3 : uint8_t num_base_bdevs_values[] = { 3, 4, 5 };
175 : 3 : uint64_t base_bdev_blockcnt_values[] = { 1, 1024, 1024 * 1024 };
176 : 3 : uint32_t base_bdev_blocklen_values[] = { 512, 4096 };
177 : 3 : uint32_t strip_size_kb_values[] = { 1, 4, 128 };
178 : : uint8_t *num_base_bdevs;
179 : : uint64_t *base_bdev_blockcnt;
180 : : uint32_t *base_bdev_blocklen;
181 : : uint32_t *strip_size_kb;
182 : 3 : struct raid_params params;
183 : : uint64_t params_count;
184 : : int rc;
185 : :
186 : 3 : params_count = SPDK_COUNTOF(num_base_bdevs_values) *
187 : : SPDK_COUNTOF(base_bdev_blockcnt_values) *
188 : : SPDK_COUNTOF(base_bdev_blocklen_values) *
189 : : SPDK_COUNTOF(strip_size_kb_values);
190 : 3 : rc = raid_test_params_alloc(params_count);
191 [ - + ]: 3 : if (rc) {
192 : 0 : return rc;
193 : : }
194 : :
195 [ + + ]: 12 : ARRAY_FOR_EACH(num_base_bdevs_values, num_base_bdevs) {
196 [ + + ]: 36 : ARRAY_FOR_EACH(base_bdev_blockcnt_values, base_bdev_blockcnt) {
197 [ + + ]: 81 : ARRAY_FOR_EACH(base_bdev_blocklen_values, base_bdev_blocklen) {
198 [ + + ]: 216 : ARRAY_FOR_EACH(strip_size_kb_values, strip_size_kb) {
199 : 162 : params.num_base_bdevs = *num_base_bdevs;
200 : 162 : params.base_bdev_blockcnt = *base_bdev_blockcnt;
201 : 162 : params.base_bdev_blocklen = *base_bdev_blocklen;
202 [ - + ]: 162 : params.strip_size = *strip_size_kb * 1024 / *base_bdev_blocklen;
203 : 162 : params.md_len = 0;
204 [ + + ]: 162 : if (params.strip_size == 0 ||
205 [ + + ]: 135 : params.strip_size > *base_bdev_blockcnt) {
206 : 63 : continue;
207 : : }
208 : 99 : raid_test_params_add(¶ms);
209 : : }
210 : : }
211 : : }
212 : : }
213 : :
214 : 3 : return 0;
215 : : }
216 : :
217 : : static int
218 : 3 : test_cleanup(void)
219 : : {
220 : 3 : raid_test_params_free();
221 : 3 : return 0;
222 : : }
223 : :
224 : : static struct raid_bdev *
225 : 1089 : create_concat(struct raid_params *params)
226 : : {
227 : 1089 : struct raid_bdev *raid_bdev = raid_test_create_raid_bdev(params, &g_concat_module);
228 : :
229 : 1089 : CU_ASSERT(concat_start(raid_bdev) == 0);
230 : 1089 : return raid_bdev;
231 : : }
232 : :
233 : : static void
234 : 1089 : delete_concat(struct raid_bdev *raid_bdev)
235 : : {
236 : 1089 : concat_stop(raid_bdev);
237 : 1089 : raid_test_delete_raid_bdev(raid_bdev);
238 : 1089 : }
239 : :
240 : : static void
241 : 3 : test_concat_start(void)
242 : : {
243 : : struct raid_bdev *raid_bdev;
244 : : struct raid_params *params;
245 : : struct concat_block_range *block_range;
246 : : uint64_t total_blockcnt;
247 : : int i;
248 : :
249 [ + + ]: 102 : RAID_PARAMS_FOR_EACH(params) {
250 : 99 : raid_bdev = create_concat(params);
251 : 99 : block_range = raid_bdev->module_private;
252 : 99 : total_blockcnt = 0;
253 [ + + ]: 495 : for (i = 0; i < params->num_base_bdevs; i++) {
254 : 396 : CU_ASSERT(block_range[i].start == total_blockcnt);
255 : 396 : CU_ASSERT(block_range[i].length == params->base_bdev_blockcnt);
256 : 396 : total_blockcnt += params->base_bdev_blockcnt;
257 : : }
258 : 99 : delete_concat(raid_bdev);
259 : : }
260 : 3 : }
261 : :
262 : : static void
263 : 990 : raid_io_cleanup(struct raid_bdev_io *raid_io)
264 : : {
265 [ + + ]: 990 : if (raid_io->iovs) {
266 : 792 : free(raid_io->iovs->iov_base);
267 : 792 : free(raid_io->iovs);
268 : : }
269 : :
270 : 990 : free(raid_io);
271 : 990 : }
272 : :
273 : : static void
274 : 990 : raid_io_initialize(struct raid_bdev_io *raid_io, struct raid_bdev_io_channel *raid_ch,
275 : : struct raid_bdev *raid_bdev, uint64_t lba, uint64_t blocks, int16_t iotype)
276 : : {
277 : : struct iovec *iovs;
278 : : int iovcnt;
279 : : void *md_buf;
280 : :
281 [ + + + + ]: 990 : if (iotype == SPDK_BDEV_IO_TYPE_UNMAP || iotype == SPDK_BDEV_IO_TYPE_FLUSH) {
282 : 198 : iovs = NULL;
283 : 198 : iovcnt = 0;
284 : 198 : md_buf = NULL;
285 : : } else {
286 : 792 : iovcnt = 1;
287 : 792 : iovs = calloc(iovcnt, sizeof(struct iovec));
288 [ - + ]: 792 : SPDK_CU_ASSERT_FATAL(iovs != NULL);
289 : 792 : iovs->iov_len = raid_io->num_blocks * BLOCK_LEN;
290 : 792 : iovs->iov_base = calloc(1, iovs->iov_len);
291 [ - + ]: 792 : SPDK_CU_ASSERT_FATAL(iovs->iov_base != NULL);
292 : 792 : md_buf = (void *)0xAEDFEBAC;
293 : : }
294 : :
295 : 990 : raid_test_bdev_io_init(raid_io, raid_bdev, raid_ch, iotype, lba, blocks, iovs, iovcnt, md_buf);
296 : 990 : }
297 : :
298 : : static void
299 : 198 : submit_and_verify_rw(enum CONCAT_IO_TYPE io_type, struct raid_params *params)
300 : : {
301 : : struct raid_bdev *raid_bdev;
302 : : struct raid_bdev_io *raid_io;
303 : : struct raid_bdev_io_channel *raid_ch;
304 : : uint64_t lba, blocks;
305 : : int i;
306 : :
307 : 198 : lba = 0;
308 : 198 : blocks = 1;
309 [ + + ]: 990 : for (i = 0; i < params->num_base_bdevs; i++) {
310 : 792 : init_globals();
311 : 792 : raid_bdev = create_concat(params);
312 : 792 : raid_io = calloc(1, sizeof(*raid_io));
313 [ - + ]: 792 : SPDK_CU_ASSERT_FATAL(raid_io != NULL);
314 : 792 : raid_ch = raid_test_create_io_channel(raid_bdev);
315 : :
316 [ + + - - : 792 : switch (io_type) {
- ]
317 : 396 : case CONCAT_WRITEV:
318 : 396 : raid_io_initialize(raid_io, raid_ch, raid_bdev, lba, blocks, SPDK_BDEV_IO_TYPE_WRITE);
319 : 396 : concat_submit_rw_request(raid_io);
320 : 396 : break;
321 : 396 : case CONCAT_READV:
322 : 396 : raid_io_initialize(raid_io, raid_ch, raid_bdev, lba, blocks, SPDK_BDEV_IO_TYPE_READ);
323 : 396 : concat_submit_rw_request(raid_io);
324 : 396 : break;
325 : 0 : case CONCAT_UNMAP:
326 : 0 : raid_io_initialize(raid_io, raid_ch, raid_bdev, lba, blocks, SPDK_BDEV_IO_TYPE_UNMAP);
327 : 0 : concat_submit_null_payload_request(raid_io);
328 : 0 : break;
329 : 0 : case CONCAT_FLUSH:
330 : 0 : raid_io_initialize(raid_io, raid_ch, raid_bdev, lba, blocks, SPDK_BDEV_IO_TYPE_FLUSH);
331 : 0 : concat_submit_null_payload_request(raid_io);
332 : 0 : break;
333 : 0 : default:
334 : 0 : CU_ASSERT(false);
335 : : }
336 : :
337 : : /*
338 : : * We submit request to the first lba of each underlying device,
339 : : * so the offset of the underling device should always be 0.
340 : : */
341 : 792 : CU_ASSERT(g_req_records.offset_blocks[0] == 0);
342 : 792 : CU_ASSERT(g_req_records.num_blocks[0] == blocks);
343 : 792 : CU_ASSERT(g_req_records.io_type[0] == io_type);
344 : 792 : CU_ASSERT(g_req_records.count == 1);
345 : 792 : CU_ASSERT(g_req_records.md == (void *)0xAEDFEBAC);
346 : 792 : raid_io_cleanup(raid_io);
347 : 792 : raid_test_destroy_io_channel(raid_ch);
348 : 792 : delete_concat(raid_bdev);
349 : 792 : lba += params->base_bdev_blockcnt;
350 : : }
351 : 198 : }
352 : :
353 : : static void
354 : 3 : test_concat_rw(void)
355 : : {
356 : : struct raid_params *params;
357 : 3 : enum CONCAT_IO_TYPE io_type_list[] = {CONCAT_WRITEV, CONCAT_READV};
358 : : enum CONCAT_IO_TYPE io_type;
359 : : int i;
360 : :
361 [ + + ]: 102 : RAID_PARAMS_FOR_EACH(params) {
362 [ + + ]: 297 : for (i = 0; i < 2; i ++) {
363 : 198 : io_type = io_type_list[i];
364 : 198 : submit_and_verify_rw(io_type, params);
365 : : }
366 : : }
367 : 3 : }
368 : :
369 : : static void
370 : 198 : submit_and_verify_null_payload(enum CONCAT_IO_TYPE io_type, struct raid_params *params)
371 : : {
372 : : struct raid_bdev *raid_bdev;
373 : : struct raid_bdev_io *raid_io;
374 : : struct raid_bdev_io_channel *raid_ch;
375 : : uint64_t lba, blocks;
376 : :
377 : : /*
378 : : * In this unittest, all base bdevs have the same blockcnt.
379 : : * If the base_bdev_blockcnt > 1, the request will start from
380 : : * the second bdev, and across two bdevs.
381 : : * If the base_bdev_blockcnt == 1, the request will start from
382 : : * the third bdev. In this case, if there are only 3 bdevs,
383 : : * we can not set blocks to base_bdev_blockcnt + 1 because the request
384 : : * will be beyond the end of the last bdev, so we set the blocks to 1
385 : : */
386 : 198 : lba = params->base_bdev_blockcnt + 1;
387 [ + + + + ]: 198 : if (params->base_bdev_blockcnt == 1 && params->num_base_bdevs == 3) {
388 : 6 : blocks = 1;
389 : : } else {
390 : 192 : blocks = params->base_bdev_blockcnt + 1;
391 : : }
392 : 198 : init_globals();
393 : 198 : raid_bdev = create_concat(params);
394 : 198 : raid_io = calloc(1, sizeof(*raid_io));
395 [ - + ]: 198 : SPDK_CU_ASSERT_FATAL(raid_io != NULL);
396 : 198 : raid_ch = raid_test_create_io_channel(raid_bdev);
397 : :
398 [ + + - ]: 198 : switch (io_type) {
399 : 99 : case CONCAT_UNMAP:
400 : 99 : raid_io_initialize(raid_io, raid_ch, raid_bdev, lba, blocks, SPDK_BDEV_IO_TYPE_UNMAP);
401 : 99 : concat_submit_null_payload_request(raid_io);
402 : 99 : break;
403 : 99 : case CONCAT_FLUSH:
404 : 99 : raid_io_initialize(raid_io, raid_ch, raid_bdev, lba, blocks, SPDK_BDEV_IO_TYPE_FLUSH);
405 : 99 : concat_submit_null_payload_request(raid_io);
406 : 99 : break;
407 : 0 : default:
408 : 0 : CU_ASSERT(false);
409 : : }
410 : :
411 [ + + ]: 198 : if (params->base_bdev_blockcnt == 1) {
412 [ + + ]: 18 : if (params->num_base_bdevs == 3) {
413 : 6 : CU_ASSERT(g_req_records.count == 1);
414 : 6 : CU_ASSERT(g_req_records.offset_blocks[0] == 0);
415 : 6 : CU_ASSERT(g_req_records.num_blocks[0] == 1);
416 : : } else {
417 : 12 : CU_ASSERT(g_req_records.count == 2);
418 : 12 : CU_ASSERT(g_req_records.offset_blocks[0] == 0);
419 : 12 : CU_ASSERT(g_req_records.num_blocks[0] == 1);
420 : 12 : CU_ASSERT(g_req_records.io_type[0] == io_type);
421 : 12 : CU_ASSERT(g_req_records.offset_blocks[1] == 0);
422 : 12 : CU_ASSERT(g_req_records.num_blocks[1] == 1);
423 : 12 : CU_ASSERT(g_req_records.io_type[1] == io_type);
424 : : }
425 : : } else {
426 : 180 : CU_ASSERT(g_req_records.count == 2);
427 : 180 : CU_ASSERT(g_req_records.offset_blocks[0] == 1);
428 : 180 : CU_ASSERT(g_req_records.num_blocks[0] == params->base_bdev_blockcnt - 1);
429 : 180 : CU_ASSERT(g_req_records.io_type[0] == io_type);
430 : 180 : CU_ASSERT(g_req_records.offset_blocks[1] == 0);
431 : 180 : CU_ASSERT(g_req_records.num_blocks[1] == 2);
432 : 180 : CU_ASSERT(g_req_records.io_type[1] == io_type);
433 : : }
434 : 198 : raid_io_cleanup(raid_io);
435 : 198 : raid_test_destroy_io_channel(raid_ch);
436 : 198 : delete_concat(raid_bdev);
437 : 198 : }
438 : :
439 : : static void
440 : 3 : test_concat_null_payload(void)
441 : : {
442 : : struct raid_params *params;
443 : 3 : enum CONCAT_IO_TYPE io_type_list[] = {CONCAT_FLUSH, CONCAT_UNMAP};
444 : : enum CONCAT_IO_TYPE io_type;
445 : : int i;
446 : :
447 [ + + ]: 102 : RAID_PARAMS_FOR_EACH(params) {
448 [ + + ]: 297 : for (i = 0; i < 2; i ++) {
449 : 198 : io_type = io_type_list[i];
450 : 198 : submit_and_verify_null_payload(io_type, params);
451 : : }
452 : : }
453 : 3 : }
454 : :
455 : : int
456 : 3 : main(int argc, char **argv)
457 : : {
458 : 3 : CU_pSuite suite = NULL;
459 : : unsigned int num_failures;
460 : :
461 : 3 : CU_initialize_registry();
462 : :
463 : 3 : suite = CU_add_suite("concat", test_setup, test_cleanup);
464 : 3 : CU_ADD_TEST(suite, test_concat_start);
465 : 3 : CU_ADD_TEST(suite, test_concat_rw);
466 : 3 : CU_ADD_TEST(suite, test_concat_null_payload);
467 : :
468 : 3 : allocate_threads(1);
469 : 3 : set_thread(0);
470 : :
471 : 3 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
472 : 3 : CU_cleanup_registry();
473 : :
474 : 3 : free_threads();
475 : :
476 : 3 : return num_failures;
477 : : }
|