xref: /spdk/test/unit/lib/reduce/reduce.c/reduce_ut.c (revision 2f557958d0762ad00e068f997e2d25a205ded4b7)
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(&params) == -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(&params) == -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(&params) == -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(&params);
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(&params) == -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(&params) == -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(&params) == -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(&params);
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(&params, &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(&params) / backing_dev.blocklen;
228 
229 	g_vol = NULL;
230 	g_ziperrno = 0;
231 	spdk_reduce_vol_init(&params, &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, &params);
239 
240 	g_vol = NULL;
241 	g_ziperrno = 0;
242 	spdk_reduce_vol_init(&params, &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, &params);
337 	pm_file_init(&pm_file, &params);
338 
339 	g_vol = NULL;
340 	g_ziperrno = -1;
341 	spdk_reduce_vol_init(&params, &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, &params, 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(&params.uuid);
372 
373 	backing_dev_init(&backing_dev, &params);
374 	pm_file_init(&pm_file, &params);
375 
376 	g_vol = NULL;
377 	g_ziperrno = -1;
378 	spdk_reduce_vol_init(&params, &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, &params, 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