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