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 38 #include "reduce/reduce.c" 39 #include "spdk_internal/mock.h" 40 #include "common/lib/test_env.c" 41 42 static struct spdk_reduce_vol *g_vol; 43 static int g_ziperrno; 44 static char *g_volatile_pm_buf; 45 static size_t g_volatile_pm_buf_len; 46 static char *g_persistent_pm_buf; 47 static size_t g_persistent_pm_buf_len; 48 static bool g_backing_dev_closed; 49 static char *g_backing_dev_buf; 50 static const char *g_path; 51 52 #define TEST_MD_PATH "/tmp" 53 54 static void 55 sync_pm_buf(const void *addr, size_t length) 56 { 57 uint64_t offset = (char *)addr - g_volatile_pm_buf; 58 59 memcpy(&g_persistent_pm_buf[offset], addr, length); 60 } 61 62 int 63 pmem_msync(const void *addr, size_t length) 64 { 65 sync_pm_buf(addr, length); 66 return 0; 67 } 68 69 void 70 pmem_persist(const void *addr, size_t len) 71 { 72 sync_pm_buf(addr, len); 73 } 74 75 static void 76 get_pm_file_size(void) 77 { 78 struct spdk_reduce_vol_params params; 79 int64_t pm_size, expected_pm_size; 80 81 params.vol_size = 0; 82 params.chunk_size = 0; 83 params.backing_io_unit_size = 0; 84 CU_ASSERT(spdk_reduce_get_pm_file_size(¶ms) == -EINVAL); 85 86 /* 87 * Select a valid backing_io_unit_size. This should still fail since 88 * vol_size and chunk_size are still 0. 89 */ 90 params.backing_io_unit_size = 4096; 91 CU_ASSERT(spdk_reduce_get_pm_file_size(¶ms) == -EINVAL); 92 93 /* 94 * Select a valid chunk_size. This should still fail since val_size 95 * is still 0. 96 */ 97 params.chunk_size = 4096 * 4; 98 CU_ASSERT(spdk_reduce_get_pm_file_size(¶ms) == -EINVAL); 99 100 /* Select a valid vol_size. This should return a proper pm_size. */ 101 params.vol_size = 4096 * 4 * 100; 102 pm_size = spdk_reduce_get_pm_file_size(¶ms); 103 expected_pm_size = sizeof(struct spdk_reduce_vol_superblock); 104 /* 100 chunks in logical map * 8 bytes per chunk */ 105 expected_pm_size += 100 * sizeof(uint64_t); 106 /* 100 chunks * 4 backing io units per chunk * 8 bytes per backing io unit */ 107 expected_pm_size += 100 * 4 * sizeof(uint64_t); 108 /* reduce allocates some extra chunks too for in-flight writes when logical map 109 * is full. REDUCE_EXTRA_CHUNKS is a private #ifdef in reduce.c. 110 */ 111 expected_pm_size += REDUCE_NUM_EXTRA_CHUNKS * 4 * sizeof(uint64_t); 112 /* reduce will add some padding so numbers may not match exactly. Make sure 113 * they are close though. 114 */ 115 CU_ASSERT((pm_size - expected_pm_size) < REDUCE_PM_SIZE_ALIGNMENT); 116 } 117 118 static void 119 get_backing_device_size(void) 120 { 121 struct spdk_reduce_vol_params params; 122 int64_t backing_size, expected_backing_size; 123 124 params.vol_size = 0; 125 params.chunk_size = 0; 126 params.backing_io_unit_size = 0; 127 params.logical_block_size = 512; 128 CU_ASSERT(spdk_reduce_get_backing_device_size(¶ms) == -EINVAL); 129 130 /* 131 * Select a valid backing_io_unit_size. This should still fail since 132 * vol_size and chunk_size are still 0. 133 */ 134 params.backing_io_unit_size = 4096; 135 CU_ASSERT(spdk_reduce_get_backing_device_size(¶ms) == -EINVAL); 136 137 /* 138 * Select a valid chunk_size. This should still fail since val_size 139 * is still 0. 140 */ 141 params.chunk_size = 4096 * 4; 142 CU_ASSERT(spdk_reduce_get_backing_device_size(¶ms) == -EINVAL); 143 144 /* Select a valid vol_size. This should return a proper backing device size. */ 145 params.vol_size = 4096 * 4 * 100; 146 backing_size = spdk_reduce_get_backing_device_size(¶ms); 147 expected_backing_size = params.vol_size; 148 /* reduce allocates some extra chunks too for in-flight writes when logical map 149 * is full. REDUCE_EXTRA_CHUNKS is a private #ifdef in reduce.c. Backing device 150 * must have space allocated for these extra chunks. 151 */ 152 expected_backing_size += REDUCE_NUM_EXTRA_CHUNKS * params.chunk_size; 153 /* Account for superblock as well. */ 154 expected_backing_size += sizeof(struct spdk_reduce_vol_superblock); 155 CU_ASSERT(backing_size == expected_backing_size); 156 } 157 158 void * 159 pmem_map_file(const char *path, size_t len, int flags, mode_t mode, 160 size_t *mapped_lenp, int *is_pmemp) 161 { 162 CU_ASSERT(g_volatile_pm_buf == NULL); 163 g_path = path; 164 *is_pmemp = 1; 165 166 if (g_persistent_pm_buf == NULL) { 167 g_persistent_pm_buf = calloc(1, len); 168 g_persistent_pm_buf_len = len; 169 SPDK_CU_ASSERT_FATAL(g_persistent_pm_buf != NULL); 170 } 171 172 *mapped_lenp = g_persistent_pm_buf_len; 173 g_volatile_pm_buf = calloc(1, g_persistent_pm_buf_len); 174 SPDK_CU_ASSERT_FATAL(g_volatile_pm_buf != NULL); 175 g_volatile_pm_buf_len = g_persistent_pm_buf_len; 176 177 return g_volatile_pm_buf; 178 } 179 180 int 181 pmem_unmap(void *addr, size_t len) 182 { 183 CU_ASSERT(addr == g_volatile_pm_buf); 184 CU_ASSERT(len == g_volatile_pm_buf_len); 185 free(g_volatile_pm_buf); 186 g_volatile_pm_buf = NULL; 187 g_volatile_pm_buf_len = 0; 188 189 return 0; 190 } 191 192 static void 193 persistent_pm_buf_destroy(void) 194 { 195 CU_ASSERT(g_persistent_pm_buf != NULL); 196 free(g_persistent_pm_buf); 197 g_persistent_pm_buf = NULL; 198 g_persistent_pm_buf_len = 0; 199 } 200 201 static void 202 init_cb(void *cb_arg, struct spdk_reduce_vol *vol, int ziperrno) 203 { 204 g_vol = vol; 205 g_ziperrno = ziperrno; 206 } 207 208 static void 209 load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int ziperrno) 210 { 211 g_vol = vol; 212 g_ziperrno = ziperrno; 213 } 214 215 static void 216 unload_cb(void *cb_arg, int ziperrno) 217 { 218 g_ziperrno = ziperrno; 219 } 220 221 static void 222 init_failure(void) 223 { 224 struct spdk_reduce_vol_params params = {}; 225 struct spdk_reduce_backing_dev backing_dev = {}; 226 227 backing_dev.blocklen = 512; 228 229 params.vol_size = 1024 * 1024; /* 1MB */ 230 params.chunk_size = 16 * 1024; 231 params.backing_io_unit_size = backing_dev.blocklen; 232 params.logical_block_size = 512; 233 234 /* backing_dev and pm_file have an invalid size. This should fail. */ 235 g_vol = NULL; 236 g_ziperrno = 0; 237 spdk_reduce_vol_init(¶ms, &backing_dev, TEST_MD_PATH, init_cb, NULL); 238 CU_ASSERT(g_ziperrno == -EINVAL); 239 SPDK_CU_ASSERT_FATAL(g_vol == NULL); 240 241 /* backing_dev now has valid size, but backing_dev still has null 242 * function pointers. This should fail. 243 */ 244 backing_dev.blockcnt = spdk_reduce_get_backing_device_size(¶ms) / backing_dev.blocklen; 245 246 g_vol = NULL; 247 g_ziperrno = 0; 248 spdk_reduce_vol_init(¶ms, &backing_dev, TEST_MD_PATH, init_cb, NULL); 249 CU_ASSERT(g_ziperrno == -EINVAL); 250 SPDK_CU_ASSERT_FATAL(g_vol == NULL); 251 } 252 253 static void 254 backing_dev_readv(struct spdk_reduce_backing_dev *backing_dev, struct iovec *iov, int iovcnt, 255 uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args) 256 { 257 char *offset; 258 int i; 259 260 offset = g_backing_dev_buf + lba * backing_dev->blocklen; 261 for (i = 0; i < iovcnt; i++) { 262 memcpy(iov[i].iov_base, offset, iov[i].iov_len); 263 offset += iov[i].iov_len; 264 } 265 args->cb_fn(args->cb_arg, 0); 266 } 267 268 static void 269 backing_dev_writev(struct spdk_reduce_backing_dev *backing_dev, struct iovec *iov, int iovcnt, 270 uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args) 271 { 272 char *offset; 273 int i; 274 275 offset = g_backing_dev_buf + lba * backing_dev->blocklen; 276 for (i = 0; i < iovcnt; i++) { 277 memcpy(offset, iov[i].iov_base, iov[i].iov_len); 278 offset += iov[i].iov_len; 279 } 280 args->cb_fn(args->cb_arg, 0); 281 } 282 283 static void 284 backing_dev_unmap(struct spdk_reduce_backing_dev *backing_dev, 285 uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args) 286 { 287 char *offset; 288 289 offset = g_backing_dev_buf + lba * backing_dev->blocklen; 290 memset(offset, 0, lba_count * backing_dev->blocklen); 291 args->cb_fn(args->cb_arg, 0); 292 } 293 294 static void 295 backing_dev_close(struct spdk_reduce_backing_dev *backing_dev) 296 { 297 g_backing_dev_closed = true; 298 } 299 300 static void 301 backing_dev_destroy(struct spdk_reduce_backing_dev *backing_dev) 302 { 303 /* We don't free this during backing_dev_close so that we can test init/unload/load 304 * scenarios. 305 */ 306 free(g_backing_dev_buf); 307 g_backing_dev_buf = NULL; 308 } 309 310 static void 311 backing_dev_init(struct spdk_reduce_backing_dev *backing_dev, struct spdk_reduce_vol_params *params) 312 { 313 int64_t size; 314 315 size = spdk_reduce_get_backing_device_size(params); 316 backing_dev->blocklen = params->backing_io_unit_size; 317 backing_dev->blockcnt = size / backing_dev->blocklen; 318 backing_dev->readv = backing_dev_readv; 319 backing_dev->writev = backing_dev_writev; 320 backing_dev->unmap = backing_dev_unmap; 321 backing_dev->close = backing_dev_close; 322 323 g_backing_dev_buf = calloc(1, size); 324 SPDK_CU_ASSERT_FATAL(g_backing_dev_buf != NULL); 325 } 326 327 static void 328 init_md(void) 329 { 330 struct spdk_reduce_vol_params params = {}; 331 struct spdk_reduce_vol_params *persistent_params; 332 struct spdk_reduce_backing_dev backing_dev = {}; 333 struct spdk_uuid uuid; 334 uint64_t *entry; 335 336 params.vol_size = 1024 * 1024; /* 1MB */ 337 params.chunk_size = 16 * 1024; 338 params.backing_io_unit_size = 512; 339 params.logical_block_size = 512; 340 341 backing_dev_init(&backing_dev, ¶ms); 342 343 g_vol = NULL; 344 g_ziperrno = -1; 345 spdk_reduce_vol_init(¶ms, &backing_dev, TEST_MD_PATH, init_cb, NULL); 346 CU_ASSERT(g_ziperrno == 0); 347 SPDK_CU_ASSERT_FATAL(g_vol != NULL); 348 /* Confirm that reduce persisted the params to metadata. */ 349 CU_ASSERT(memcmp(g_persistent_pm_buf, SPDK_REDUCE_SIGNATURE, 8) == 0); 350 persistent_params = (struct spdk_reduce_vol_params *)(g_persistent_pm_buf + 8); 351 CU_ASSERT(memcmp(persistent_params, ¶ms, sizeof(params)) == 0); 352 /* Now confirm that contents of pm_file after the superblock have been initialized 353 * to REDUCE_EMPTY_MAP_ENTRY. 354 */ 355 entry = (uint64_t *)(g_persistent_pm_buf + sizeof(struct spdk_reduce_vol_superblock)); 356 while (entry != (uint64_t *)(g_persistent_pm_buf + g_vol->pm_file.size)) { 357 CU_ASSERT(*entry == REDUCE_EMPTY_MAP_ENTRY); 358 entry++; 359 } 360 361 /* Check that the pm file path was constructed correctly. It should be in 362 * the form: 363 * TEST_MD_PATH + "/" + <uuid string> 364 */ 365 CU_ASSERT(strncmp(&g_path[0], TEST_MD_PATH, strlen(TEST_MD_PATH)) == 0); 366 CU_ASSERT(g_path[strlen(TEST_MD_PATH)] == '/'); 367 CU_ASSERT(spdk_uuid_parse(&uuid, &g_path[strlen(TEST_MD_PATH) + 1]) == 0); 368 CU_ASSERT(spdk_uuid_compare(&uuid, spdk_reduce_vol_get_uuid(g_vol)) == 0); 369 370 g_ziperrno = -1; 371 g_backing_dev_closed = false; 372 spdk_reduce_vol_unload(g_vol, unload_cb, NULL); 373 CU_ASSERT(g_ziperrno == 0); 374 CU_ASSERT(g_backing_dev_closed == true); 375 CU_ASSERT(g_volatile_pm_buf == NULL); 376 377 persistent_pm_buf_destroy(); 378 backing_dev_destroy(&backing_dev); 379 } 380 381 static void 382 init_backing_dev(void) 383 { 384 struct spdk_reduce_vol_params params = {}; 385 struct spdk_reduce_vol_params *persistent_params; 386 struct spdk_reduce_backing_dev backing_dev = {}; 387 388 params.vol_size = 1024 * 1024; /* 1MB */ 389 params.chunk_size = 16 * 1024; 390 params.backing_io_unit_size = 512; 391 params.logical_block_size = 512; 392 spdk_uuid_generate(¶ms.uuid); 393 394 backing_dev_init(&backing_dev, ¶ms); 395 396 g_vol = NULL; 397 g_path = NULL; 398 g_ziperrno = -1; 399 spdk_reduce_vol_init(¶ms, &backing_dev, TEST_MD_PATH, init_cb, NULL); 400 CU_ASSERT(g_ziperrno == 0); 401 SPDK_CU_ASSERT_FATAL(g_vol != NULL); 402 SPDK_CU_ASSERT_FATAL(g_path != NULL); 403 /* Confirm that libreduce persisted the params to the backing device. */ 404 CU_ASSERT(memcmp(g_backing_dev_buf, SPDK_REDUCE_SIGNATURE, 8) == 0); 405 persistent_params = (struct spdk_reduce_vol_params *)(g_backing_dev_buf + 8); 406 CU_ASSERT(memcmp(persistent_params, ¶ms, sizeof(params)) == 0); 407 CU_ASSERT(backing_dev.close != NULL); 408 /* Confirm that the path to the persistent memory metadata file was persisted to 409 * the backing device. 410 */ 411 CU_ASSERT(strncmp(g_path, 412 g_backing_dev_buf + REDUCE_BACKING_DEV_PATH_OFFSET, 413 REDUCE_PATH_MAX) == 0); 414 415 g_ziperrno = -1; 416 g_backing_dev_closed = false; 417 spdk_reduce_vol_unload(g_vol, unload_cb, NULL); 418 CU_ASSERT(g_ziperrno == 0); 419 CU_ASSERT(g_backing_dev_closed == true); 420 421 persistent_pm_buf_destroy(); 422 backing_dev_destroy(&backing_dev); 423 } 424 425 static void 426 load(void) 427 { 428 struct spdk_reduce_vol_params params = {}; 429 struct spdk_reduce_backing_dev backing_dev = {}; 430 char pmem_file_path[REDUCE_PATH_MAX]; 431 432 params.vol_size = 1024 * 1024; /* 1MB */ 433 params.chunk_size = 16 * 1024; 434 params.backing_io_unit_size = 512; 435 params.logical_block_size = 512; 436 spdk_uuid_generate(¶ms.uuid); 437 438 backing_dev_init(&backing_dev, ¶ms); 439 440 g_vol = NULL; 441 g_ziperrno = -1; 442 spdk_reduce_vol_init(¶ms, &backing_dev, TEST_MD_PATH, init_cb, NULL); 443 CU_ASSERT(g_ziperrno == 0); 444 SPDK_CU_ASSERT_FATAL(g_vol != NULL); 445 SPDK_CU_ASSERT_FATAL(g_path != NULL); 446 memcpy(pmem_file_path, g_path, sizeof(pmem_file_path)); 447 448 g_ziperrno = -1; 449 spdk_reduce_vol_unload(g_vol, unload_cb, NULL); 450 CU_ASSERT(g_ziperrno == 0); 451 452 g_vol = NULL; 453 g_path = NULL; 454 g_ziperrno = -1; 455 spdk_reduce_vol_load(&backing_dev, load_cb, NULL); 456 CU_ASSERT(g_ziperrno == 0); 457 SPDK_CU_ASSERT_FATAL(g_vol != NULL); 458 SPDK_CU_ASSERT_FATAL(g_path != NULL); 459 CU_ASSERT(strncmp(g_path, pmem_file_path, sizeof(pmem_file_path)) == 0); 460 CU_ASSERT(g_vol->params.vol_size == params.vol_size); 461 CU_ASSERT(g_vol->params.chunk_size == params.chunk_size); 462 CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size); 463 464 g_ziperrno = -1; 465 spdk_reduce_vol_unload(g_vol, unload_cb, NULL); 466 CU_ASSERT(g_ziperrno == 0); 467 468 persistent_pm_buf_destroy(); 469 backing_dev_destroy(&backing_dev); 470 } 471 472 int 473 main(int argc, char **argv) 474 { 475 CU_pSuite suite = NULL; 476 unsigned int num_failures; 477 478 if (CU_initialize_registry() != CUE_SUCCESS) { 479 return CU_get_error(); 480 } 481 482 suite = CU_add_suite("reduce", NULL, NULL); 483 if (suite == NULL) { 484 CU_cleanup_registry(); 485 return CU_get_error(); 486 } 487 488 if ( 489 CU_add_test(suite, "get_pm_file_size", get_pm_file_size) == NULL || 490 CU_add_test(suite, "get_backing_device_size", get_backing_device_size) == NULL || 491 CU_add_test(suite, "init_failure", init_failure) == NULL || 492 CU_add_test(suite, "init_md", init_md) == NULL || 493 CU_add_test(suite, "init_backing_dev", init_backing_dev) == NULL || 494 CU_add_test(suite, "load", load) == NULL 495 ) { 496 CU_cleanup_registry(); 497 return CU_get_error(); 498 } 499 500 CU_basic_set_mode(CU_BRM_VERBOSE); 501 CU_basic_run_tests(); 502 num_failures = CU_get_number_of_failures(); 503 CU_cleanup_registry(); 504 return num_failures; 505 } 506