xref: /spdk/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c (revision ae7b5890ef728af40bd233a5011b924c482603bf)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk_cunit.h"
37 #include "common/lib/test_env.c"
38 
39 #include "ftl/ftl_io.c"
40 
41 DEFINE_STUB(ftl_trace_alloc_id, uint64_t, (struct spdk_ftl_dev *dev), 0);
42 DEFINE_STUB_V(ftl_band_acquire_lba_map, (struct ftl_band *band));
43 DEFINE_STUB_V(ftl_band_release_lba_map, (struct ftl_band *band));
44 
45 static struct spdk_ftl_dev *
46 setup_device(void)
47 {
48 	struct spdk_ftl_dev *dev;
49 	struct ftl_io_channel *ioch;
50 
51 	dev = calloc(1, sizeof(*dev));
52 	SPDK_CU_ASSERT_FATAL(dev != NULL);
53 	dev->ioch = calloc(1, sizeof(*ioch) + sizeof(struct spdk_io_channel));
54 	SPDK_CU_ASSERT_FATAL(dev->ioch != NULL);
55 
56 	ioch = spdk_io_channel_get_ctx(dev->ioch);
57 
58 	ioch->elem_size = sizeof(struct ftl_md_io);
59 	ioch->io_pool = spdk_mempool_create("io-pool", 4096, ioch->elem_size, 0, 0);
60 
61 	SPDK_CU_ASSERT_FATAL(ioch->io_pool != NULL);
62 
63 	return dev;
64 }
65 
66 static void
67 free_device(struct spdk_ftl_dev *dev)
68 {
69 	struct ftl_io_channel *ioch;
70 
71 	ioch = spdk_io_channel_get_ctx(dev->ioch);
72 	spdk_mempool_free(ioch->io_pool);
73 
74 	free(dev->ioch);
75 	free(dev);
76 }
77 
78 static void
79 setup_io(struct ftl_io *io, struct spdk_ftl_dev *dev, ftl_io_fn cb, void *ctx)
80 {
81 	io->dev = dev;
82 	io->cb_fn = cb;
83 	io->cb_ctx = ctx;
84 }
85 
86 static struct ftl_io *
87 alloc_io(struct spdk_ftl_dev *dev, ftl_io_fn cb, void *ctx)
88 {
89 	struct ftl_io *io;
90 
91 	io = ftl_io_alloc(dev->ioch);
92 	SPDK_CU_ASSERT_FATAL(io != NULL);
93 	setup_io(io, dev, cb, ctx);
94 
95 	return io;
96 }
97 
98 static void
99 io_complete_cb(struct ftl_io *io, void *ctx, int status)
100 {
101 	*(int *)ctx = status;
102 }
103 
104 static void
105 test_completion(void)
106 {
107 	struct spdk_ftl_dev *dev;
108 	struct ftl_io_channel *ioch;
109 	struct ftl_io *io;
110 	int req, status = 0;
111 	size_t pool_size;
112 
113 	dev = setup_device();
114 	ioch = spdk_io_channel_get_ctx(dev->ioch);
115 	pool_size = spdk_mempool_count(ioch->io_pool);
116 
117 	io = alloc_io(dev, io_complete_cb, &status);
118 	io->status = -EIO;
119 
120 #define NUM_REQUESTS 16
121 	for (req = 0; req < NUM_REQUESTS; ++req) {
122 		ftl_io_inc_req(io);
123 		CU_ASSERT_FALSE(ftl_io_done(io));
124 	}
125 
126 	CU_ASSERT_EQUAL(io->req_cnt, NUM_REQUESTS);
127 
128 	for (req = 0; req < (NUM_REQUESTS - 1); ++req) {
129 		ftl_io_dec_req(io);
130 		CU_ASSERT_FALSE(ftl_io_done(io));
131 	}
132 
133 	CU_ASSERT_EQUAL(io->req_cnt, 1);
134 
135 	ftl_io_dec_req(io);
136 	CU_ASSERT_TRUE(ftl_io_done(io));
137 
138 	ftl_io_complete(io);
139 	CU_ASSERT_EQUAL(status, -EIO);
140 
141 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
142 
143 	free_device(dev);
144 }
145 
146 static void
147 test_alloc_free(void)
148 {
149 	struct spdk_ftl_dev *dev;
150 	struct ftl_io_channel *ioch;
151 	struct ftl_io *parent, *child;
152 	int parent_status = -1;
153 	size_t pool_size;
154 
155 	dev = setup_device();
156 	ioch = spdk_io_channel_get_ctx(dev->ioch);
157 	pool_size = spdk_mempool_count(ioch->io_pool);
158 
159 	parent = alloc_io(dev, io_complete_cb, &parent_status);
160 	SPDK_CU_ASSERT_FATAL(parent != NULL);
161 	child = ftl_io_alloc_child(parent);
162 	SPDK_CU_ASSERT_FATAL(child != NULL);
163 
164 	ftl_io_free(child);
165 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - 1);
166 
167 	child = ftl_io_alloc_child(parent);
168 	SPDK_CU_ASSERT_FATAL(child != NULL);
169 	ftl_io_complete(child);
170 	CU_ASSERT_EQUAL(parent_status, -1);
171 	ftl_io_complete(parent);
172 	CU_ASSERT_EQUAL(parent_status, 0);
173 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
174 
175 	parent_status = -1;
176 	parent = alloc_io(dev, io_complete_cb, &parent_status);
177 	SPDK_CU_ASSERT_FATAL(parent != NULL);
178 	child = ftl_io_alloc_child(parent);
179 	SPDK_CU_ASSERT_FATAL(child != NULL);
180 
181 	ftl_io_free(child);
182 	CU_ASSERT_EQUAL(parent_status, -1);
183 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - 1);
184 	ftl_io_complete(parent);
185 	CU_ASSERT_EQUAL(parent_status, 0);
186 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
187 
188 	free_device(dev);
189 }
190 
191 static void
192 test_child_requests(void)
193 {
194 	struct spdk_ftl_dev *dev;
195 	struct ftl_io_channel *ioch;
196 #define MAX_CHILDREN 16
197 	struct ftl_io *parent, *child[MAX_CHILDREN];
198 	int status[MAX_CHILDREN + 1], i;
199 	size_t pool_size;
200 
201 	dev = setup_device();
202 	ioch = spdk_io_channel_get_ctx(dev->ioch);
203 	pool_size = spdk_mempool_count(ioch->io_pool);
204 
205 	/* Verify correct behaviour when children finish first */
206 	parent = alloc_io(dev, io_complete_cb, &status[0]);
207 	parent->status = 0;
208 
209 	ftl_io_inc_req(parent);
210 	status[0] = -1;
211 
212 	for (i = 0; i < MAX_CHILDREN; ++i) {
213 		status[i + 1] = -1;
214 
215 		child[i] = ftl_io_alloc_child(parent);
216 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
217 		setup_io(child[i], dev, io_complete_cb, &status[i + 1]);
218 		child[i]->status = 0;
219 
220 		ftl_io_inc_req(child[i]);
221 	}
222 
223 	CU_ASSERT_FALSE(ftl_io_done(parent));
224 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1);
225 
226 	for (i = 0; i < MAX_CHILDREN; ++i) {
227 		CU_ASSERT_FALSE(ftl_io_done(child[i]));
228 		ftl_io_dec_req(child[i]);
229 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
230 		CU_ASSERT_FALSE(ftl_io_done(parent));
231 
232 		ftl_io_complete(child[i]);
233 		CU_ASSERT_FALSE(ftl_io_done(parent));
234 		CU_ASSERT_EQUAL(status[i + 1], 0);
235 	}
236 
237 	CU_ASSERT_EQUAL(status[0], -1);
238 
239 	ftl_io_dec_req(parent);
240 	CU_ASSERT_EQUAL(parent->req_cnt, 0);
241 	CU_ASSERT_TRUE(ftl_io_done(parent));
242 
243 	ftl_io_complete(parent);
244 	CU_ASSERT_EQUAL(status[0], 0);
245 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
246 
247 
248 	/* Verify correct behaviour when parent finishes first */
249 	parent = alloc_io(dev, io_complete_cb, &status[0]);
250 	parent->status = 0;
251 
252 	ftl_io_inc_req(parent);
253 	status[0] = -1;
254 
255 	for (i = 0; i < MAX_CHILDREN; ++i) {
256 		status[i + 1] = -1;
257 
258 		child[i] = ftl_io_alloc_child(parent);
259 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
260 		setup_io(child[i], dev, io_complete_cb, &status[i + 1]);
261 		child[i]->status = 0;
262 
263 		ftl_io_inc_req(child[i]);
264 	}
265 
266 	CU_ASSERT_FALSE(ftl_io_done(parent));
267 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1);
268 
269 	ftl_io_dec_req(parent);
270 	CU_ASSERT_TRUE(ftl_io_done(parent));
271 	CU_ASSERT_EQUAL(parent->req_cnt, 0);
272 
273 	ftl_io_complete(parent);
274 	CU_ASSERT_EQUAL(status[0], -1);
275 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1);
276 
277 	for (i = 0; i < MAX_CHILDREN; ++i) {
278 		CU_ASSERT_FALSE(ftl_io_done(child[i]));
279 		ftl_io_dec_req(child[i]);
280 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
281 
282 		ftl_io_complete(child[i]);
283 		CU_ASSERT_EQUAL(status[i + 1], 0);
284 	}
285 
286 	CU_ASSERT_EQUAL(status[0], 0);
287 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
288 
289 	free_device(dev);
290 }
291 
292 static void
293 test_child_status(void)
294 {
295 	struct spdk_ftl_dev *dev;
296 	struct ftl_io_channel *ioch;
297 	struct ftl_io *parent, *child[2];
298 	int parent_status, child_status[2];
299 	size_t pool_size, i;
300 
301 	dev = setup_device();
302 	ioch = spdk_io_channel_get_ctx(dev->ioch);
303 	pool_size = spdk_mempool_count(ioch->io_pool);
304 
305 	/* Verify the first error is returned by the parent */
306 	parent = alloc_io(dev, io_complete_cb, &parent_status);
307 	parent->status = 0;
308 
309 	for (i = 0; i < 2; ++i) {
310 		child[i] = ftl_io_alloc_child(parent);
311 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
312 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
313 	}
314 
315 	child[0]->status = -3;
316 	child[1]->status = -4;
317 
318 	ftl_io_complete(child[1]);
319 	ftl_io_complete(child[0]);
320 	ftl_io_complete(parent);
321 
322 	CU_ASSERT_EQUAL(child_status[0], -3);
323 	CU_ASSERT_EQUAL(child_status[1], -4);
324 	CU_ASSERT_EQUAL(parent_status, -4);
325 
326 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
327 
328 	/* Verify parent's status is kept if children finish successfully */
329 	parent = alloc_io(dev, io_complete_cb, &parent_status);
330 	parent->status = -1;
331 
332 	for (i = 0; i < 2; ++i) {
333 		child[i] = ftl_io_alloc_child(parent);
334 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
335 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
336 	}
337 
338 	child[0]->status = 0;
339 	child[1]->status = 0;
340 
341 	ftl_io_complete(parent);
342 	ftl_io_complete(child[1]);
343 	ftl_io_complete(child[0]);
344 
345 	CU_ASSERT_EQUAL(child_status[0], 0);
346 	CU_ASSERT_EQUAL(child_status[1], 0);
347 	CU_ASSERT_EQUAL(parent_status, -1);
348 
349 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
350 
351 	/* Verify parent's status is kept if children fail too */
352 	parent = alloc_io(dev, io_complete_cb, &parent_status);
353 	parent->status = -1;
354 
355 	for (i = 0; i < 2; ++i) {
356 		child[i] = ftl_io_alloc_child(parent);
357 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
358 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
359 	}
360 
361 	child[0]->status = -3;
362 	child[1]->status = -4;
363 
364 	ftl_io_complete(parent);
365 	ftl_io_complete(child[1]);
366 	ftl_io_complete(child[0]);
367 
368 	CU_ASSERT_EQUAL(child_status[0], -3);
369 	CU_ASSERT_EQUAL(child_status[1], -4);
370 	CU_ASSERT_EQUAL(parent_status, -1);
371 
372 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
373 
374 	free_device(dev);
375 }
376 
377 static void
378 test_multi_generation(void)
379 {
380 	struct spdk_ftl_dev *dev;
381 	struct ftl_io_channel *ioch;
382 #define MAX_GRAND_CHILDREN	32
383 	struct ftl_io *parent, *child[MAX_CHILDREN], *gchild[MAX_CHILDREN * MAX_GRAND_CHILDREN];
384 	int parent_status, child_status[MAX_CHILDREN], gchild_status[MAX_CHILDREN * MAX_GRAND_CHILDREN];
385 	size_t pool_size;
386 	int i, j;
387 
388 	dev = setup_device();
389 	ioch = spdk_io_channel_get_ctx(dev->ioch);
390 	pool_size = spdk_mempool_count(ioch->io_pool);
391 
392 	/* Verify correct behaviour when children finish first */
393 	parent = alloc_io(dev, io_complete_cb, &parent_status);
394 	parent->status = 0;
395 
396 	ftl_io_inc_req(parent);
397 	parent_status = -1;
398 
399 	for (i = 0; i < MAX_CHILDREN; ++i) {
400 		child_status[i] = -1;
401 
402 		child[i] = ftl_io_alloc_child(parent);
403 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
404 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
405 		child[i]->status = 0;
406 
407 
408 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
409 			struct ftl_io *io = ftl_io_alloc_child(child[i]);
410 			SPDK_CU_ASSERT_FATAL(io != NULL);
411 
412 			gchild[i * MAX_GRAND_CHILDREN + j] = io;
413 			gchild_status[i * MAX_GRAND_CHILDREN + j] = -1;
414 			setup_io(io, dev, io_complete_cb, &gchild_status[i * MAX_GRAND_CHILDREN + j]);
415 			io->status = 0;
416 
417 			ftl_io_inc_req(io);
418 		}
419 
420 		ftl_io_inc_req(child[i]);
421 	}
422 
423 	for (i = 0; i < MAX_CHILDREN; ++i) {
424 		CU_ASSERT_FALSE(ftl_io_done(child[i]));
425 		ftl_io_dec_req(child[i]);
426 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
427 
428 		ftl_io_complete(child[i]);
429 		CU_ASSERT_FALSE(ftl_io_done(parent));
430 		CU_ASSERT_EQUAL(child_status[i], -1);
431 
432 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
433 			struct ftl_io *io = gchild[i * MAX_GRAND_CHILDREN + j];
434 
435 			CU_ASSERT_FALSE(ftl_io_done(io));
436 			ftl_io_dec_req(io);
437 			CU_ASSERT_TRUE(ftl_io_done(io));
438 			ftl_io_complete(io);
439 			CU_ASSERT_EQUAL(gchild_status[i * MAX_GRAND_CHILDREN + j], 0);
440 		}
441 
442 		CU_ASSERT_EQUAL(child_status[i], 0);
443 	}
444 
445 	ftl_io_dec_req(parent);
446 	CU_ASSERT_TRUE(ftl_io_done(parent));
447 	ftl_io_complete(parent);
448 	CU_ASSERT_EQUAL(parent_status, 0);
449 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
450 
451 	/* Verify correct behaviour when parents finish first */
452 	parent = alloc_io(dev, io_complete_cb, &parent_status);
453 	parent->status = 0;
454 	parent_status = -1;
455 
456 	for (i = 0; i < MAX_CHILDREN; ++i) {
457 		child_status[i] = -1;
458 
459 		child[i] = ftl_io_alloc_child(parent);
460 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
461 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
462 		child[i]->status = 0;
463 
464 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
465 			struct ftl_io *io = ftl_io_alloc_child(child[i]);
466 			SPDK_CU_ASSERT_FATAL(io != NULL);
467 
468 			gchild[i * MAX_GRAND_CHILDREN + j] = io;
469 			gchild_status[i * MAX_GRAND_CHILDREN + j] = -1;
470 			setup_io(io, dev, io_complete_cb, &gchild_status[i * MAX_GRAND_CHILDREN + j]);
471 			io->status = 0;
472 
473 			ftl_io_inc_req(io);
474 		}
475 
476 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
477 		ftl_io_complete(child[i]);
478 		CU_ASSERT_EQUAL(child_status[i], -1);
479 	}
480 
481 	CU_ASSERT_TRUE(ftl_io_done(parent));
482 	ftl_io_complete(parent);
483 	CU_ASSERT_EQUAL(parent_status, -1);
484 
485 	for (i = 0; i < MAX_CHILDREN; ++i) {
486 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
487 			struct ftl_io *io = gchild[i * MAX_GRAND_CHILDREN + j];
488 
489 			CU_ASSERT_FALSE(ftl_io_done(io));
490 			ftl_io_dec_req(io);
491 			CU_ASSERT_TRUE(ftl_io_done(io));
492 			ftl_io_complete(io);
493 			CU_ASSERT_EQUAL(gchild_status[i * MAX_GRAND_CHILDREN + j], 0);
494 		}
495 
496 		CU_ASSERT_EQUAL(child_status[i], 0);
497 	}
498 
499 	CU_ASSERT_EQUAL(parent_status, 0);
500 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
501 
502 	free_device(dev);
503 }
504 
505 int
506 main(int argc, char **argv)
507 {
508 	CU_pSuite suite;
509 	unsigned int num_failures;
510 
511 	if (CU_initialize_registry() != CUE_SUCCESS) {
512 		return CU_get_error();
513 	}
514 
515 	suite = CU_add_suite("ftl_io_suite", NULL, NULL);
516 	if (!suite) {
517 		CU_cleanup_registry();
518 		return CU_get_error();
519 	}
520 
521 	if (
522 		CU_add_test(suite, "test_completion",
523 			    test_completion) == NULL
524 		|| CU_add_test(suite, "test_alloc_free",
525 			       test_alloc_free) == NULL
526 		|| CU_add_test(suite, "test_child_requests",
527 			       test_child_requests) == NULL
528 		|| CU_add_test(suite, "test_child_status",
529 			       test_child_status) == NULL
530 		|| CU_add_test(suite, "test_multi_generation",
531 			       test_multi_generation) == NULL
532 
533 	) {
534 		CU_cleanup_registry();
535 		return CU_get_error();
536 	}
537 
538 	CU_basic_set_mode(CU_BRM_VERBOSE);
539 	CU_basic_run_tests();
540 	num_failures = CU_get_number_of_failures();
541 	CU_cleanup_registry();
542 
543 	return num_failures;
544 }
545