xref: /spdk/test/unit/lib/reduce/reduce.c/reduce_ut.c (revision 72f8c6a1f3f4aa1b3c373ced13e8d0ec06825ddc)
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(&params) == -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(&params) == -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(&params) == -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(&params);
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(&params) == -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(&params) == -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(&params) == -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(&params);
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(&params, &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(&params) / backing_dev.blocklen;
238 
239 	g_vol = NULL;
240 	g_ziperrno = 0;
241 	spdk_reduce_vol_init(&params, &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, &params);
334 
335 	g_vol = NULL;
336 	g_ziperrno = -1;
337 	spdk_reduce_vol_init(&params, &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, &params, 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(&params.uuid);
384 
385 	backing_dev_init(&backing_dev, &params);
386 
387 	g_vol = NULL;
388 	g_path = NULL;
389 	g_ziperrno = -1;
390 	spdk_reduce_vol_init(&params, &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, &params, 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(&params.uuid);
427 
428 	backing_dev_init(&backing_dev, &params);
429 
430 	g_vol = NULL;
431 	g_ziperrno = -1;
432 	spdk_reduce_vol_init(&params, &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