xref: /spdk/test/unit/lib/reduce/reduce.c/reduce_ut.c (revision 5b1b3ddf33574616fcf1c507af3c61f895a0c6fb)
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_reduce_errno;
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 	uint64_t pm_size, expected_pm_size;
80 
81 	params.backing_io_unit_size = 4096;
82 	params.chunk_size = 4096 * 4;
83 	params.vol_size = 4096 * 4 * 100;
84 
85 	pm_size = _get_pm_file_size(&params);
86 	expected_pm_size = sizeof(struct spdk_reduce_vol_superblock);
87 	/* 100 chunks in logical map * 8 bytes per chunk */
88 	expected_pm_size += 100 * sizeof(uint64_t);
89 	/* 100 chunks * 4 backing io units per chunk * 8 bytes per backing io unit */
90 	expected_pm_size += 100 * 4 * sizeof(uint64_t);
91 	/* reduce allocates some extra chunks too for in-flight writes when logical map
92 	 * is full.  REDUCE_EXTRA_CHUNKS is a private #ifdef in reduce.c.
93 	 */
94 	expected_pm_size += REDUCE_NUM_EXTRA_CHUNKS * 4 * sizeof(uint64_t);
95 	/* reduce will add some padding so numbers may not match exactly.  Make sure
96 	 * they are close though.
97 	 */
98 	CU_ASSERT((pm_size - expected_pm_size) < REDUCE_PM_SIZE_ALIGNMENT);
99 }
100 
101 static void
102 get_vol_size(void)
103 {
104 	uint64_t chunk_size, backing_dev_size;
105 
106 	chunk_size = 16 * 1024;
107 	backing_dev_size = 16 * 1024 * 1000;
108 	CU_ASSERT(_get_vol_size(chunk_size, backing_dev_size) < backing_dev_size);
109 }
110 
111 void *
112 pmem_map_file(const char *path, size_t len, int flags, mode_t mode,
113 	      size_t *mapped_lenp, int *is_pmemp)
114 {
115 	CU_ASSERT(g_volatile_pm_buf == NULL);
116 	g_path = path;
117 	*is_pmemp = 1;
118 
119 	if (g_persistent_pm_buf == NULL) {
120 		g_persistent_pm_buf = calloc(1, len);
121 		g_persistent_pm_buf_len = len;
122 		SPDK_CU_ASSERT_FATAL(g_persistent_pm_buf != NULL);
123 	}
124 
125 	*mapped_lenp = g_persistent_pm_buf_len;
126 	g_volatile_pm_buf = calloc(1, g_persistent_pm_buf_len);
127 	SPDK_CU_ASSERT_FATAL(g_volatile_pm_buf != NULL);
128 	g_volatile_pm_buf_len = g_persistent_pm_buf_len;
129 
130 	return g_volatile_pm_buf;
131 }
132 
133 int
134 pmem_unmap(void *addr, size_t len)
135 {
136 	CU_ASSERT(addr == g_volatile_pm_buf);
137 	CU_ASSERT(len == g_volatile_pm_buf_len);
138 	free(g_volatile_pm_buf);
139 	g_volatile_pm_buf = NULL;
140 	g_volatile_pm_buf_len = 0;
141 
142 	return 0;
143 }
144 
145 static void
146 persistent_pm_buf_destroy(void)
147 {
148 	CU_ASSERT(g_persistent_pm_buf != NULL);
149 	free(g_persistent_pm_buf);
150 	g_persistent_pm_buf = NULL;
151 	g_persistent_pm_buf_len = 0;
152 }
153 
154 static void
155 init_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
156 {
157 	g_vol = vol;
158 	g_reduce_errno = reduce_errno;
159 }
160 
161 static void
162 load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
163 {
164 	g_vol = vol;
165 	g_reduce_errno = reduce_errno;
166 }
167 
168 static void
169 unload_cb(void *cb_arg, int reduce_errno)
170 {
171 	g_reduce_errno = reduce_errno;
172 }
173 
174 static void
175 init_failure(void)
176 {
177 	struct spdk_reduce_vol_params params = {};
178 	struct spdk_reduce_backing_dev backing_dev = {};
179 
180 	backing_dev.blocklen = 512;
181 	/* This blockcnt is too small for a reduce vol - there needs to be
182 	 *  enough space for at least REDUCE_NUM_EXTRA_CHUNKS + 1 chunks.
183 	 */
184 	backing_dev.blockcnt = 20;
185 
186 	params.vol_size = 0;
187 	params.chunk_size = 16 * 1024;
188 	params.backing_io_unit_size = backing_dev.blocklen;
189 	params.logical_block_size = 512;
190 
191 	/* backing_dev has an invalid size.  This should fail. */
192 	g_vol = NULL;
193 	g_reduce_errno = 0;
194 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
195 	CU_ASSERT(g_reduce_errno == -EINVAL);
196 	SPDK_CU_ASSERT_FATAL(g_vol == NULL);
197 
198 	/* backing_dev now has valid size, but backing_dev still has null
199 	 *  function pointers.  This should fail.
200 	 */
201 	backing_dev.blockcnt = 20000;
202 
203 	g_vol = NULL;
204 	g_reduce_errno = 0;
205 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
206 	CU_ASSERT(g_reduce_errno == -EINVAL);
207 	SPDK_CU_ASSERT_FATAL(g_vol == NULL);
208 }
209 
210 static void
211 backing_dev_readv(struct spdk_reduce_backing_dev *backing_dev, struct iovec *iov, int iovcnt,
212 		  uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args)
213 {
214 	char *offset;
215 	int i;
216 
217 	offset = g_backing_dev_buf + lba * backing_dev->blocklen;
218 	for (i = 0; i < iovcnt; i++) {
219 		memcpy(iov[i].iov_base, offset, iov[i].iov_len);
220 		offset += iov[i].iov_len;
221 	}
222 	args->cb_fn(args->cb_arg, 0);
223 }
224 
225 static void
226 backing_dev_writev(struct spdk_reduce_backing_dev *backing_dev, struct iovec *iov, int iovcnt,
227 		   uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args)
228 {
229 	char *offset;
230 	int i;
231 
232 	offset = g_backing_dev_buf + lba * backing_dev->blocklen;
233 	for (i = 0; i < iovcnt; i++) {
234 		memcpy(offset, iov[i].iov_base, iov[i].iov_len);
235 		offset += iov[i].iov_len;
236 	}
237 	args->cb_fn(args->cb_arg, 0);
238 }
239 
240 static void
241 backing_dev_unmap(struct spdk_reduce_backing_dev *backing_dev,
242 		  uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args)
243 {
244 	char *offset;
245 
246 	offset = g_backing_dev_buf + lba * backing_dev->blocklen;
247 	memset(offset, 0, lba_count * backing_dev->blocklen);
248 	args->cb_fn(args->cb_arg, 0);
249 }
250 
251 static void
252 backing_dev_close(struct spdk_reduce_backing_dev *backing_dev)
253 {
254 	g_backing_dev_closed = true;
255 }
256 
257 static void
258 backing_dev_destroy(struct spdk_reduce_backing_dev *backing_dev)
259 {
260 	/* We don't free this during backing_dev_close so that we can test init/unload/load
261 	 *  scenarios.
262 	 */
263 	free(g_backing_dev_buf);
264 	g_backing_dev_buf = NULL;
265 }
266 
267 static void
268 backing_dev_init(struct spdk_reduce_backing_dev *backing_dev, struct spdk_reduce_vol_params *params,
269 		 uint32_t backing_blocklen)
270 {
271 	int64_t size;
272 
273 	size = 4 * 1024 * 1024;
274 	backing_dev->blocklen = backing_blocklen;
275 	backing_dev->blockcnt = size / backing_dev->blocklen;
276 	backing_dev->readv = backing_dev_readv;
277 	backing_dev->writev = backing_dev_writev;
278 	backing_dev->unmap = backing_dev_unmap;
279 	backing_dev->close = backing_dev_close;
280 
281 	g_backing_dev_buf = calloc(1, size);
282 	SPDK_CU_ASSERT_FATAL(g_backing_dev_buf != NULL);
283 }
284 
285 static void
286 init_md(void)
287 {
288 	struct spdk_reduce_vol_params params = {};
289 	struct spdk_reduce_vol_params *persistent_params;
290 	struct spdk_reduce_backing_dev backing_dev = {};
291 	struct spdk_uuid uuid;
292 	uint64_t *entry;
293 
294 	params.chunk_size = 16 * 1024;
295 	params.backing_io_unit_size = 512;
296 	params.logical_block_size = 512;
297 
298 	backing_dev_init(&backing_dev, &params, 512);
299 
300 	g_vol = NULL;
301 	g_reduce_errno = -1;
302 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
303 	CU_ASSERT(g_reduce_errno == 0);
304 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
305 	/* Confirm that reduce persisted the params to metadata. */
306 	CU_ASSERT(memcmp(g_persistent_pm_buf, SPDK_REDUCE_SIGNATURE, 8) == 0);
307 	persistent_params = (struct spdk_reduce_vol_params *)(g_persistent_pm_buf + 8);
308 	CU_ASSERT(memcmp(persistent_params, &params, sizeof(params)) == 0);
309 	/* Now confirm that contents of pm_file after the superblock have been initialized
310 	 *  to REDUCE_EMPTY_MAP_ENTRY.
311 	 */
312 	entry = (uint64_t *)(g_persistent_pm_buf + sizeof(struct spdk_reduce_vol_superblock));
313 	while (entry != (uint64_t *)(g_persistent_pm_buf + g_vol->pm_file.size)) {
314 		CU_ASSERT(*entry == REDUCE_EMPTY_MAP_ENTRY);
315 		entry++;
316 	}
317 
318 	/* Check that the pm file path was constructed correctly.  It should be in
319 	 * the form:
320 	 * TEST_MD_PATH + "/" + <uuid string>
321 	 */
322 	CU_ASSERT(strncmp(&g_path[0], TEST_MD_PATH, strlen(TEST_MD_PATH)) == 0);
323 	CU_ASSERT(g_path[strlen(TEST_MD_PATH)] == '/');
324 	CU_ASSERT(spdk_uuid_parse(&uuid, &g_path[strlen(TEST_MD_PATH) + 1]) == 0);
325 	CU_ASSERT(spdk_uuid_compare(&uuid, spdk_reduce_vol_get_uuid(g_vol)) == 0);
326 
327 	g_reduce_errno = -1;
328 	g_backing_dev_closed = false;
329 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
330 	CU_ASSERT(g_reduce_errno == 0);
331 	CU_ASSERT(g_backing_dev_closed == true);
332 	CU_ASSERT(g_volatile_pm_buf == NULL);
333 
334 	persistent_pm_buf_destroy();
335 	backing_dev_destroy(&backing_dev);
336 }
337 
338 static void
339 _init_backing_dev(uint32_t backing_blocklen)
340 {
341 	struct spdk_reduce_vol_params params = {};
342 	struct spdk_reduce_vol_params *persistent_params;
343 	struct spdk_reduce_backing_dev backing_dev = {};
344 
345 	params.chunk_size = 16 * 1024;
346 	params.backing_io_unit_size = 512;
347 	params.logical_block_size = 512;
348 	spdk_uuid_generate(&params.uuid);
349 
350 	backing_dev_init(&backing_dev, &params, backing_blocklen);
351 
352 	g_vol = NULL;
353 	g_path = NULL;
354 	g_reduce_errno = -1;
355 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
356 	CU_ASSERT(g_reduce_errno == 0);
357 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
358 	SPDK_CU_ASSERT_FATAL(g_path != NULL);
359 	/* Confirm that libreduce persisted the params to the backing device. */
360 	CU_ASSERT(memcmp(g_backing_dev_buf, SPDK_REDUCE_SIGNATURE, 8) == 0);
361 	persistent_params = (struct spdk_reduce_vol_params *)(g_backing_dev_buf + 8);
362 	CU_ASSERT(memcmp(persistent_params, &params, sizeof(params)) == 0);
363 	CU_ASSERT(backing_dev.close != NULL);
364 	/* Confirm that the path to the persistent memory metadata file was persisted to
365 	 *  the backing device.
366 	 */
367 	CU_ASSERT(strncmp(g_path,
368 			  g_backing_dev_buf + REDUCE_BACKING_DEV_PATH_OFFSET,
369 			  REDUCE_PATH_MAX) == 0);
370 
371 	g_reduce_errno = -1;
372 	g_backing_dev_closed = false;
373 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
374 	CU_ASSERT(g_reduce_errno == 0);
375 	CU_ASSERT(g_backing_dev_closed == true);
376 
377 	persistent_pm_buf_destroy();
378 	backing_dev_destroy(&backing_dev);
379 }
380 
381 static void
382 init_backing_dev(void)
383 {
384 	_init_backing_dev(512);
385 	_init_backing_dev(4096);
386 }
387 
388 static void
389 _load(uint32_t backing_blocklen)
390 {
391 	struct spdk_reduce_vol_params params = {};
392 	struct spdk_reduce_backing_dev backing_dev = {};
393 	char pmem_file_path[REDUCE_PATH_MAX];
394 
395 	params.chunk_size = 16 * 1024;
396 	params.backing_io_unit_size = 512;
397 	params.logical_block_size = 512;
398 	spdk_uuid_generate(&params.uuid);
399 
400 	backing_dev_init(&backing_dev, &params, backing_blocklen);
401 
402 	g_vol = NULL;
403 	g_reduce_errno = -1;
404 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
405 	CU_ASSERT(g_reduce_errno == 0);
406 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
407 	SPDK_CU_ASSERT_FATAL(g_path != NULL);
408 	memcpy(pmem_file_path, g_path, sizeof(pmem_file_path));
409 
410 	g_reduce_errno = -1;
411 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
412 	CU_ASSERT(g_reduce_errno == 0);
413 
414 	g_vol = NULL;
415 	g_path = NULL;
416 	g_reduce_errno = -1;
417 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
418 	CU_ASSERT(g_reduce_errno == 0);
419 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
420 	SPDK_CU_ASSERT_FATAL(g_path != NULL);
421 	CU_ASSERT(strncmp(g_path, pmem_file_path, sizeof(pmem_file_path)) == 0);
422 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
423 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
424 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
425 
426 	g_reduce_errno = -1;
427 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
428 	CU_ASSERT(g_reduce_errno == 0);
429 
430 	persistent_pm_buf_destroy();
431 	backing_dev_destroy(&backing_dev);
432 }
433 
434 static void
435 load(void)
436 {
437 	_load(512);
438 	_load(4096);
439 }
440 
441 static uint64_t
442 _vol_get_chunk_map_index(struct spdk_reduce_vol *vol, uint64_t offset)
443 {
444 	uint64_t logical_map_index = offset / vol->logical_blocks_per_chunk;
445 
446 	return vol->pm_logical_map[logical_map_index];
447 }
448 
449 static uint64_t *
450 _vol_get_chunk_map(struct spdk_reduce_vol *vol, uint64_t chunk_map_index)
451 {
452 	return &vol->pm_chunk_maps[chunk_map_index * vol->backing_io_units_per_chunk];
453 }
454 
455 static void
456 write_cb(void *arg, int reduce_errno)
457 {
458 	g_reduce_errno = reduce_errno;
459 }
460 
461 static void
462 read_cb(void *arg, int reduce_errno)
463 {
464 	g_reduce_errno = reduce_errno;
465 }
466 
467 static void
468 _write_maps(uint32_t backing_blocklen)
469 {
470 	struct spdk_reduce_vol_params params = {};
471 	struct spdk_reduce_backing_dev backing_dev = {};
472 	struct iovec iov;
473 	char buf[16 * 1024]; /* chunk size */
474 	uint32_t i;
475 	uint64_t old_chunk0_map_index, new_chunk0_map_index;
476 	uint64_t *old_chunk0_map, *new_chunk0_map;
477 
478 	params.chunk_size = 16 * 1024;
479 	params.backing_io_unit_size = 4096;
480 	params.logical_block_size = 512;
481 	spdk_uuid_generate(&params.uuid);
482 
483 	backing_dev_init(&backing_dev, &params, backing_blocklen);
484 
485 	g_vol = NULL;
486 	g_reduce_errno = -1;
487 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
488 	CU_ASSERT(g_reduce_errno == 0);
489 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
490 
491 	for (i = 0; i < g_vol->params.vol_size / g_vol->params.chunk_size; i++) {
492 		CU_ASSERT(_vol_get_chunk_map_index(g_vol, i) == REDUCE_EMPTY_MAP_ENTRY);
493 	}
494 
495 	iov.iov_base = buf;
496 	iov.iov_len = params.logical_block_size;
497 	g_reduce_errno = -1;
498 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, 1, write_cb, NULL);
499 	CU_ASSERT(g_reduce_errno == 0);
500 
501 	old_chunk0_map_index = _vol_get_chunk_map_index(g_vol, 0);
502 	CU_ASSERT(old_chunk0_map_index != REDUCE_EMPTY_MAP_ENTRY);
503 	CU_ASSERT(spdk_bit_array_get(g_vol->allocated_chunk_maps, old_chunk0_map_index) == true);
504 
505 	old_chunk0_map = _vol_get_chunk_map(g_vol, old_chunk0_map_index);
506 	for (i = 0; i < g_vol->backing_io_units_per_chunk; i++) {
507 		CU_ASSERT(old_chunk0_map[i] != REDUCE_EMPTY_MAP_ENTRY);
508 		CU_ASSERT(spdk_bit_array_get(g_vol->allocated_backing_io_units, old_chunk0_map[i]) == true);
509 	}
510 
511 	g_reduce_errno = -1;
512 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, 1, write_cb, NULL);
513 	CU_ASSERT(g_reduce_errno == 0);
514 
515 	new_chunk0_map_index = _vol_get_chunk_map_index(g_vol, 0);
516 	CU_ASSERT(new_chunk0_map_index != REDUCE_EMPTY_MAP_ENTRY);
517 	CU_ASSERT(new_chunk0_map_index != old_chunk0_map_index);
518 	CU_ASSERT(spdk_bit_array_get(g_vol->allocated_chunk_maps, new_chunk0_map_index) == true);
519 	CU_ASSERT(spdk_bit_array_get(g_vol->allocated_chunk_maps, old_chunk0_map_index) == false);
520 
521 	for (i = 0; i < g_vol->backing_io_units_per_chunk; i++) {
522 		CU_ASSERT(spdk_bit_array_get(g_vol->allocated_backing_io_units, old_chunk0_map[i]) == false);
523 	}
524 
525 	new_chunk0_map = _vol_get_chunk_map(g_vol, new_chunk0_map_index);
526 	for (i = 0; i < g_vol->backing_io_units_per_chunk; i++) {
527 		CU_ASSERT(new_chunk0_map[i] != REDUCE_EMPTY_MAP_ENTRY);
528 		CU_ASSERT(spdk_bit_array_get(g_vol->allocated_backing_io_units, new_chunk0_map[i]) == true);
529 	}
530 
531 	g_reduce_errno = -1;
532 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
533 	CU_ASSERT(g_reduce_errno == 0);
534 
535 	g_vol = NULL;
536 	g_reduce_errno = -1;
537 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
538 	CU_ASSERT(g_reduce_errno == 0);
539 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
540 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
541 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
542 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
543 
544 	g_reduce_errno = -1;
545 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
546 	CU_ASSERT(g_reduce_errno == 0);
547 
548 	persistent_pm_buf_destroy();
549 	backing_dev_destroy(&backing_dev);
550 }
551 
552 static void
553 write_maps(void)
554 {
555 	_write_maps(512);
556 	_write_maps(4096);
557 }
558 
559 static void
560 _read_write(uint32_t backing_blocklen)
561 {
562 	struct spdk_reduce_vol_params params = {};
563 	struct spdk_reduce_backing_dev backing_dev = {};
564 	struct iovec iov;
565 	char buf[16 * 1024]; /* chunk size */
566 	char compare_buf[16 * 1024];
567 	uint32_t i;
568 
569 	params.chunk_size = 16 * 1024;
570 	params.backing_io_unit_size = 4096;
571 	params.logical_block_size = 512;
572 	spdk_uuid_generate(&params.uuid);
573 
574 	backing_dev_init(&backing_dev, &params, backing_blocklen);
575 
576 	g_vol = NULL;
577 	g_reduce_errno = -1;
578 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
579 	CU_ASSERT(g_reduce_errno == 0);
580 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
581 
582 	/* Write 0xAA to 2 512-byte logical blocks, starting at LBA 2. */
583 	memset(buf, 0xAA, 2 * params.logical_block_size);
584 	iov.iov_base = buf;
585 	iov.iov_len = 2 * params.logical_block_size;
586 	g_reduce_errno = -1;
587 	spdk_reduce_vol_writev(g_vol, &iov, 1, 2, 2, write_cb, NULL);
588 	CU_ASSERT(g_reduce_errno == 0);
589 
590 	memset(compare_buf, 0xAA, sizeof(compare_buf));
591 	for (i = 0; i < params.chunk_size / params.logical_block_size; i++) {
592 		memset(buf, 0xFF, params.logical_block_size);
593 		iov.iov_base = buf;
594 		iov.iov_len = params.logical_block_size;
595 		g_reduce_errno = -1;
596 		spdk_reduce_vol_readv(g_vol, &iov, 1, i, 1, read_cb, NULL);
597 		CU_ASSERT(g_reduce_errno == 0);
598 
599 		switch (i) {
600 		case 2:
601 		case 3:
602 			CU_ASSERT(memcmp(buf, compare_buf, params.logical_block_size) == 0);
603 			break;
604 		default:
605 			CU_ASSERT(spdk_mem_all_zero(buf, params.logical_block_size));
606 			break;
607 		}
608 	}
609 
610 	g_reduce_errno = -1;
611 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
612 	CU_ASSERT(g_reduce_errno == 0);
613 
614 	g_vol = NULL;
615 	g_reduce_errno = -1;
616 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
617 	CU_ASSERT(g_reduce_errno == 0);
618 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
619 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
620 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
621 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
622 
623 	g_reduce_errno = -1;
624 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
625 	CU_ASSERT(g_reduce_errno == 0);
626 
627 	persistent_pm_buf_destroy();
628 	backing_dev_destroy(&backing_dev);
629 }
630 
631 static void
632 read_write(void)
633 {
634 	_read_write(512);
635 	_read_write(4096);
636 }
637 
638 int
639 main(int argc, char **argv)
640 {
641 	CU_pSuite	suite = NULL;
642 	unsigned int	num_failures;
643 
644 	if (CU_initialize_registry() != CUE_SUCCESS) {
645 		return CU_get_error();
646 	}
647 
648 	suite = CU_add_suite("reduce", NULL, NULL);
649 	if (suite == NULL) {
650 		CU_cleanup_registry();
651 		return CU_get_error();
652 	}
653 
654 	if (
655 		CU_add_test(suite, "get_pm_file_size", get_pm_file_size) == NULL ||
656 		CU_add_test(suite, "get_vol_size", get_vol_size) == NULL ||
657 		CU_add_test(suite, "init_failure", init_failure) == NULL ||
658 		CU_add_test(suite, "init_md", init_md) == NULL ||
659 		CU_add_test(suite, "init_backing_dev", init_backing_dev) == NULL ||
660 		CU_add_test(suite, "load", load) == NULL ||
661 		CU_add_test(suite, "write_maps", write_maps) == NULL ||
662 		CU_add_test(suite, "read_write", read_write) == NULL
663 	) {
664 		CU_cleanup_registry();
665 		return CU_get_error();
666 	}
667 
668 	CU_basic_set_mode(CU_BRM_VERBOSE);
669 	CU_basic_run_tests();
670 	num_failures = CU_get_number_of_failures();
671 	CU_cleanup_registry();
672 	return num_failures;
673 }
674