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_md, (struct ftl_band *band)); 43 DEFINE_STUB_V(ftl_band_release_md, (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, spdk_ftl_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, spdk_ftl_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(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