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