xref: /spdk/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c (revision 3219bc9a80bb834322bdeb419603aa28ad3927d6)
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(spdk_bdev_io_get_append_location, uint64_t, (struct spdk_bdev_io *bdev_io), 0);
43 DEFINE_STUB_V(ftl_band_acquire_lba_map, (struct ftl_band *band));
44 DEFINE_STUB_V(ftl_band_release_lba_map, (struct ftl_band *band));
45 
46 static struct spdk_ftl_dev *
47 setup_device(void)
48 {
49 	struct spdk_ftl_dev *dev;
50 	struct ftl_io_channel *ioch;
51 
52 	dev = calloc(1, sizeof(*dev));
53 	SPDK_CU_ASSERT_FATAL(dev != NULL);
54 	dev->core_thread.ioch = calloc(1, sizeof(*ioch) + sizeof(struct spdk_io_channel));
55 	SPDK_CU_ASSERT_FATAL(dev->core_thread.ioch != NULL);
56 
57 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
58 
59 	ioch->elem_size = sizeof(struct ftl_md_io);
60 	ioch->io_pool = spdk_mempool_create("io-pool", 4096, ioch->elem_size, 0, 0);
61 
62 	SPDK_CU_ASSERT_FATAL(ioch->io_pool != NULL);
63 
64 	return dev;
65 }
66 
67 static void
68 free_device(struct spdk_ftl_dev *dev)
69 {
70 	struct ftl_io_channel *ioch;
71 
72 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
73 	spdk_mempool_free(ioch->io_pool);
74 
75 	free(dev->core_thread.ioch);
76 	free(dev);
77 }
78 
79 static void
80 setup_io(struct ftl_io *io, struct spdk_ftl_dev *dev, ftl_io_fn cb, void *ctx)
81 {
82 	io->dev = dev;
83 	io->cb_fn = cb;
84 	io->cb_ctx = ctx;
85 }
86 
87 static struct ftl_io *
88 alloc_io(struct spdk_ftl_dev *dev, ftl_io_fn cb, void *ctx)
89 {
90 	struct ftl_io *io;
91 
92 	io = ftl_io_alloc(dev->core_thread.ioch);
93 	SPDK_CU_ASSERT_FATAL(io != NULL);
94 	setup_io(io, dev, cb, ctx);
95 
96 	return io;
97 }
98 
99 static void
100 io_complete_cb(struct ftl_io *io, void *ctx, int status)
101 {
102 	*(int *)ctx = status;
103 }
104 
105 static void
106 test_completion(void)
107 {
108 	struct spdk_ftl_dev *dev;
109 	struct ftl_io_channel *ioch;
110 	struct ftl_io *io;
111 	int req, status = 0;
112 	size_t pool_size;
113 
114 	dev = setup_device();
115 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
116 	pool_size = spdk_mempool_count(ioch->io_pool);
117 
118 	io = alloc_io(dev, io_complete_cb, &status);
119 	io->status = -EIO;
120 
121 #define NUM_REQUESTS 16
122 	for (req = 0; req < NUM_REQUESTS; ++req) {
123 		ftl_io_inc_req(io);
124 		CU_ASSERT_FALSE(ftl_io_done(io));
125 	}
126 
127 	CU_ASSERT_EQUAL(io->req_cnt, NUM_REQUESTS);
128 
129 	for (req = 0; req < (NUM_REQUESTS - 1); ++req) {
130 		ftl_io_dec_req(io);
131 		CU_ASSERT_FALSE(ftl_io_done(io));
132 	}
133 
134 	CU_ASSERT_EQUAL(io->req_cnt, 1);
135 
136 	ftl_io_dec_req(io);
137 	CU_ASSERT_TRUE(ftl_io_done(io));
138 
139 	ftl_io_complete(io);
140 	CU_ASSERT_EQUAL(status, -EIO);
141 
142 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
143 
144 	free_device(dev);
145 }
146 
147 static void
148 test_alloc_free(void)
149 {
150 	struct spdk_ftl_dev *dev;
151 	struct ftl_io_channel *ioch;
152 	struct ftl_io *parent, *child;
153 	int parent_status = -1;
154 	size_t pool_size;
155 
156 	dev = setup_device();
157 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
158 	pool_size = spdk_mempool_count(ioch->io_pool);
159 
160 	parent = alloc_io(dev, io_complete_cb, &parent_status);
161 	SPDK_CU_ASSERT_FATAL(parent != NULL);
162 	child = ftl_io_alloc_child(parent);
163 	SPDK_CU_ASSERT_FATAL(child != NULL);
164 
165 	ftl_io_free(child);
166 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - 1);
167 
168 	child = ftl_io_alloc_child(parent);
169 	SPDK_CU_ASSERT_FATAL(child != NULL);
170 	ftl_io_complete(child);
171 	CU_ASSERT_EQUAL(parent_status, -1);
172 	ftl_io_complete(parent);
173 	CU_ASSERT_EQUAL(parent_status, 0);
174 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
175 
176 	parent_status = -1;
177 	parent = alloc_io(dev, io_complete_cb, &parent_status);
178 	SPDK_CU_ASSERT_FATAL(parent != NULL);
179 	child = ftl_io_alloc_child(parent);
180 	SPDK_CU_ASSERT_FATAL(child != NULL);
181 
182 	ftl_io_free(child);
183 	CU_ASSERT_EQUAL(parent_status, -1);
184 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - 1);
185 	ftl_io_complete(parent);
186 	CU_ASSERT_EQUAL(parent_status, 0);
187 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
188 
189 	free_device(dev);
190 }
191 
192 static void
193 test_child_requests(void)
194 {
195 	struct spdk_ftl_dev *dev;
196 	struct ftl_io_channel *ioch;
197 #define MAX_CHILDREN 16
198 	struct ftl_io *parent, *child[MAX_CHILDREN];
199 	int status[MAX_CHILDREN + 1], i;
200 	size_t pool_size;
201 
202 	dev = setup_device();
203 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
204 	pool_size = spdk_mempool_count(ioch->io_pool);
205 
206 	/* Verify correct behaviour when children finish first */
207 	parent = alloc_io(dev, io_complete_cb, &status[0]);
208 	parent->status = 0;
209 
210 	ftl_io_inc_req(parent);
211 	status[0] = -1;
212 
213 	for (i = 0; i < MAX_CHILDREN; ++i) {
214 		status[i + 1] = -1;
215 
216 		child[i] = ftl_io_alloc_child(parent);
217 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
218 		setup_io(child[i], dev, io_complete_cb, &status[i + 1]);
219 		child[i]->status = 0;
220 
221 		ftl_io_inc_req(child[i]);
222 	}
223 
224 	CU_ASSERT_FALSE(ftl_io_done(parent));
225 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1);
226 
227 	for (i = 0; i < MAX_CHILDREN; ++i) {
228 		CU_ASSERT_FALSE(ftl_io_done(child[i]));
229 		ftl_io_dec_req(child[i]);
230 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
231 		CU_ASSERT_FALSE(ftl_io_done(parent));
232 
233 		ftl_io_complete(child[i]);
234 		CU_ASSERT_FALSE(ftl_io_done(parent));
235 		CU_ASSERT_EQUAL(status[i + 1], 0);
236 	}
237 
238 	CU_ASSERT_EQUAL(status[0], -1);
239 
240 	ftl_io_dec_req(parent);
241 	CU_ASSERT_EQUAL(parent->req_cnt, 0);
242 	CU_ASSERT_TRUE(ftl_io_done(parent));
243 
244 	ftl_io_complete(parent);
245 	CU_ASSERT_EQUAL(status[0], 0);
246 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
247 
248 
249 	/* Verify correct behaviour when parent finishes first */
250 	parent = alloc_io(dev, io_complete_cb, &status[0]);
251 	parent->status = 0;
252 
253 	ftl_io_inc_req(parent);
254 	status[0] = -1;
255 
256 	for (i = 0; i < MAX_CHILDREN; ++i) {
257 		status[i + 1] = -1;
258 
259 		child[i] = ftl_io_alloc_child(parent);
260 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
261 		setup_io(child[i], dev, io_complete_cb, &status[i + 1]);
262 		child[i]->status = 0;
263 
264 		ftl_io_inc_req(child[i]);
265 	}
266 
267 	CU_ASSERT_FALSE(ftl_io_done(parent));
268 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1);
269 
270 	ftl_io_dec_req(parent);
271 	CU_ASSERT_TRUE(ftl_io_done(parent));
272 	CU_ASSERT_EQUAL(parent->req_cnt, 0);
273 
274 	ftl_io_complete(parent);
275 	CU_ASSERT_EQUAL(status[0], -1);
276 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size - MAX_CHILDREN - 1);
277 
278 	for (i = 0; i < MAX_CHILDREN; ++i) {
279 		CU_ASSERT_FALSE(ftl_io_done(child[i]));
280 		ftl_io_dec_req(child[i]);
281 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
282 
283 		ftl_io_complete(child[i]);
284 		CU_ASSERT_EQUAL(status[i + 1], 0);
285 	}
286 
287 	CU_ASSERT_EQUAL(status[0], 0);
288 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
289 
290 	free_device(dev);
291 }
292 
293 static void
294 test_child_status(void)
295 {
296 	struct spdk_ftl_dev *dev;
297 	struct ftl_io_channel *ioch;
298 	struct ftl_io *parent, *child[2];
299 	int parent_status, child_status[2];
300 	size_t pool_size, i;
301 
302 	dev = setup_device();
303 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
304 	pool_size = spdk_mempool_count(ioch->io_pool);
305 
306 	/* Verify the first error is returned by the parent */
307 	parent = alloc_io(dev, io_complete_cb, &parent_status);
308 	parent->status = 0;
309 
310 	for (i = 0; i < 2; ++i) {
311 		child[i] = ftl_io_alloc_child(parent);
312 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
313 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
314 	}
315 
316 	child[0]->status = -3;
317 	child[1]->status = -4;
318 
319 	ftl_io_complete(child[1]);
320 	ftl_io_complete(child[0]);
321 	ftl_io_complete(parent);
322 
323 	CU_ASSERT_EQUAL(child_status[0], -3);
324 	CU_ASSERT_EQUAL(child_status[1], -4);
325 	CU_ASSERT_EQUAL(parent_status, -4);
326 
327 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
328 
329 	/* Verify parent's status is kept if children finish successfully */
330 	parent = alloc_io(dev, io_complete_cb, &parent_status);
331 	parent->status = -1;
332 
333 	for (i = 0; i < 2; ++i) {
334 		child[i] = ftl_io_alloc_child(parent);
335 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
336 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
337 	}
338 
339 	child[0]->status = 0;
340 	child[1]->status = 0;
341 
342 	ftl_io_complete(parent);
343 	ftl_io_complete(child[1]);
344 	ftl_io_complete(child[0]);
345 
346 	CU_ASSERT_EQUAL(child_status[0], 0);
347 	CU_ASSERT_EQUAL(child_status[1], 0);
348 	CU_ASSERT_EQUAL(parent_status, -1);
349 
350 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
351 
352 	/* Verify parent's status is kept if children fail too */
353 	parent = alloc_io(dev, io_complete_cb, &parent_status);
354 	parent->status = -1;
355 
356 	for (i = 0; i < 2; ++i) {
357 		child[i] = ftl_io_alloc_child(parent);
358 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
359 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
360 	}
361 
362 	child[0]->status = -3;
363 	child[1]->status = -4;
364 
365 	ftl_io_complete(parent);
366 	ftl_io_complete(child[1]);
367 	ftl_io_complete(child[0]);
368 
369 	CU_ASSERT_EQUAL(child_status[0], -3);
370 	CU_ASSERT_EQUAL(child_status[1], -4);
371 	CU_ASSERT_EQUAL(parent_status, -1);
372 
373 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
374 
375 	free_device(dev);
376 }
377 
378 static void
379 test_multi_generation(void)
380 {
381 	struct spdk_ftl_dev *dev;
382 	struct ftl_io_channel *ioch;
383 #define MAX_GRAND_CHILDREN	32
384 	struct ftl_io *parent, *child[MAX_CHILDREN], *gchild[MAX_CHILDREN * MAX_GRAND_CHILDREN];
385 	int parent_status, child_status[MAX_CHILDREN], gchild_status[MAX_CHILDREN * MAX_GRAND_CHILDREN];
386 	size_t pool_size;
387 	int i, j;
388 
389 	dev = setup_device();
390 	ioch = spdk_io_channel_get_ctx(dev->core_thread.ioch);
391 	pool_size = spdk_mempool_count(ioch->io_pool);
392 
393 	/* Verify correct behaviour when children finish first */
394 	parent = alloc_io(dev, io_complete_cb, &parent_status);
395 	parent->status = 0;
396 
397 	ftl_io_inc_req(parent);
398 	parent_status = -1;
399 
400 	for (i = 0; i < MAX_CHILDREN; ++i) {
401 		child_status[i] = -1;
402 
403 		child[i] = ftl_io_alloc_child(parent);
404 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
405 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
406 		child[i]->status = 0;
407 
408 
409 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
410 			struct ftl_io *io = ftl_io_alloc_child(child[i]);
411 			SPDK_CU_ASSERT_FATAL(io != NULL);
412 
413 			gchild[i * MAX_GRAND_CHILDREN + j] = io;
414 			gchild_status[i * MAX_GRAND_CHILDREN + j] = -1;
415 			setup_io(io, dev, io_complete_cb, &gchild_status[i * MAX_GRAND_CHILDREN + j]);
416 			io->status = 0;
417 
418 			ftl_io_inc_req(io);
419 		}
420 
421 		ftl_io_inc_req(child[i]);
422 	}
423 
424 	for (i = 0; i < MAX_CHILDREN; ++i) {
425 		CU_ASSERT_FALSE(ftl_io_done(child[i]));
426 		ftl_io_dec_req(child[i]);
427 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
428 
429 		ftl_io_complete(child[i]);
430 		CU_ASSERT_FALSE(ftl_io_done(parent));
431 		CU_ASSERT_EQUAL(child_status[i], -1);
432 
433 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
434 			struct ftl_io *io = gchild[i * MAX_GRAND_CHILDREN + j];
435 
436 			CU_ASSERT_FALSE(ftl_io_done(io));
437 			ftl_io_dec_req(io);
438 			CU_ASSERT_TRUE(ftl_io_done(io));
439 			ftl_io_complete(io);
440 			CU_ASSERT_EQUAL(gchild_status[i * MAX_GRAND_CHILDREN + j], 0);
441 		}
442 
443 		CU_ASSERT_EQUAL(child_status[i], 0);
444 	}
445 
446 	ftl_io_dec_req(parent);
447 	CU_ASSERT_TRUE(ftl_io_done(parent));
448 	ftl_io_complete(parent);
449 	CU_ASSERT_EQUAL(parent_status, 0);
450 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
451 
452 	/* Verify correct behaviour when parents finish first */
453 	parent = alloc_io(dev, io_complete_cb, &parent_status);
454 	parent->status = 0;
455 	parent_status = -1;
456 
457 	for (i = 0; i < MAX_CHILDREN; ++i) {
458 		child_status[i] = -1;
459 
460 		child[i] = ftl_io_alloc_child(parent);
461 		SPDK_CU_ASSERT_FATAL(child[i] != NULL);
462 		setup_io(child[i], dev, io_complete_cb, &child_status[i]);
463 		child[i]->status = 0;
464 
465 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
466 			struct ftl_io *io = ftl_io_alloc_child(child[i]);
467 			SPDK_CU_ASSERT_FATAL(io != NULL);
468 
469 			gchild[i * MAX_GRAND_CHILDREN + j] = io;
470 			gchild_status[i * MAX_GRAND_CHILDREN + j] = -1;
471 			setup_io(io, dev, io_complete_cb, &gchild_status[i * MAX_GRAND_CHILDREN + j]);
472 			io->status = 0;
473 
474 			ftl_io_inc_req(io);
475 		}
476 
477 		CU_ASSERT_TRUE(ftl_io_done(child[i]));
478 		ftl_io_complete(child[i]);
479 		CU_ASSERT_EQUAL(child_status[i], -1);
480 	}
481 
482 	CU_ASSERT_TRUE(ftl_io_done(parent));
483 	ftl_io_complete(parent);
484 	CU_ASSERT_EQUAL(parent_status, -1);
485 
486 	for (i = 0; i < MAX_CHILDREN; ++i) {
487 		for (j = 0; j < MAX_GRAND_CHILDREN; ++j) {
488 			struct ftl_io *io = gchild[i * MAX_GRAND_CHILDREN + j];
489 
490 			CU_ASSERT_FALSE(ftl_io_done(io));
491 			ftl_io_dec_req(io);
492 			CU_ASSERT_TRUE(ftl_io_done(io));
493 			ftl_io_complete(io);
494 			CU_ASSERT_EQUAL(gchild_status[i * MAX_GRAND_CHILDREN + j], 0);
495 		}
496 
497 		CU_ASSERT_EQUAL(child_status[i], 0);
498 	}
499 
500 	CU_ASSERT_EQUAL(parent_status, 0);
501 	CU_ASSERT_EQUAL(spdk_mempool_count(ioch->io_pool), pool_size);
502 
503 	free_device(dev);
504 }
505 
506 int
507 main(int argc, char **argv)
508 {
509 	CU_pSuite suite;
510 	unsigned int num_failures;
511 
512 	if (CU_initialize_registry() != CUE_SUCCESS) {
513 		return CU_get_error();
514 	}
515 
516 	suite = CU_add_suite("ftl_io_suite", NULL, NULL);
517 	if (!suite) {
518 		CU_cleanup_registry();
519 		return CU_get_error();
520 	}
521 
522 	if (
523 		CU_add_test(suite, "test_completion",
524 			    test_completion) == NULL
525 		|| CU_add_test(suite, "test_alloc_free",
526 			       test_alloc_free) == NULL
527 		|| CU_add_test(suite, "test_child_requests",
528 			       test_child_requests) == NULL
529 		|| CU_add_test(suite, "test_child_status",
530 			       test_child_status) == NULL
531 		|| CU_add_test(suite, "test_multi_generation",
532 			       test_multi_generation) == NULL
533 
534 	) {
535 		CU_cleanup_registry();
536 		return CU_get_error();
537 	}
538 
539 	CU_basic_set_mode(CU_BRM_VERBOSE);
540 	CU_basic_run_tests();
541 	num_failures = CU_get_number_of_failures();
542 	CU_cleanup_registry();
543 
544 	return num_failures;
545 }
546