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