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