xref: /spdk/test/unit/lib/reduce/reduce.c/reduce_ut.c (revision 412fced1b5fc287476aabf5b48d7c7a89eaca00f)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2016 Intel Corporation.
36bf35070SJim Harris  *   All rights reserved.
442f59f50SAlexey Marchuk  *   Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
56bf35070SJim Harris  */
66bf35070SJim Harris 
76bf35070SJim Harris #include "spdk/stdinc.h"
86bf35070SJim Harris 
9ae431e31SKonrad Sztyber #include "spdk_internal/cunit.h"
106bf35070SJim Harris 
116bf35070SJim Harris #include "reduce/reduce.c"
12dfd44de7SJim Harris #include "spdk_internal/mock.h"
13b86e85f5SAlexey Marchuk #define UNIT_TEST_NO_VTOPHYS
146bf35070SJim Harris #include "common/lib/test_env.c"
15*412fced1SYalong Wang #include "thread/thread_internal.h"
16b86e85f5SAlexey Marchuk #undef UNIT_TEST_NO_VTOPHYS
176bf35070SJim Harris 
18f28f8133SJim Harris static struct spdk_reduce_vol *g_vol;
193213962eSJim Harris static int g_reduce_errno;
20dfd44de7SJim Harris static char *g_volatile_pm_buf;
21d31f2be5SJim Harris static size_t g_volatile_pm_buf_len;
22dfd44de7SJim Harris static char *g_persistent_pm_buf;
231d75aad3SJim Harris static size_t g_persistent_pm_buf_len;
24b7623dd4SJim Harris static char *g_backing_dev_buf;
25c887a4f9SJim Harris static char g_path[REDUCE_PATH_MAX];
262b336507Spaul luse static char *g_decomp_buf;
2742f59f50SAlexey Marchuk static int g_decompressed_len;
28dfd44de7SJim Harris 
29d31f2be5SJim Harris #define TEST_MD_PATH "/tmp"
30cfc372c2SJim Harris 
31b86e85f5SAlexey Marchuk uint64_t
32b86e85f5SAlexey Marchuk spdk_vtophys(const void *buf, uint64_t *size)
33b86e85f5SAlexey Marchuk {
34b86e85f5SAlexey Marchuk 	/* add + 1 to buf addr for cases where buf is the start of the page, that will give us correct end of the page */
35b86e85f5SAlexey Marchuk 	const uint8_t *page_2mb_end = (const uint8_t *)SPDK_ALIGN_CEIL((uintptr_t)buf + 1, VALUE_2MB);
36b86e85f5SAlexey Marchuk 	uint64_t bytes_to_page_end = page_2mb_end - (const uint8_t *)buf;
37b86e85f5SAlexey Marchuk 	uint64_t _size;
38b86e85f5SAlexey Marchuk 
39b86e85f5SAlexey Marchuk 	if (*size) {
40b86e85f5SAlexey Marchuk 		_size = *size;
41b86e85f5SAlexey Marchuk 		_size = spdk_min(_size, bytes_to_page_end);
42b86e85f5SAlexey Marchuk 		*size = _size;
43b86e85f5SAlexey Marchuk 	}
44b86e85f5SAlexey Marchuk 
45b86e85f5SAlexey Marchuk 	return (uintptr_t)buf;
46b86e85f5SAlexey Marchuk }
47b86e85f5SAlexey Marchuk 
4894d353eaSJim Harris enum ut_reduce_bdev_io_type {
4994d353eaSJim Harris 	UT_REDUCE_IO_READV = 1,
5094d353eaSJim Harris 	UT_REDUCE_IO_WRITEV = 2,
5194d353eaSJim Harris 	UT_REDUCE_IO_UNMAP = 3,
5294d353eaSJim Harris };
5394d353eaSJim Harris 
5494d353eaSJim Harris struct ut_reduce_bdev_io {
5594d353eaSJim Harris 	enum ut_reduce_bdev_io_type type;
5694d353eaSJim Harris 	struct spdk_reduce_backing_dev *backing_dev;
5794d353eaSJim Harris 	struct iovec *iov;
5894d353eaSJim Harris 	int iovcnt;
5994d353eaSJim Harris 	uint64_t lba;
6094d353eaSJim Harris 	uint32_t lba_count;
6194d353eaSJim Harris 	struct spdk_reduce_vol_cb_args *args;
6294d353eaSJim Harris 	TAILQ_ENTRY(ut_reduce_bdev_io)	link;
6394d353eaSJim Harris };
6494d353eaSJim Harris 
6594d353eaSJim Harris static bool g_defer_bdev_io = false;
6694d353eaSJim Harris static TAILQ_HEAD(, ut_reduce_bdev_io) g_pending_bdev_io =
6794d353eaSJim Harris 	TAILQ_HEAD_INITIALIZER(g_pending_bdev_io);
6894d353eaSJim Harris static uint32_t g_pending_bdev_io_count = 0;
69*412fced1SYalong Wang static struct spdk_thread *g_thread = NULL;
7094d353eaSJim Harris 
71dfd44de7SJim Harris static void
72dfd44de7SJim Harris sync_pm_buf(const void *addr, size_t length)
73dfd44de7SJim Harris {
74dfd44de7SJim Harris 	uint64_t offset = (char *)addr - g_volatile_pm_buf;
75dfd44de7SJim Harris 
76dfd44de7SJim Harris 	memcpy(&g_persistent_pm_buf[offset], addr, length);
77dfd44de7SJim Harris }
78dfd44de7SJim Harris 
79dfd44de7SJim Harris int
80dfd44de7SJim Harris pmem_msync(const void *addr, size_t length)
81dfd44de7SJim Harris {
82dfd44de7SJim Harris 	sync_pm_buf(addr, length);
83dfd44de7SJim Harris 	return 0;
84dfd44de7SJim Harris }
85dfd44de7SJim Harris 
86dfd44de7SJim Harris void
87dfd44de7SJim Harris pmem_persist(const void *addr, size_t len)
88dfd44de7SJim Harris {
89dfd44de7SJim Harris 	sync_pm_buf(addr, len);
90dfd44de7SJim Harris }
91f28f8133SJim Harris 
926bf35070SJim Harris static void
93dd42c808SJim Harris get_pm_file_size(void)
946bf35070SJim Harris {
95dd42c808SJim Harris 	struct spdk_reduce_vol_params params;
960b0af8f3SJim Harris 	uint64_t pm_size, expected_pm_size;
97dd42c808SJim Harris 
98dd42c808SJim Harris 	params.backing_io_unit_size = 4096;
99dd42c808SJim Harris 	params.chunk_size = 4096 * 4;
100dd42c808SJim Harris 	params.vol_size = 4096 * 4 * 100;
1010b0af8f3SJim Harris 
1020b0af8f3SJim Harris 	pm_size = _get_pm_file_size(&params);
103dd42c808SJim Harris 	expected_pm_size = sizeof(struct spdk_reduce_vol_superblock);
104dd42c808SJim Harris 	/* 100 chunks in logical map * 8 bytes per chunk */
105dd42c808SJim Harris 	expected_pm_size += 100 * sizeof(uint64_t);
106c9c7c281SJosh Soref 	/* 100 chunks * (chunk struct size + 4 backing io units per chunk * 8 bytes per backing io unit) */
107cc6314a4Spaul luse 	expected_pm_size += 100 * (sizeof(struct spdk_reduce_chunk_map) + 4 * sizeof(uint64_t));
108dd42c808SJim Harris 	/* reduce allocates some extra chunks too for in-flight writes when logical map
109cc6314a4Spaul luse 	 * is full.  REDUCE_EXTRA_CHUNKS is a private #ifdef in reduce.c Here we need the num chunks
110cc6314a4Spaul luse 	 * times (chunk struct size + 4 backing io units per chunk * 8 bytes per backing io unit).
111dd42c808SJim Harris 	 */
112cc6314a4Spaul luse 	expected_pm_size += REDUCE_NUM_EXTRA_CHUNKS *
113cc6314a4Spaul luse 			    (sizeof(struct spdk_reduce_chunk_map) + 4 * sizeof(uint64_t));
114dd42c808SJim Harris 	/* reduce will add some padding so numbers may not match exactly.  Make sure
115dd42c808SJim Harris 	 * they are close though.
116dd42c808SJim Harris 	 */
117cc6314a4Spaul luse 	CU_ASSERT((pm_size - expected_pm_size) <= REDUCE_PM_SIZE_ALIGNMENT);
118dd42c808SJim Harris }
119dd42c808SJim Harris 
120dd42c808SJim Harris static void
1210b0af8f3SJim Harris get_vol_size(void)
122dd42c808SJim Harris {
1230b0af8f3SJim Harris 	uint64_t chunk_size, backing_dev_size;
124dd42c808SJim Harris 
1250b0af8f3SJim Harris 	chunk_size = 16 * 1024;
1260b0af8f3SJim Harris 	backing_dev_size = 16 * 1024 * 1000;
1270b0af8f3SJim Harris 	CU_ASSERT(_get_vol_size(chunk_size, backing_dev_size) < backing_dev_size);
1286bf35070SJim Harris }
1296bf35070SJim Harris 
130d31f2be5SJim Harris void *
131d31f2be5SJim Harris pmem_map_file(const char *path, size_t len, int flags, mode_t mode,
132d31f2be5SJim Harris 	      size_t *mapped_lenp, int *is_pmemp)
133c6323a8dSJim Harris {
134d31f2be5SJim Harris 	CU_ASSERT(g_volatile_pm_buf == NULL);
135c887a4f9SJim Harris 	snprintf(g_path, sizeof(g_path), "%s", path);
136d31f2be5SJim Harris 	*is_pmemp = 1;
137d31f2be5SJim Harris 
138d31f2be5SJim Harris 	if (g_persistent_pm_buf == NULL) {
139d31f2be5SJim Harris 		g_persistent_pm_buf = calloc(1, len);
1401d75aad3SJim Harris 		g_persistent_pm_buf_len = len;
141d31f2be5SJim Harris 		SPDK_CU_ASSERT_FATAL(g_persistent_pm_buf != NULL);
142d31f2be5SJim Harris 	}
143d31f2be5SJim Harris 
1441d75aad3SJim Harris 	*mapped_lenp = g_persistent_pm_buf_len;
1451d75aad3SJim Harris 	g_volatile_pm_buf = calloc(1, g_persistent_pm_buf_len);
1461d75aad3SJim Harris 	SPDK_CU_ASSERT_FATAL(g_volatile_pm_buf != NULL);
14763ba545fSJim Harris 	memcpy(g_volatile_pm_buf, g_persistent_pm_buf, g_persistent_pm_buf_len);
1481d75aad3SJim Harris 	g_volatile_pm_buf_len = g_persistent_pm_buf_len;
1491d75aad3SJim Harris 
150d31f2be5SJim Harris 	return g_volatile_pm_buf;
151d31f2be5SJim Harris }
152d31f2be5SJim Harris 
153d31f2be5SJim Harris int
154d31f2be5SJim Harris pmem_unmap(void *addr, size_t len)
155d31f2be5SJim Harris {
156d31f2be5SJim Harris 	CU_ASSERT(addr == g_volatile_pm_buf);
157d31f2be5SJim Harris 	CU_ASSERT(len == g_volatile_pm_buf_len);
158c6323a8dSJim Harris 	free(g_volatile_pm_buf);
159c6323a8dSJim Harris 	g_volatile_pm_buf = NULL;
1601d75aad3SJim Harris 	g_volatile_pm_buf_len = 0;
161d31f2be5SJim Harris 
162d31f2be5SJim Harris 	return 0;
163c6323a8dSJim Harris }
164c6323a8dSJim Harris 
165c6323a8dSJim Harris static void
166d31f2be5SJim Harris persistent_pm_buf_destroy(void)
167c6323a8dSJim Harris {
168c6323a8dSJim Harris 	CU_ASSERT(g_persistent_pm_buf != NULL);
169c6323a8dSJim Harris 	free(g_persistent_pm_buf);
170c6323a8dSJim Harris 	g_persistent_pm_buf = NULL;
1711d75aad3SJim Harris 	g_persistent_pm_buf_len = 0;
172c6323a8dSJim Harris }
173c6323a8dSJim Harris 
174cfb65ba6SJim Harris static void
175cfb65ba6SJim Harris unlink_cb(void)
176c0f38051SJim Harris {
177c0f38051SJim Harris 	persistent_pm_buf_destroy();
178c0f38051SJim Harris }
179c0f38051SJim Harris 
180f28f8133SJim Harris static void
1813213962eSJim Harris init_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
182f28f8133SJim Harris {
183f28f8133SJim Harris 	g_vol = vol;
1843213962eSJim Harris 	g_reduce_errno = reduce_errno;
185f28f8133SJim Harris }
186f28f8133SJim Harris 
187f28f8133SJim Harris static void
1883213962eSJim Harris load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
189eebdab61SJim Harris {
190eebdab61SJim Harris 	g_vol = vol;
1913213962eSJim Harris 	g_reduce_errno = reduce_errno;
192eebdab61SJim Harris }
193eebdab61SJim Harris 
194eebdab61SJim Harris static void
1953213962eSJim Harris unload_cb(void *cb_arg, int reduce_errno)
196f28f8133SJim Harris {
1973213962eSJim Harris 	g_reduce_errno = reduce_errno;
198f28f8133SJim Harris }
199f28f8133SJim Harris 
200f28f8133SJim Harris static void
201649a585fSJim Harris init_failure(void)
202f28f8133SJim Harris {
203f28f8133SJim Harris 	struct spdk_reduce_vol_params params = {};
204f28f8133SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
205f28f8133SJim Harris 
206f28f8133SJim Harris 	backing_dev.blocklen = 512;
2070b0af8f3SJim Harris 	/* This blockcnt is too small for a reduce vol - there needs to be
2080b0af8f3SJim Harris 	 *  enough space for at least REDUCE_NUM_EXTRA_CHUNKS + 1 chunks.
2090b0af8f3SJim Harris 	 */
2100b0af8f3SJim Harris 	backing_dev.blockcnt = 20;
211f28f8133SJim Harris 
2120b0af8f3SJim Harris 	params.vol_size = 0;
213f28f8133SJim Harris 	params.chunk_size = 16 * 1024;
214f28f8133SJim Harris 	params.backing_io_unit_size = backing_dev.blocklen;
2155ae61d42SJim Harris 	params.logical_block_size = 512;
216f28f8133SJim Harris 
2170b0af8f3SJim Harris 	/* backing_dev has an invalid size.  This should fail. */
218f28f8133SJim Harris 	g_vol = NULL;
2193213962eSJim Harris 	g_reduce_errno = 0;
220d31f2be5SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
2213213962eSJim Harris 	CU_ASSERT(g_reduce_errno == -EINVAL);
222f28f8133SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol == NULL);
223f28f8133SJim Harris 
224d31f2be5SJim Harris 	/* backing_dev now has valid size, but backing_dev still has null
225d31f2be5SJim Harris 	 *  function pointers.  This should fail.
226f28f8133SJim Harris 	 */
2270b0af8f3SJim Harris 	backing_dev.blockcnt = 20000;
228f28f8133SJim Harris 
229f28f8133SJim Harris 	g_vol = NULL;
2303213962eSJim Harris 	g_reduce_errno = 0;
231d31f2be5SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
2323213962eSJim Harris 	CU_ASSERT(g_reduce_errno == -EINVAL);
233f28f8133SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol == NULL);
234dfd44de7SJim Harris }
235dfd44de7SJim Harris 
236dfd44de7SJim Harris static void
237670db760SJim Harris backing_dev_readv_execute(struct spdk_reduce_backing_dev *backing_dev,
238670db760SJim Harris 			  struct iovec *iov, int iovcnt,
239670db760SJim Harris 			  uint64_t lba, uint32_t lba_count,
240670db760SJim Harris 			  struct spdk_reduce_vol_cb_args *args)
241b7623dd4SJim Harris {
242b7623dd4SJim Harris 	char *offset;
243b7623dd4SJim Harris 	int i;
244b7623dd4SJim Harris 
245b7623dd4SJim Harris 	offset = g_backing_dev_buf + lba * backing_dev->blocklen;
246b7623dd4SJim Harris 	for (i = 0; i < iovcnt; i++) {
247b7623dd4SJim Harris 		memcpy(iov[i].iov_base, offset, iov[i].iov_len);
248b7623dd4SJim Harris 		offset += iov[i].iov_len;
249b7623dd4SJim Harris 	}
250b7623dd4SJim Harris 	args->cb_fn(args->cb_arg, 0);
251b7623dd4SJim Harris }
252b7623dd4SJim Harris 
253b7623dd4SJim Harris static void
25494d353eaSJim Harris backing_dev_insert_io(enum ut_reduce_bdev_io_type type, struct spdk_reduce_backing_dev *backing_dev,
25594d353eaSJim Harris 		      struct iovec *iov, int iovcnt, uint64_t lba, uint32_t lba_count,
25694d353eaSJim Harris 		      struct spdk_reduce_vol_cb_args *args)
25794d353eaSJim Harris {
25894d353eaSJim Harris 	struct ut_reduce_bdev_io *ut_bdev_io;
25994d353eaSJim Harris 
26094d353eaSJim Harris 	ut_bdev_io = calloc(1, sizeof(*ut_bdev_io));
26194d353eaSJim Harris 	SPDK_CU_ASSERT_FATAL(ut_bdev_io != NULL);
26294d353eaSJim Harris 
26394d353eaSJim Harris 	ut_bdev_io->type = type;
26494d353eaSJim Harris 	ut_bdev_io->backing_dev = backing_dev;
26594d353eaSJim Harris 	ut_bdev_io->iov = iov;
26694d353eaSJim Harris 	ut_bdev_io->iovcnt = iovcnt;
26794d353eaSJim Harris 	ut_bdev_io->lba = lba;
26894d353eaSJim Harris 	ut_bdev_io->lba_count = lba_count;
26994d353eaSJim Harris 	ut_bdev_io->args = args;
27094d353eaSJim Harris 	TAILQ_INSERT_TAIL(&g_pending_bdev_io, ut_bdev_io, link);
27194d353eaSJim Harris 	g_pending_bdev_io_count++;
27294d353eaSJim Harris }
27394d353eaSJim Harris 
27494d353eaSJim Harris static void
275670db760SJim Harris backing_dev_readv(struct spdk_reduce_backing_dev *backing_dev, struct iovec *iov, int iovcnt,
276b7623dd4SJim Harris 		  uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args)
277b7623dd4SJim Harris {
27894d353eaSJim Harris 	if (g_defer_bdev_io == false) {
27994d353eaSJim Harris 		CU_ASSERT(g_pending_bdev_io_count == 0);
28094d353eaSJim Harris 		CU_ASSERT(TAILQ_EMPTY(&g_pending_bdev_io));
281670db760SJim Harris 		backing_dev_readv_execute(backing_dev, iov, iovcnt, lba, lba_count, args);
28294d353eaSJim Harris 		return;
28394d353eaSJim Harris 	}
28494d353eaSJim Harris 
28594d353eaSJim Harris 	backing_dev_insert_io(UT_REDUCE_IO_READV, backing_dev, iov, iovcnt, lba, lba_count, args);
286670db760SJim Harris }
287670db760SJim Harris 
288670db760SJim Harris static void
289670db760SJim Harris backing_dev_writev_execute(struct spdk_reduce_backing_dev *backing_dev,
290670db760SJim Harris 			   struct iovec *iov, int iovcnt,
291670db760SJim Harris 			   uint64_t lba, uint32_t lba_count,
292670db760SJim Harris 			   struct spdk_reduce_vol_cb_args *args)
293670db760SJim Harris {
294b7623dd4SJim Harris 	char *offset;
295b7623dd4SJim Harris 	int i;
296b7623dd4SJim Harris 
297b7623dd4SJim Harris 	offset = g_backing_dev_buf + lba * backing_dev->blocklen;
298b7623dd4SJim Harris 	for (i = 0; i < iovcnt; i++) {
299b7623dd4SJim Harris 		memcpy(offset, iov[i].iov_base, iov[i].iov_len);
300b7623dd4SJim Harris 		offset += iov[i].iov_len;
301b7623dd4SJim Harris 	}
302b7623dd4SJim Harris 	args->cb_fn(args->cb_arg, 0);
303b7623dd4SJim Harris }
304b7623dd4SJim Harris 
305b7623dd4SJim Harris static void
306670db760SJim Harris backing_dev_writev(struct spdk_reduce_backing_dev *backing_dev, struct iovec *iov, int iovcnt,
307b7623dd4SJim Harris 		   uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args)
308b7623dd4SJim Harris {
30994d353eaSJim Harris 	if (g_defer_bdev_io == false) {
31094d353eaSJim Harris 		CU_ASSERT(g_pending_bdev_io_count == 0);
31194d353eaSJim Harris 		CU_ASSERT(TAILQ_EMPTY(&g_pending_bdev_io));
312670db760SJim Harris 		backing_dev_writev_execute(backing_dev, iov, iovcnt, lba, lba_count, args);
31394d353eaSJim Harris 		return;
31494d353eaSJim Harris 	}
31594d353eaSJim Harris 
31694d353eaSJim Harris 	backing_dev_insert_io(UT_REDUCE_IO_WRITEV, backing_dev, iov, iovcnt, lba, lba_count, args);
317670db760SJim Harris }
318670db760SJim Harris 
319670db760SJim Harris static void
320670db760SJim Harris backing_dev_unmap_execute(struct spdk_reduce_backing_dev *backing_dev,
321670db760SJim Harris 			  uint64_t lba, uint32_t lba_count,
322670db760SJim Harris 			  struct spdk_reduce_vol_cb_args *args)
323670db760SJim Harris {
324b7623dd4SJim Harris 	char *offset;
325b7623dd4SJim Harris 
326b7623dd4SJim Harris 	offset = g_backing_dev_buf + lba * backing_dev->blocklen;
327b7623dd4SJim Harris 	memset(offset, 0, lba_count * backing_dev->blocklen);
328b7623dd4SJim Harris 	args->cb_fn(args->cb_arg, 0);
329b7623dd4SJim Harris }
330b7623dd4SJim Harris 
331b7623dd4SJim Harris static void
332670db760SJim Harris backing_dev_unmap(struct spdk_reduce_backing_dev *backing_dev,
333670db760SJim Harris 		  uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args)
334670db760SJim Harris {
33594d353eaSJim Harris 	if (g_defer_bdev_io == false) {
33694d353eaSJim Harris 		CU_ASSERT(g_pending_bdev_io_count == 0);
33794d353eaSJim Harris 		CU_ASSERT(TAILQ_EMPTY(&g_pending_bdev_io));
338670db760SJim Harris 		backing_dev_unmap_execute(backing_dev, lba, lba_count, args);
33994d353eaSJim Harris 		return;
34094d353eaSJim Harris 	}
34194d353eaSJim Harris 
34294d353eaSJim Harris 	backing_dev_insert_io(UT_REDUCE_IO_UNMAP, backing_dev, NULL, 0, lba, lba_count, args);
34394d353eaSJim Harris }
34494d353eaSJim Harris 
34594d353eaSJim Harris static void
34694d353eaSJim Harris backing_dev_io_execute(uint32_t count)
34794d353eaSJim Harris {
34894d353eaSJim Harris 	struct ut_reduce_bdev_io *ut_bdev_io;
34994d353eaSJim Harris 	uint32_t done = 0;
35094d353eaSJim Harris 
35194d353eaSJim Harris 	CU_ASSERT(g_defer_bdev_io == true);
35294d353eaSJim Harris 	while (!TAILQ_EMPTY(&g_pending_bdev_io) && (count == 0 || done < count)) {
35394d353eaSJim Harris 		ut_bdev_io = TAILQ_FIRST(&g_pending_bdev_io);
35494d353eaSJim Harris 		TAILQ_REMOVE(&g_pending_bdev_io, ut_bdev_io, link);
35594d353eaSJim Harris 		g_pending_bdev_io_count--;
35694d353eaSJim Harris 		switch (ut_bdev_io->type) {
35794d353eaSJim Harris 		case UT_REDUCE_IO_READV:
35894d353eaSJim Harris 			backing_dev_readv_execute(ut_bdev_io->backing_dev,
35994d353eaSJim Harris 						  ut_bdev_io->iov, ut_bdev_io->iovcnt,
36094d353eaSJim Harris 						  ut_bdev_io->lba, ut_bdev_io->lba_count,
36194d353eaSJim Harris 						  ut_bdev_io->args);
36294d353eaSJim Harris 			break;
36394d353eaSJim Harris 		case UT_REDUCE_IO_WRITEV:
36494d353eaSJim Harris 			backing_dev_writev_execute(ut_bdev_io->backing_dev,
36594d353eaSJim Harris 						   ut_bdev_io->iov, ut_bdev_io->iovcnt,
36694d353eaSJim Harris 						   ut_bdev_io->lba, ut_bdev_io->lba_count,
36794d353eaSJim Harris 						   ut_bdev_io->args);
36894d353eaSJim Harris 			break;
36994d353eaSJim Harris 		case UT_REDUCE_IO_UNMAP:
37094d353eaSJim Harris 			backing_dev_unmap_execute(ut_bdev_io->backing_dev,
37194d353eaSJim Harris 						  ut_bdev_io->lba, ut_bdev_io->lba_count,
37294d353eaSJim Harris 						  ut_bdev_io->args);
37394d353eaSJim Harris 			break;
37494d353eaSJim Harris 		default:
37594d353eaSJim Harris 			CU_ASSERT(false);
37694d353eaSJim Harris 			break;
37794d353eaSJim Harris 		}
37894d353eaSJim Harris 		free(ut_bdev_io);
37994d353eaSJim Harris 		done++;
38094d353eaSJim Harris 	}
381670db760SJim Harris }
382670db760SJim Harris 
383245271b6SYankun Li static void
384245271b6SYankun Li backing_dev_submit_io(struct spdk_reduce_backing_io *backing_io)
385245271b6SYankun Li {
386245271b6SYankun Li 	switch (backing_io->backing_io_type) {
387245271b6SYankun Li 	case SPDK_REDUCE_BACKING_IO_WRITE:
388245271b6SYankun Li 		backing_dev_writev(backing_io->dev, backing_io->iov, backing_io->iovcnt,
389245271b6SYankun Li 				   backing_io->lba, backing_io->lba_count, backing_io->backing_cb_args);
390245271b6SYankun Li 		break;
391245271b6SYankun Li 	case SPDK_REDUCE_BACKING_IO_READ:
392245271b6SYankun Li 		backing_dev_readv(backing_io->dev, backing_io->iov, backing_io->iovcnt,
393245271b6SYankun Li 				  backing_io->lba, backing_io->lba_count, backing_io->backing_cb_args);
394245271b6SYankun Li 		break;
395245271b6SYankun Li 	case SPDK_REDUCE_BACKING_IO_UNMAP:
396245271b6SYankun Li 		backing_dev_unmap(backing_io->dev, backing_io->lba, backing_io->lba_count,
397245271b6SYankun Li 				  backing_io->backing_cb_args);
398245271b6SYankun Li 		break;
399245271b6SYankun Li 	default:
400245271b6SYankun Li 		CU_ASSERT(false);
401245271b6SYankun Li 		break;
402245271b6SYankun Li 	}
403245271b6SYankun Li }
404245271b6SYankun Li 
40527631eb5SJim Harris static int
40627631eb5SJim Harris ut_compress(char *outbuf, uint32_t *compressed_len, char *inbuf, uint32_t inbuflen)
40727631eb5SJim Harris {
40827631eb5SJim Harris 	uint32_t len = 0;
40927631eb5SJim Harris 	uint8_t count;
41027631eb5SJim Harris 	char last;
41127631eb5SJim Harris 
41227631eb5SJim Harris 	while (true) {
41327631eb5SJim Harris 		if (inbuflen == 0) {
41427631eb5SJim Harris 			*compressed_len = len;
41527631eb5SJim Harris 			return 0;
41627631eb5SJim Harris 		}
41727631eb5SJim Harris 
41827631eb5SJim Harris 		if (*compressed_len < (len + 2)) {
41927631eb5SJim Harris 			return -ENOSPC;
42027631eb5SJim Harris 		}
42127631eb5SJim Harris 
42227631eb5SJim Harris 		last = *inbuf;
42327631eb5SJim Harris 		count = 1;
42427631eb5SJim Harris 		inbuflen--;
42527631eb5SJim Harris 		inbuf++;
42627631eb5SJim Harris 
42727631eb5SJim Harris 		while (inbuflen > 0 && *inbuf == last && count < UINT8_MAX) {
42827631eb5SJim Harris 			count++;
42927631eb5SJim Harris 			inbuflen--;
43027631eb5SJim Harris 			inbuf++;
43127631eb5SJim Harris 		}
43227631eb5SJim Harris 
43327631eb5SJim Harris 		outbuf[len] = count;
43427631eb5SJim Harris 		outbuf[len + 1] = last;
43527631eb5SJim Harris 		len += 2;
43627631eb5SJim Harris 	}
43727631eb5SJim Harris }
43827631eb5SJim Harris 
43927631eb5SJim Harris static int
44027631eb5SJim Harris ut_decompress(uint8_t *outbuf, uint32_t *compressed_len, uint8_t *inbuf, uint32_t inbuflen)
44127631eb5SJim Harris {
44227631eb5SJim Harris 	uint32_t len = 0;
44327631eb5SJim Harris 
44427631eb5SJim Harris 	SPDK_CU_ASSERT_FATAL(inbuflen % 2 == 0);
44527631eb5SJim Harris 
44627631eb5SJim Harris 	while (true) {
44727631eb5SJim Harris 		if (inbuflen == 0) {
44827631eb5SJim Harris 			*compressed_len = len;
44927631eb5SJim Harris 			return 0;
45027631eb5SJim Harris 		}
45127631eb5SJim Harris 
45227631eb5SJim Harris 		if ((len + inbuf[0]) > *compressed_len) {
45327631eb5SJim Harris 			return -ENOSPC;
45427631eb5SJim Harris 		}
45527631eb5SJim Harris 
45627631eb5SJim Harris 		memset(outbuf, inbuf[1], inbuf[0]);
45727631eb5SJim Harris 		outbuf += inbuf[0];
45827631eb5SJim Harris 		len += inbuf[0];
45927631eb5SJim Harris 		inbuflen -= 2;
46027631eb5SJim Harris 		inbuf += 2;
46127631eb5SJim Harris 	}
46227631eb5SJim Harris }
46327631eb5SJim Harris 
46427631eb5SJim Harris static void
46527631eb5SJim Harris ut_build_data_buffer(uint8_t *data, uint32_t data_len, uint8_t init_val, uint32_t repeat)
46627631eb5SJim Harris {
46727631eb5SJim Harris 	uint32_t _repeat = repeat;
46827631eb5SJim Harris 
46927631eb5SJim Harris 	SPDK_CU_ASSERT_FATAL(repeat > 0);
47027631eb5SJim Harris 
47127631eb5SJim Harris 	while (data_len > 0) {
47227631eb5SJim Harris 		*data = init_val;
47327631eb5SJim Harris 		data++;
47427631eb5SJim Harris 		data_len--;
47527631eb5SJim Harris 		_repeat--;
47627631eb5SJim Harris 		if (_repeat == 0) {
47727631eb5SJim Harris 			init_val++;
47827631eb5SJim Harris 			_repeat = repeat;
47927631eb5SJim Harris 		}
48027631eb5SJim Harris 	}
48127631eb5SJim Harris }
48227631eb5SJim Harris 
483670db760SJim Harris static void
484309f7791SJim Harris backing_dev_compress(struct spdk_reduce_backing_dev *backing_dev,
485309f7791SJim Harris 		     struct iovec *src_iov, int src_iovcnt,
486309f7791SJim Harris 		     struct iovec *dst_iov, int dst_iovcnt,
487309f7791SJim Harris 		     struct spdk_reduce_vol_cb_args *args)
488309f7791SJim Harris {
4891679104eSJim Harris 	uint32_t compressed_len;
4902b336507Spaul luse 	uint64_t total_length = 0;
4912b336507Spaul luse 	char *buf = g_decomp_buf;
4922b336507Spaul luse 	int rc, i;
4931679104eSJim Harris 
494309f7791SJim Harris 	CU_ASSERT(dst_iovcnt == 1);
4952b336507Spaul luse 
4962b336507Spaul luse 	for (i = 0; i < src_iovcnt; i++) {
4972b336507Spaul luse 		memcpy(buf, src_iov[i].iov_base, src_iov[i].iov_len);
4982b336507Spaul luse 		buf += src_iov[i].iov_len;
4992b336507Spaul luse 		total_length += src_iov[i].iov_len;
5002b336507Spaul luse 	}
5011679104eSJim Harris 
5021679104eSJim Harris 	compressed_len = dst_iov[0].iov_len;
5031679104eSJim Harris 	rc = ut_compress(dst_iov[0].iov_base, &compressed_len,
5042b336507Spaul luse 			 g_decomp_buf, total_length);
5052b336507Spaul luse 
506bb5083a8Spaul luse 	args->output_size = compressed_len;
507bb5083a8Spaul luse 
508bb5083a8Spaul luse 	args->cb_fn(args->cb_arg, rc);
509309f7791SJim Harris }
510309f7791SJim Harris 
511309f7791SJim Harris static void
512309f7791SJim Harris backing_dev_decompress(struct spdk_reduce_backing_dev *backing_dev,
513309f7791SJim Harris 		       struct iovec *src_iov, int src_iovcnt,
514309f7791SJim Harris 		       struct iovec *dst_iov, int dst_iovcnt,
515309f7791SJim Harris 		       struct spdk_reduce_vol_cb_args *args)
516309f7791SJim Harris {
5172b336507Spaul luse 	uint32_t decompressed_len = 0;
5182b336507Spaul luse 	char *buf = g_decomp_buf;
5192b336507Spaul luse 	int rc, i;
5201679104eSJim Harris 
521309f7791SJim Harris 	CU_ASSERT(src_iovcnt == 1);
5221679104eSJim Harris 
5232b336507Spaul luse 	for (i = 0; i < dst_iovcnt; i++) {
5242b336507Spaul luse 		decompressed_len += dst_iov[i].iov_len;
5252b336507Spaul luse 	}
5262b336507Spaul luse 
5272b336507Spaul luse 	rc = ut_decompress(g_decomp_buf, &decompressed_len,
5281679104eSJim Harris 			   src_iov[0].iov_base, src_iov[0].iov_len);
5292b336507Spaul luse 
5302b336507Spaul luse 	for (i = 0; i < dst_iovcnt; i++) {
5312b336507Spaul luse 		memcpy(dst_iov[i].iov_base, buf, dst_iov[i].iov_len);
5322b336507Spaul luse 		buf += dst_iov[i].iov_len;
5332b336507Spaul luse 	}
5342b336507Spaul luse 
535bb5083a8Spaul luse 	args->output_size = decompressed_len;
536bb5083a8Spaul luse 
537bb5083a8Spaul luse 	args->cb_fn(args->cb_arg, rc);
538309f7791SJim Harris }
539309f7791SJim Harris 
540309f7791SJim Harris static void
541b7623dd4SJim Harris backing_dev_destroy(struct spdk_reduce_backing_dev *backing_dev)
542b7623dd4SJim Harris {
543b7623dd4SJim Harris 	/* We don't free this during backing_dev_close so that we can test init/unload/load
544b7623dd4SJim Harris 	 *  scenarios.
545b7623dd4SJim Harris 	 */
546b7623dd4SJim Harris 	free(g_backing_dev_buf);
5472b336507Spaul luse 	free(g_decomp_buf);
548b7623dd4SJim Harris 	g_backing_dev_buf = NULL;
549b7623dd4SJim Harris }
550b7623dd4SJim Harris 
551b7623dd4SJim Harris static void
552212770dbSJim Harris backing_dev_init(struct spdk_reduce_backing_dev *backing_dev, struct spdk_reduce_vol_params *params,
553212770dbSJim Harris 		 uint32_t backing_blocklen)
5543981ba69SJim Harris {
555b7623dd4SJim Harris 	int64_t size;
556b7623dd4SJim Harris 
5570b0af8f3SJim Harris 	size = 4 * 1024 * 1024;
558212770dbSJim Harris 	backing_dev->blocklen = backing_blocklen;
559b7623dd4SJim Harris 	backing_dev->blockcnt = size / backing_dev->blocklen;
560245271b6SYankun Li 	backing_dev->submit_backing_io = backing_dev_submit_io;
561309f7791SJim Harris 	backing_dev->compress = backing_dev_compress;
562309f7791SJim Harris 	backing_dev->decompress = backing_dev_decompress;
56342f59f50SAlexey Marchuk 	backing_dev->sgl_in = true;
56442f59f50SAlexey Marchuk 	backing_dev->sgl_out = true;
565b7623dd4SJim Harris 
5662b336507Spaul luse 	g_decomp_buf = calloc(1, params->chunk_size);
5672b336507Spaul luse 	SPDK_CU_ASSERT_FATAL(g_decomp_buf != NULL);
5682b336507Spaul luse 
569b7623dd4SJim Harris 	g_backing_dev_buf = calloc(1, size);
570b7623dd4SJim Harris 	SPDK_CU_ASSERT_FATAL(g_backing_dev_buf != NULL);
5713981ba69SJim Harris }
5723981ba69SJim Harris 
5733981ba69SJim Harris static void
574dfd44de7SJim Harris init_md(void)
575dfd44de7SJim Harris {
576dfd44de7SJim Harris 	struct spdk_reduce_vol_params params = {};
577dfd44de7SJim Harris 	struct spdk_reduce_vol_params *persistent_params;
578dfd44de7SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
579d31f2be5SJim Harris 	struct spdk_uuid uuid;
580a03515deSJim Harris 	uint64_t *entry;
581dfd44de7SJim Harris 
582dfd44de7SJim Harris 	params.chunk_size = 16 * 1024;
5833981ba69SJim Harris 	params.backing_io_unit_size = 512;
5845ae61d42SJim Harris 	params.logical_block_size = 512;
585dfd44de7SJim Harris 
586212770dbSJim Harris 	backing_dev_init(&backing_dev, &params, 512);
587dfd44de7SJim Harris 
588dfd44de7SJim Harris 	g_vol = NULL;
5893213962eSJim Harris 	g_reduce_errno = -1;
590d31f2be5SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
5913213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
592dfd44de7SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
593dfd44de7SJim Harris 	/* Confirm that reduce persisted the params to metadata. */
594dfd44de7SJim Harris 	CU_ASSERT(memcmp(g_persistent_pm_buf, SPDK_REDUCE_SIGNATURE, 8) == 0);
595dfd44de7SJim Harris 	persistent_params = (struct spdk_reduce_vol_params *)(g_persistent_pm_buf + 8);
596dfd44de7SJim Harris 	CU_ASSERT(memcmp(persistent_params, &params, sizeof(params)) == 0);
597a03515deSJim Harris 	/* Now confirm that contents of pm_file after the superblock have been initialized
598a03515deSJim Harris 	 *  to REDUCE_EMPTY_MAP_ENTRY.
599a03515deSJim Harris 	 */
600a03515deSJim Harris 	entry = (uint64_t *)(g_persistent_pm_buf + sizeof(struct spdk_reduce_vol_superblock));
601a03515deSJim Harris 	while (entry != (uint64_t *)(g_persistent_pm_buf + g_vol->pm_file.size)) {
602a03515deSJim Harris 		CU_ASSERT(*entry == REDUCE_EMPTY_MAP_ENTRY);
603a03515deSJim Harris 		entry++;
604a03515deSJim Harris 	}
605dfd44de7SJim Harris 
606d31f2be5SJim Harris 	/* Check that the pm file path was constructed correctly.  It should be in
607d31f2be5SJim Harris 	 * the form:
608d31f2be5SJim Harris 	 * TEST_MD_PATH + "/" + <uuid string>
609d31f2be5SJim Harris 	 */
610d31f2be5SJim Harris 	CU_ASSERT(strncmp(&g_path[0], TEST_MD_PATH, strlen(TEST_MD_PATH)) == 0);
611d31f2be5SJim Harris 	CU_ASSERT(g_path[strlen(TEST_MD_PATH)] == '/');
612d31f2be5SJim Harris 	CU_ASSERT(spdk_uuid_parse(&uuid, &g_path[strlen(TEST_MD_PATH) + 1]) == 0);
613d31f2be5SJim Harris 	CU_ASSERT(spdk_uuid_compare(&uuid, spdk_reduce_vol_get_uuid(g_vol)) == 0);
614d31f2be5SJim Harris 
6153213962eSJim Harris 	g_reduce_errno = -1;
616dfd44de7SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
6173213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
618c6323a8dSJim Harris 	CU_ASSERT(g_volatile_pm_buf == NULL);
619c6323a8dSJim Harris 
620d31f2be5SJim Harris 	persistent_pm_buf_destroy();
621b7623dd4SJim Harris 	backing_dev_destroy(&backing_dev);
622f28f8133SJim Harris }
623f28f8133SJim Harris 
624b4f9dd1aSJim Harris static void
625212770dbSJim Harris _init_backing_dev(uint32_t backing_blocklen)
626b4f9dd1aSJim Harris {
627b4f9dd1aSJim Harris 	struct spdk_reduce_vol_params params = {};
628b4f9dd1aSJim Harris 	struct spdk_reduce_vol_params *persistent_params;
629b4f9dd1aSJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
630b4f9dd1aSJim Harris 
631b4f9dd1aSJim Harris 	params.chunk_size = 16 * 1024;
632b4f9dd1aSJim Harris 	params.backing_io_unit_size = 512;
6335ae61d42SJim Harris 	params.logical_block_size = 512;
634b4f9dd1aSJim Harris 	spdk_uuid_generate(&params.uuid);
635b4f9dd1aSJim Harris 
636212770dbSJim Harris 	backing_dev_init(&backing_dev, &params, backing_blocklen);
637b4f9dd1aSJim Harris 
638b4f9dd1aSJim Harris 	g_vol = NULL;
639c887a4f9SJim Harris 	memset(g_path, 0, sizeof(g_path));
6403213962eSJim Harris 	g_reduce_errno = -1;
641d31f2be5SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
6423213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
643b4f9dd1aSJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
644c887a4f9SJim Harris 	CU_ASSERT(strncmp(TEST_MD_PATH, g_path, strlen(TEST_MD_PATH)) == 0);
645b4f9dd1aSJim Harris 	/* Confirm that libreduce persisted the params to the backing device. */
646b4f9dd1aSJim Harris 	CU_ASSERT(memcmp(g_backing_dev_buf, SPDK_REDUCE_SIGNATURE, 8) == 0);
647b4f9dd1aSJim Harris 	persistent_params = (struct spdk_reduce_vol_params *)(g_backing_dev_buf + 8);
648b4f9dd1aSJim Harris 	CU_ASSERT(memcmp(persistent_params, &params, sizeof(params)) == 0);
649cfc372c2SJim Harris 	/* Confirm that the path to the persistent memory metadata file was persisted to
650cfc372c2SJim Harris 	 *  the backing device.
651cfc372c2SJim Harris 	 */
652d31f2be5SJim Harris 	CU_ASSERT(strncmp(g_path,
653cfc372c2SJim Harris 			  g_backing_dev_buf + REDUCE_BACKING_DEV_PATH_OFFSET,
654cfc372c2SJim Harris 			  REDUCE_PATH_MAX) == 0);
655b4f9dd1aSJim Harris 
6563213962eSJim Harris 	g_reduce_errno = -1;
657b4f9dd1aSJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
6583213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
659b4f9dd1aSJim Harris 
660d31f2be5SJim Harris 	persistent_pm_buf_destroy();
661b4f9dd1aSJim Harris 	backing_dev_destroy(&backing_dev);
662b4f9dd1aSJim Harris }
663b4f9dd1aSJim Harris 
664eebdab61SJim Harris static void
665212770dbSJim Harris init_backing_dev(void)
666212770dbSJim Harris {
667212770dbSJim Harris 	_init_backing_dev(512);
668212770dbSJim Harris 	_init_backing_dev(4096);
669212770dbSJim Harris }
670212770dbSJim Harris 
671212770dbSJim Harris static void
672212770dbSJim Harris _load(uint32_t backing_blocklen)
673eebdab61SJim Harris {
674eebdab61SJim Harris 	struct spdk_reduce_vol_params params = {};
675eebdab61SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
676eebdab61SJim Harris 	char pmem_file_path[REDUCE_PATH_MAX];
677eebdab61SJim Harris 
678eebdab61SJim Harris 	params.chunk_size = 16 * 1024;
679eebdab61SJim Harris 	params.backing_io_unit_size = 512;
6805ae61d42SJim Harris 	params.logical_block_size = 512;
681eebdab61SJim Harris 	spdk_uuid_generate(&params.uuid);
682eebdab61SJim Harris 
683212770dbSJim Harris 	backing_dev_init(&backing_dev, &params, backing_blocklen);
684eebdab61SJim Harris 
685eebdab61SJim Harris 	g_vol = NULL;
6863213962eSJim Harris 	g_reduce_errno = -1;
687eebdab61SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
6883213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
689eebdab61SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
690c887a4f9SJim Harris 	CU_ASSERT(strncmp(TEST_MD_PATH, g_path, strlen(TEST_MD_PATH)) == 0);
691eebdab61SJim Harris 	memcpy(pmem_file_path, g_path, sizeof(pmem_file_path));
692eebdab61SJim Harris 
6933213962eSJim Harris 	g_reduce_errno = -1;
694eebdab61SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
6953213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
696eebdab61SJim Harris 
697eebdab61SJim Harris 	g_vol = NULL;
698c887a4f9SJim Harris 	memset(g_path, 0, sizeof(g_path));
6993213962eSJim Harris 	g_reduce_errno = -1;
700eebdab61SJim Harris 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
7013213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
702eebdab61SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
703eebdab61SJim Harris 	CU_ASSERT(strncmp(g_path, pmem_file_path, sizeof(pmem_file_path)) == 0);
704922258a6SJim Harris 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
705922258a6SJim Harris 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
706922258a6SJim Harris 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
707eebdab61SJim Harris 
7083213962eSJim Harris 	g_reduce_errno = -1;
709eebdab61SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
7103213962eSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
711eebdab61SJim Harris 
712eebdab61SJim Harris 	persistent_pm_buf_destroy();
713eebdab61SJim Harris 	backing_dev_destroy(&backing_dev);
714eebdab61SJim Harris }
715eebdab61SJim Harris 
716212770dbSJim Harris static void
717212770dbSJim Harris load(void)
718212770dbSJim Harris {
719212770dbSJim Harris 	_load(512);
720212770dbSJim Harris 	_load(4096);
721212770dbSJim Harris }
722212770dbSJim Harris 
72384f8b633SJim Harris static uint64_t
72484f8b633SJim Harris _vol_get_chunk_map_index(struct spdk_reduce_vol *vol, uint64_t offset)
72584f8b633SJim Harris {
72684f8b633SJim Harris 	uint64_t logical_map_index = offset / vol->logical_blocks_per_chunk;
72784f8b633SJim Harris 
72884f8b633SJim Harris 	return vol->pm_logical_map[logical_map_index];
72984f8b633SJim Harris }
73084f8b633SJim Harris 
73184f8b633SJim Harris static void
73284f8b633SJim Harris write_cb(void *arg, int reduce_errno)
73384f8b633SJim Harris {
73484f8b633SJim Harris 	g_reduce_errno = reduce_errno;
73584f8b633SJim Harris }
73684f8b633SJim Harris 
73784f8b633SJim Harris static void
738383b1173SJim Harris read_cb(void *arg, int reduce_errno)
739383b1173SJim Harris {
740383b1173SJim Harris 	g_reduce_errno = reduce_errno;
741383b1173SJim Harris }
742383b1173SJim Harris 
743383b1173SJim Harris static void
744*412fced1SYalong Wang unmap_cb(void *arg, int reduce_errno)
745*412fced1SYalong Wang {
746*412fced1SYalong Wang 	g_reduce_errno = reduce_errno;
747*412fced1SYalong Wang }
748*412fced1SYalong Wang 
749*412fced1SYalong Wang static void
750212770dbSJim Harris _write_maps(uint32_t backing_blocklen)
75184f8b633SJim Harris {
75284f8b633SJim Harris 	struct spdk_reduce_vol_params params = {};
75384f8b633SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
75484f8b633SJim Harris 	struct iovec iov;
755cdd64c1cSJim Harris 	const int bufsize = 16 * 1024; /* chunk size */
756cdd64c1cSJim Harris 	char buf[bufsize];
757cdd64c1cSJim Harris 	uint32_t num_lbas, i;
75884f8b633SJim Harris 	uint64_t old_chunk0_map_index, new_chunk0_map_index;
7593c070c97SJim Harris 	struct spdk_reduce_chunk_map *old_chunk0_map, *new_chunk0_map;
76084f8b633SJim Harris 
761cdd64c1cSJim Harris 	params.chunk_size = bufsize;
76284f8b633SJim Harris 	params.backing_io_unit_size = 4096;
76384f8b633SJim Harris 	params.logical_block_size = 512;
764cdd64c1cSJim Harris 	num_lbas = bufsize / params.logical_block_size;
76584f8b633SJim Harris 	spdk_uuid_generate(&params.uuid);
76684f8b633SJim Harris 
767212770dbSJim Harris 	backing_dev_init(&backing_dev, &params, backing_blocklen);
76884f8b633SJim Harris 
76984f8b633SJim Harris 	g_vol = NULL;
77084f8b633SJim Harris 	g_reduce_errno = -1;
77184f8b633SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
77284f8b633SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
77384f8b633SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
77484f8b633SJim Harris 
77584f8b633SJim Harris 	for (i = 0; i < g_vol->params.vol_size / g_vol->params.chunk_size; i++) {
77684f8b633SJim Harris 		CU_ASSERT(_vol_get_chunk_map_index(g_vol, i) == REDUCE_EMPTY_MAP_ENTRY);
77784f8b633SJim Harris 	}
77884f8b633SJim Harris 
779cdd64c1cSJim Harris 	ut_build_data_buffer(buf, bufsize, 0x00, 1);
78084f8b633SJim Harris 	iov.iov_base = buf;
781cdd64c1cSJim Harris 	iov.iov_len = bufsize;
78284f8b633SJim Harris 	g_reduce_errno = -1;
783cdd64c1cSJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, num_lbas, write_cb, NULL);
78484f8b633SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
78584f8b633SJim Harris 
78684f8b633SJim Harris 	old_chunk0_map_index = _vol_get_chunk_map_index(g_vol, 0);
78784f8b633SJim Harris 	CU_ASSERT(old_chunk0_map_index != REDUCE_EMPTY_MAP_ENTRY);
78884f8b633SJim Harris 	CU_ASSERT(spdk_bit_array_get(g_vol->allocated_chunk_maps, old_chunk0_map_index) == true);
78984f8b633SJim Harris 
7903c070c97SJim Harris 	old_chunk0_map = _reduce_vol_get_chunk_map(g_vol, old_chunk0_map_index);
79184f8b633SJim Harris 	for (i = 0; i < g_vol->backing_io_units_per_chunk; i++) {
7923c070c97SJim Harris 		CU_ASSERT(old_chunk0_map->io_unit_index[i] != REDUCE_EMPTY_MAP_ENTRY);
7933c070c97SJim Harris 		CU_ASSERT(spdk_bit_array_get(g_vol->allocated_backing_io_units,
7943c070c97SJim Harris 					     old_chunk0_map->io_unit_index[i]) == true);
79584f8b633SJim Harris 	}
79684f8b633SJim Harris 
79784f8b633SJim Harris 	g_reduce_errno = -1;
798cdd64c1cSJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, num_lbas, write_cb, NULL);
79984f8b633SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
80084f8b633SJim Harris 
80184f8b633SJim Harris 	new_chunk0_map_index = _vol_get_chunk_map_index(g_vol, 0);
80284f8b633SJim Harris 	CU_ASSERT(new_chunk0_map_index != REDUCE_EMPTY_MAP_ENTRY);
80384f8b633SJim Harris 	CU_ASSERT(new_chunk0_map_index != old_chunk0_map_index);
80484f8b633SJim Harris 	CU_ASSERT(spdk_bit_array_get(g_vol->allocated_chunk_maps, new_chunk0_map_index) == true);
80584f8b633SJim Harris 	CU_ASSERT(spdk_bit_array_get(g_vol->allocated_chunk_maps, old_chunk0_map_index) == false);
80684f8b633SJim Harris 
80784f8b633SJim Harris 	for (i = 0; i < g_vol->backing_io_units_per_chunk; i++) {
8083c070c97SJim Harris 		CU_ASSERT(spdk_bit_array_get(g_vol->allocated_backing_io_units,
8093c070c97SJim Harris 					     old_chunk0_map->io_unit_index[i]) == false);
81084f8b633SJim Harris 	}
81184f8b633SJim Harris 
8123c070c97SJim Harris 	new_chunk0_map = _reduce_vol_get_chunk_map(g_vol, new_chunk0_map_index);
81384f8b633SJim Harris 	for (i = 0; i < g_vol->backing_io_units_per_chunk; i++) {
8143c070c97SJim Harris 		CU_ASSERT(new_chunk0_map->io_unit_index[i] != REDUCE_EMPTY_MAP_ENTRY);
8153c070c97SJim Harris 		CU_ASSERT(spdk_bit_array_get(g_vol->allocated_backing_io_units,
8163c070c97SJim Harris 					     new_chunk0_map->io_unit_index[i]) == true);
81784f8b633SJim Harris 	}
81884f8b633SJim Harris 
81984f8b633SJim Harris 	g_reduce_errno = -1;
82084f8b633SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
82184f8b633SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
82284f8b633SJim Harris 
82384f8b633SJim Harris 	g_vol = NULL;
82484f8b633SJim Harris 	g_reduce_errno = -1;
82584f8b633SJim Harris 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
82684f8b633SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
82784f8b633SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
82884f8b633SJim Harris 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
82984f8b633SJim Harris 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
83084f8b633SJim Harris 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
83184f8b633SJim Harris 
83284f8b633SJim Harris 	g_reduce_errno = -1;
83384f8b633SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
83484f8b633SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
83584f8b633SJim Harris 
83684f8b633SJim Harris 	persistent_pm_buf_destroy();
83784f8b633SJim Harris 	backing_dev_destroy(&backing_dev);
83884f8b633SJim Harris }
83984f8b633SJim Harris 
840212770dbSJim Harris static void
841212770dbSJim Harris write_maps(void)
842212770dbSJim Harris {
843212770dbSJim Harris 	_write_maps(512);
844212770dbSJim Harris 	_write_maps(4096);
845212770dbSJim Harris }
846383b1173SJim Harris 
847383b1173SJim Harris static void
848212770dbSJim Harris _read_write(uint32_t backing_blocklen)
849383b1173SJim Harris {
850383b1173SJim Harris 	struct spdk_reduce_vol_params params = {};
851383b1173SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
852383b1173SJim Harris 	struct iovec iov;
853383b1173SJim Harris 	char buf[16 * 1024]; /* chunk size */
854383b1173SJim Harris 	char compare_buf[16 * 1024];
855383b1173SJim Harris 	uint32_t i;
856383b1173SJim Harris 
857383b1173SJim Harris 	params.chunk_size = 16 * 1024;
858383b1173SJim Harris 	params.backing_io_unit_size = 4096;
859383b1173SJim Harris 	params.logical_block_size = 512;
860383b1173SJim Harris 	spdk_uuid_generate(&params.uuid);
861383b1173SJim Harris 
862212770dbSJim Harris 	backing_dev_init(&backing_dev, &params, backing_blocklen);
863383b1173SJim Harris 
864383b1173SJim Harris 	g_vol = NULL;
865383b1173SJim Harris 	g_reduce_errno = -1;
866383b1173SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
867383b1173SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
868383b1173SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
869383b1173SJim Harris 
870383b1173SJim Harris 	/* Write 0xAA to 2 512-byte logical blocks, starting at LBA 2. */
871383b1173SJim Harris 	memset(buf, 0xAA, 2 * params.logical_block_size);
872383b1173SJim Harris 	iov.iov_base = buf;
873383b1173SJim Harris 	iov.iov_len = 2 * params.logical_block_size;
874383b1173SJim Harris 	g_reduce_errno = -1;
875383b1173SJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 2, 2, write_cb, NULL);
876383b1173SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
877383b1173SJim Harris 
878383b1173SJim Harris 	memset(compare_buf, 0xAA, sizeof(compare_buf));
879383b1173SJim Harris 	for (i = 0; i < params.chunk_size / params.logical_block_size; i++) {
880383b1173SJim Harris 		memset(buf, 0xFF, params.logical_block_size);
881383b1173SJim Harris 		iov.iov_base = buf;
882383b1173SJim Harris 		iov.iov_len = params.logical_block_size;
883383b1173SJim Harris 		g_reduce_errno = -1;
884383b1173SJim Harris 		spdk_reduce_vol_readv(g_vol, &iov, 1, i, 1, read_cb, NULL);
885383b1173SJim Harris 		CU_ASSERT(g_reduce_errno == 0);
886383b1173SJim Harris 
887383b1173SJim Harris 		switch (i) {
888383b1173SJim Harris 		case 2:
889383b1173SJim Harris 		case 3:
890383b1173SJim Harris 			CU_ASSERT(memcmp(buf, compare_buf, params.logical_block_size) == 0);
891383b1173SJim Harris 			break;
892383b1173SJim Harris 		default:
893383b1173SJim Harris 			CU_ASSERT(spdk_mem_all_zero(buf, params.logical_block_size));
894383b1173SJim Harris 			break;
895383b1173SJim Harris 		}
896383b1173SJim Harris 	}
897383b1173SJim Harris 
898383b1173SJim Harris 	g_reduce_errno = -1;
899383b1173SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
900383b1173SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
901383b1173SJim Harris 
902cf467578Spaul luse 	/* Overwrite what we just wrote with 0xCC */
903cf467578Spaul luse 	g_vol = NULL;
904cf467578Spaul luse 	g_reduce_errno = -1;
905cf467578Spaul luse 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
906cf467578Spaul luse 	CU_ASSERT(g_reduce_errno == 0);
907cf467578Spaul luse 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
908cf467578Spaul luse 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
909cf467578Spaul luse 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
910cf467578Spaul luse 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
911cf467578Spaul luse 
912cf467578Spaul luse 	memset(buf, 0xCC, 2 * params.logical_block_size);
913cf467578Spaul luse 	iov.iov_base = buf;
914cf467578Spaul luse 	iov.iov_len = 2 * params.logical_block_size;
915cf467578Spaul luse 	g_reduce_errno = -1;
916cf467578Spaul luse 	spdk_reduce_vol_writev(g_vol, &iov, 1, 2, 2, write_cb, NULL);
917cf467578Spaul luse 	CU_ASSERT(g_reduce_errno == 0);
918cf467578Spaul luse 
919cf467578Spaul luse 	memset(compare_buf, 0xCC, sizeof(compare_buf));
920cf467578Spaul luse 	for (i = 0; i < params.chunk_size / params.logical_block_size; i++) {
921cf467578Spaul luse 		memset(buf, 0xFF, params.logical_block_size);
922cf467578Spaul luse 		iov.iov_base = buf;
923cf467578Spaul luse 		iov.iov_len = params.logical_block_size;
924cf467578Spaul luse 		g_reduce_errno = -1;
925cf467578Spaul luse 		spdk_reduce_vol_readv(g_vol, &iov, 1, i, 1, read_cb, NULL);
926cf467578Spaul luse 		CU_ASSERT(g_reduce_errno == 0);
927cf467578Spaul luse 
928cf467578Spaul luse 		switch (i) {
929cf467578Spaul luse 		case 2:
930cf467578Spaul luse 		case 3:
931cf467578Spaul luse 			CU_ASSERT(memcmp(buf, compare_buf, params.logical_block_size) == 0);
932cf467578Spaul luse 			break;
933cf467578Spaul luse 		default:
934cf467578Spaul luse 			CU_ASSERT(spdk_mem_all_zero(buf, params.logical_block_size));
935cf467578Spaul luse 			break;
936cf467578Spaul luse 		}
937cf467578Spaul luse 	}
938cf467578Spaul luse 
939cf467578Spaul luse 	g_reduce_errno = -1;
940cf467578Spaul luse 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
941cf467578Spaul luse 	CU_ASSERT(g_reduce_errno == 0);
942cf467578Spaul luse 
943383b1173SJim Harris 	g_vol = NULL;
944383b1173SJim Harris 	g_reduce_errno = -1;
945383b1173SJim Harris 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
946383b1173SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
947383b1173SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
948383b1173SJim Harris 	CU_ASSERT(g_vol->params.vol_size == params.vol_size);
949383b1173SJim Harris 	CU_ASSERT(g_vol->params.chunk_size == params.chunk_size);
950383b1173SJim Harris 	CU_ASSERT(g_vol->params.backing_io_unit_size == params.backing_io_unit_size);
951383b1173SJim Harris 
952383b1173SJim Harris 	g_reduce_errno = -1;
95363ba545fSJim Harris 
95463ba545fSJim Harris 	/* Write 0xBB to 2 512-byte logical blocks, starting at LBA 37.
95563ba545fSJim Harris 	 * This is writing into the second chunk of the volume.  This also
95663ba545fSJim Harris 	 * enables implicitly checking that we reloaded the bit arrays
95763ba545fSJim Harris 	 * correctly - making sure we don't use the first chunk map again
95863ba545fSJim Harris 	 * for this new write - the first chunk map was already used by the
95963ba545fSJim Harris 	 * write from before we unloaded and reloaded.
96063ba545fSJim Harris 	 */
96163ba545fSJim Harris 	memset(buf, 0xBB, 2 * params.logical_block_size);
96263ba545fSJim Harris 	iov.iov_base = buf;
96363ba545fSJim Harris 	iov.iov_len = 2 * params.logical_block_size;
96463ba545fSJim Harris 	g_reduce_errno = -1;
96563ba545fSJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 37, 2, write_cb, NULL);
96663ba545fSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
96763ba545fSJim Harris 
96863ba545fSJim Harris 	for (i = 0; i < 2 * params.chunk_size / params.logical_block_size; i++) {
96963ba545fSJim Harris 		memset(buf, 0xFF, params.logical_block_size);
97063ba545fSJim Harris 		iov.iov_base = buf;
97163ba545fSJim Harris 		iov.iov_len = params.logical_block_size;
97263ba545fSJim Harris 		g_reduce_errno = -1;
97363ba545fSJim Harris 		spdk_reduce_vol_readv(g_vol, &iov, 1, i, 1, read_cb, NULL);
97463ba545fSJim Harris 		CU_ASSERT(g_reduce_errno == 0);
97563ba545fSJim Harris 
97663ba545fSJim Harris 		switch (i) {
97763ba545fSJim Harris 		case 2:
97863ba545fSJim Harris 		case 3:
979cf467578Spaul luse 			memset(compare_buf, 0xCC, sizeof(compare_buf));
98063ba545fSJim Harris 			CU_ASSERT(memcmp(buf, compare_buf, params.logical_block_size) == 0);
98163ba545fSJim Harris 			break;
98263ba545fSJim Harris 		case 37:
98363ba545fSJim Harris 		case 38:
98463ba545fSJim Harris 			memset(compare_buf, 0xBB, sizeof(compare_buf));
98563ba545fSJim Harris 			CU_ASSERT(memcmp(buf, compare_buf, params.logical_block_size) == 0);
98663ba545fSJim Harris 			break;
98763ba545fSJim Harris 		default:
98863ba545fSJim Harris 			CU_ASSERT(spdk_mem_all_zero(buf, params.logical_block_size));
98963ba545fSJim Harris 			break;
99063ba545fSJim Harris 		}
99163ba545fSJim Harris 	}
99263ba545fSJim Harris 
99363ba545fSJim Harris 	g_reduce_errno = -1;
994383b1173SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
995383b1173SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
996383b1173SJim Harris 
997383b1173SJim Harris 	persistent_pm_buf_destroy();
998383b1173SJim Harris 	backing_dev_destroy(&backing_dev);
999383b1173SJim Harris }
1000383b1173SJim Harris 
1001212770dbSJim Harris static void
1002212770dbSJim Harris read_write(void)
1003212770dbSJim Harris {
1004212770dbSJim Harris 	_read_write(512);
1005212770dbSJim Harris 	_read_write(4096);
1006212770dbSJim Harris }
1007212770dbSJim Harris 
1008c0f38051SJim Harris static void
10094328a67eSSeth Howell _readv_writev(uint32_t backing_blocklen)
10104328a67eSSeth Howell {
10114328a67eSSeth Howell 	struct spdk_reduce_vol_params params = {};
10124328a67eSSeth Howell 	struct spdk_reduce_backing_dev backing_dev = {};
10134328a67eSSeth Howell 	struct iovec iov[REDUCE_MAX_IOVECS + 1];
10144328a67eSSeth Howell 
10154328a67eSSeth Howell 	params.chunk_size = 16 * 1024;
10164328a67eSSeth Howell 	params.backing_io_unit_size = 4096;
10174328a67eSSeth Howell 	params.logical_block_size = 512;
10184328a67eSSeth Howell 	spdk_uuid_generate(&params.uuid);
10194328a67eSSeth Howell 
10204328a67eSSeth Howell 	backing_dev_init(&backing_dev, &params, backing_blocklen);
10214328a67eSSeth Howell 
10224328a67eSSeth Howell 	g_vol = NULL;
10234328a67eSSeth Howell 	g_reduce_errno = -1;
10244328a67eSSeth Howell 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
10254328a67eSSeth Howell 	CU_ASSERT(g_reduce_errno == 0);
10264328a67eSSeth Howell 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
10274328a67eSSeth Howell 
10284328a67eSSeth Howell 	g_reduce_errno = -1;
10294328a67eSSeth Howell 	spdk_reduce_vol_writev(g_vol, iov, REDUCE_MAX_IOVECS + 1, 2, REDUCE_MAX_IOVECS + 1, write_cb, NULL);
10304328a67eSSeth Howell 	CU_ASSERT(g_reduce_errno == -EINVAL);
10314328a67eSSeth Howell 
10324328a67eSSeth Howell 	g_reduce_errno = -1;
10334328a67eSSeth Howell 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
10344328a67eSSeth Howell 	CU_ASSERT(g_reduce_errno == 0);
10354328a67eSSeth Howell 
10364328a67eSSeth Howell 	persistent_pm_buf_destroy();
10374328a67eSSeth Howell 	backing_dev_destroy(&backing_dev);
10384328a67eSSeth Howell }
10394328a67eSSeth Howell 
10404328a67eSSeth Howell static void
10414328a67eSSeth Howell readv_writev(void)
10424328a67eSSeth Howell {
10434328a67eSSeth Howell 	_readv_writev(512);
10444328a67eSSeth Howell 	_readv_writev(4096);
10454328a67eSSeth Howell }
10464328a67eSSeth Howell 
1047*412fced1SYalong Wang /* 1.write offset  0KB, length 32KB, with 0xAA
1048*412fced1SYalong Wang  * 2.unmap offset  8KB, length 24KB, fail with -EINVAL, verify
1049*412fced1SYalong Wang  * 3.unmap offset  8KB, length  8KB
1050*412fced1SYalong Wang  * 4.unmap offset 16KB, length 16KB
1051*412fced1SYalong Wang  * 5.two fullchunk read verify
1052*412fced1SYalong Wang  */
1053*412fced1SYalong Wang static void
1054*412fced1SYalong Wang write_unmap_verify(void)
1055*412fced1SYalong Wang {
1056*412fced1SYalong Wang 	uint32_t backing_blocklen = 512;
1057*412fced1SYalong Wang 	uint64_t blocks_per_chunk;
1058*412fced1SYalong Wang 
1059*412fced1SYalong Wang 	struct spdk_reduce_vol_params params = {};
1060*412fced1SYalong Wang 	struct spdk_reduce_backing_dev backing_dev = {};
1061*412fced1SYalong Wang 	struct iovec iov;
1062*412fced1SYalong Wang 	char buf[16 * 1024]; /* chunk size */
1063*412fced1SYalong Wang 	char compare_buf[32 * 1024];
1064*412fced1SYalong Wang 
1065*412fced1SYalong Wang 	params.chunk_size = 16 * 1024;
1066*412fced1SYalong Wang 	params.backing_io_unit_size = 4096;
1067*412fced1SYalong Wang 	params.logical_block_size = 512;
1068*412fced1SYalong Wang 	spdk_uuid_generate(&params.uuid);
1069*412fced1SYalong Wang 
1070*412fced1SYalong Wang 	backing_dev_init(&backing_dev, &params, backing_blocklen);
1071*412fced1SYalong Wang 
1072*412fced1SYalong Wang 	blocks_per_chunk = params.chunk_size / params.logical_block_size;
1073*412fced1SYalong Wang 	g_vol = NULL;
1074*412fced1SYalong Wang 	g_reduce_errno = -1;
1075*412fced1SYalong Wang 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
1076*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1077*412fced1SYalong Wang 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
1078*412fced1SYalong Wang 
1079*412fced1SYalong Wang 	/* 1.write offset 0KB, length 32KB, with 0xAA */
1080*412fced1SYalong Wang 	iov.iov_base = buf;
1081*412fced1SYalong Wang 	iov.iov_len = sizeof(buf);
1082*412fced1SYalong Wang 	memset(buf, 0xAA, sizeof(buf));
1083*412fced1SYalong Wang 	g_reduce_errno = -1;
1084*412fced1SYalong Wang 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, blocks_per_chunk, write_cb, NULL);
1085*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1086*412fced1SYalong Wang 	g_reduce_errno = -1;
1087*412fced1SYalong Wang 	spdk_reduce_vol_writev(g_vol, &iov, 1, blocks_per_chunk, blocks_per_chunk, write_cb, NULL);
1088*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1089*412fced1SYalong Wang 
1090*412fced1SYalong Wang 	memset(compare_buf, 0xAA, sizeof(compare_buf));
1091*412fced1SYalong Wang 
1092*412fced1SYalong Wang 	memset(buf, 0xFF, sizeof(buf));
1093*412fced1SYalong Wang 	g_reduce_errno = -1;
1094*412fced1SYalong Wang 	spdk_reduce_vol_readv(g_vol, &iov, 1, 0, blocks_per_chunk, read_cb, NULL);
1095*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1096*412fced1SYalong Wang 	CU_ASSERT(memcmp(buf, compare_buf, sizeof(buf)) == 0);
1097*412fced1SYalong Wang 
1098*412fced1SYalong Wang 	memset(buf, 0xFF, sizeof(buf));
1099*412fced1SYalong Wang 	g_reduce_errno = -1;
1100*412fced1SYalong Wang 	spdk_reduce_vol_readv(g_vol, &iov, 1, blocks_per_chunk, blocks_per_chunk, read_cb, NULL);
1101*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1102*412fced1SYalong Wang 	CU_ASSERT(memcmp(buf, compare_buf + sizeof(buf), sizeof(buf)) == 0);
1103*412fced1SYalong Wang 
1104*412fced1SYalong Wang 	/* 2.unmap offset 8KB, length 24KB, fail with -EINVAL */
1105*412fced1SYalong Wang 	g_reduce_errno = 0;
1106*412fced1SYalong Wang 	spdk_reduce_vol_unmap(g_vol, 8 * 1024 / params.logical_block_size,
1107*412fced1SYalong Wang 			      24 * 1024 / params.logical_block_size, unmap_cb, NULL);
1108*412fced1SYalong Wang 	spdk_thread_poll(g_thread, 0, 0);
1109*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == -EINVAL);
1110*412fced1SYalong Wang 
1111*412fced1SYalong Wang 	memset(buf, 0xFF, sizeof(buf));
1112*412fced1SYalong Wang 	g_reduce_errno = -1;
1113*412fced1SYalong Wang 	spdk_reduce_vol_readv(g_vol, &iov, 1, 0, blocks_per_chunk, read_cb, NULL);
1114*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1115*412fced1SYalong Wang 	CU_ASSERT(memcmp(buf, compare_buf, sizeof(buf)) == 0);
1116*412fced1SYalong Wang 
1117*412fced1SYalong Wang 	memset(buf, 0xFF, sizeof(buf));
1118*412fced1SYalong Wang 	g_reduce_errno = -1;
1119*412fced1SYalong Wang 	spdk_reduce_vol_readv(g_vol, &iov, 1, blocks_per_chunk, blocks_per_chunk, read_cb, NULL);
1120*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1121*412fced1SYalong Wang 	CU_ASSERT(memcmp(buf, compare_buf + sizeof(buf), sizeof(buf)) == 0);
1122*412fced1SYalong Wang 
1123*412fced1SYalong Wang 	/* 3.unmap offset  8KB, length  8KB */
1124*412fced1SYalong Wang 	g_reduce_errno = -1;
1125*412fced1SYalong Wang 	spdk_reduce_vol_unmap(g_vol, 8 * 1024 / params.logical_block_size,
1126*412fced1SYalong Wang 			      8 * 1024 / params.logical_block_size, unmap_cb, NULL);
1127*412fced1SYalong Wang 	spdk_thread_poll(g_thread, 0, 0);
1128*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1129*412fced1SYalong Wang 	memset(compare_buf + 8 * 1024, 0x00, 8 * 1024);
1130*412fced1SYalong Wang 
1131*412fced1SYalong Wang 	/* 4.unmap offset 16KB, length 16KB */
1132*412fced1SYalong Wang 	g_reduce_errno = -1;
1133*412fced1SYalong Wang 	spdk_reduce_vol_unmap(g_vol, 16 * 1024 / params.logical_block_size,
1134*412fced1SYalong Wang 			      16 * 1024 / params.logical_block_size, unmap_cb, NULL);
1135*412fced1SYalong Wang 	spdk_thread_poll(g_thread, 0, 0);
1136*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1137*412fced1SYalong Wang 	memset(compare_buf + 16 * 1024, 0x00, 16 * 1024);
1138*412fced1SYalong Wang 
1139*412fced1SYalong Wang 	/* 5.two fullchunk read verify */
1140*412fced1SYalong Wang 	memset(buf, 0xFF, sizeof(buf));
1141*412fced1SYalong Wang 	g_reduce_errno = -1;
1142*412fced1SYalong Wang 	spdk_reduce_vol_readv(g_vol, &iov, 1, 0, blocks_per_chunk, read_cb, NULL);
1143*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1144*412fced1SYalong Wang 	CU_ASSERT(memcmp(buf, compare_buf, sizeof(buf)) == 0);
1145*412fced1SYalong Wang 
1146*412fced1SYalong Wang 	memset(buf, 0xFF, sizeof(buf));
1147*412fced1SYalong Wang 	g_reduce_errno = -1;
1148*412fced1SYalong Wang 	spdk_reduce_vol_readv(g_vol, &iov, 1, blocks_per_chunk, blocks_per_chunk, read_cb, NULL);
1149*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1150*412fced1SYalong Wang 	CU_ASSERT(memcmp(buf, compare_buf + 16 * 1024, sizeof(buf)) == 0);
1151*412fced1SYalong Wang 	/* done */
1152*412fced1SYalong Wang 
1153*412fced1SYalong Wang 	g_reduce_errno = -1;
1154*412fced1SYalong Wang 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
1155*412fced1SYalong Wang 	CU_ASSERT(g_reduce_errno == 0);
1156*412fced1SYalong Wang 
1157*412fced1SYalong Wang 	persistent_pm_buf_destroy();
1158*412fced1SYalong Wang 	backing_dev_destroy(&backing_dev);
1159*412fced1SYalong Wang }
1160*412fced1SYalong Wang 
11614328a67eSSeth Howell static void
1162c0f38051SJim Harris destroy_cb(void *ctx, int reduce_errno)
1163c0f38051SJim Harris {
1164c0f38051SJim Harris 	g_reduce_errno = reduce_errno;
1165c0f38051SJim Harris }
1166c0f38051SJim Harris 
1167c0f38051SJim Harris static void
1168c0f38051SJim Harris destroy(void)
1169c0f38051SJim Harris {
1170c0f38051SJim Harris 	struct spdk_reduce_vol_params params = {};
1171c0f38051SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
1172c0f38051SJim Harris 
1173c0f38051SJim Harris 	params.chunk_size = 16 * 1024;
1174c0f38051SJim Harris 	params.backing_io_unit_size = 512;
1175c0f38051SJim Harris 	params.logical_block_size = 512;
1176c0f38051SJim Harris 	spdk_uuid_generate(&params.uuid);
1177c0f38051SJim Harris 
1178c0f38051SJim Harris 	backing_dev_init(&backing_dev, &params, 512);
1179c0f38051SJim Harris 
1180c0f38051SJim Harris 	g_vol = NULL;
1181c0f38051SJim Harris 	g_reduce_errno = -1;
1182c0f38051SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
1183c0f38051SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
1184c0f38051SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
1185c0f38051SJim Harris 
1186c0f38051SJim Harris 	g_reduce_errno = -1;
1187c0f38051SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
1188c0f38051SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
1189c0f38051SJim Harris 
1190c0f38051SJim Harris 	g_vol = NULL;
1191c0f38051SJim Harris 	g_reduce_errno = -1;
1192c0f38051SJim Harris 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
1193c0f38051SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
1194c0f38051SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
1195c0f38051SJim Harris 
1196c0f38051SJim Harris 	g_reduce_errno = -1;
1197c0f38051SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
1198c0f38051SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
1199c0f38051SJim Harris 
1200c0f38051SJim Harris 	g_reduce_errno = -1;
1201c0f38051SJim Harris 	MOCK_CLEAR(spdk_malloc);
1202c0f38051SJim Harris 	MOCK_CLEAR(spdk_zmalloc);
1203c0f38051SJim Harris 	spdk_reduce_vol_destroy(&backing_dev, destroy_cb, NULL);
1204c0f38051SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
1205c0f38051SJim Harris 
1206c0f38051SJim Harris 	g_reduce_errno = 0;
1207c0f38051SJim Harris 	spdk_reduce_vol_load(&backing_dev, load_cb, NULL);
1208c0f38051SJim Harris 	CU_ASSERT(g_reduce_errno == -EILSEQ);
1209c0f38051SJim Harris 
1210c0f38051SJim Harris 	backing_dev_destroy(&backing_dev);
1211c0f38051SJim Harris }
1212c0f38051SJim Harris 
121394d353eaSJim Harris /* This test primarily checks that the reduce unit test infrastructure for asynchronous
121494d353eaSJim Harris  * backing device I/O operations is working correctly.
121594d353eaSJim Harris  */
121694d353eaSJim Harris static void
121794d353eaSJim Harris defer_bdev_io(void)
121894d353eaSJim Harris {
121994d353eaSJim Harris 	struct spdk_reduce_vol_params params = {};
122094d353eaSJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
122194d353eaSJim Harris 	const uint32_t logical_block_size = 512;
122294d353eaSJim Harris 	struct iovec iov;
122394d353eaSJim Harris 	char buf[logical_block_size];
122494d353eaSJim Harris 	char compare_buf[logical_block_size];
122594d353eaSJim Harris 
122694d353eaSJim Harris 	params.chunk_size = 16 * 1024;
122794d353eaSJim Harris 	params.backing_io_unit_size = 4096;
122894d353eaSJim Harris 	params.logical_block_size = logical_block_size;
122994d353eaSJim Harris 	spdk_uuid_generate(&params.uuid);
123094d353eaSJim Harris 
123194d353eaSJim Harris 	backing_dev_init(&backing_dev, &params, 512);
123294d353eaSJim Harris 
123394d353eaSJim Harris 	g_vol = NULL;
123494d353eaSJim Harris 	g_reduce_errno = -1;
123594d353eaSJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
123694d353eaSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
123794d353eaSJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
123894d353eaSJim Harris 
123994d353eaSJim Harris 	/* Write 0xAA to 1 512-byte logical block. */
124094d353eaSJim Harris 	memset(buf, 0xAA, params.logical_block_size);
124194d353eaSJim Harris 	iov.iov_base = buf;
124294d353eaSJim Harris 	iov.iov_len = params.logical_block_size;
124394d353eaSJim Harris 	g_reduce_errno = -100;
124494d353eaSJim Harris 	g_defer_bdev_io = true;
124594d353eaSJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, 1, write_cb, NULL);
124694d353eaSJim Harris 	/* Callback should not have executed, so this should still equal -100. */
124794d353eaSJim Harris 	CU_ASSERT(g_reduce_errno == -100);
124894d353eaSJim Harris 	CU_ASSERT(!TAILQ_EMPTY(&g_pending_bdev_io));
12491679104eSJim Harris 	/* We wrote to just 512 bytes of one chunk which was previously unallocated.  This
12501679104eSJim Harris 	 * should result in 1 pending I/O since the rest of this chunk will be zeroes and
12511679104eSJim Harris 	 * very compressible.
125294d353eaSJim Harris 	 */
12531679104eSJim Harris 	CU_ASSERT(g_pending_bdev_io_count == 1);
125494d353eaSJim Harris 
125594d353eaSJim Harris 	backing_dev_io_execute(0);
125694d353eaSJim Harris 	CU_ASSERT(TAILQ_EMPTY(&g_pending_bdev_io));
125794d353eaSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
125894d353eaSJim Harris 
125994d353eaSJim Harris 	g_defer_bdev_io = false;
126094d353eaSJim Harris 	memset(compare_buf, 0xAA, sizeof(compare_buf));
126194d353eaSJim Harris 	memset(buf, 0xFF, sizeof(buf));
126294d353eaSJim Harris 	iov.iov_base = buf;
126394d353eaSJim Harris 	iov.iov_len = params.logical_block_size;
126494d353eaSJim Harris 	g_reduce_errno = -100;
126594d353eaSJim Harris 	spdk_reduce_vol_readv(g_vol, &iov, 1, 0, 1, read_cb, NULL);
126694d353eaSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
126794d353eaSJim Harris 	CU_ASSERT(memcmp(buf, compare_buf, sizeof(buf)) == 0);
126894d353eaSJim Harris 
126994d353eaSJim Harris 	g_reduce_errno = -1;
127094d353eaSJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
127194d353eaSJim Harris 	CU_ASSERT(g_reduce_errno == 0);
127294d353eaSJim Harris 
127394d353eaSJim Harris 	persistent_pm_buf_destroy();
127494d353eaSJim Harris 	backing_dev_destroy(&backing_dev);
127594d353eaSJim Harris }
127694d353eaSJim Harris 
12774fb22601SJim Harris static void
12784fb22601SJim Harris overlapped(void)
12794fb22601SJim Harris {
12804fb22601SJim Harris 	struct spdk_reduce_vol_params params = {};
12814fb22601SJim Harris 	struct spdk_reduce_backing_dev backing_dev = {};
12824fb22601SJim Harris 	const uint32_t logical_block_size = 512;
12834fb22601SJim Harris 	struct iovec iov;
12844fb22601SJim Harris 	char buf[2 * logical_block_size];
12854fb22601SJim Harris 	char compare_buf[2 * logical_block_size];
12864fb22601SJim Harris 
12874fb22601SJim Harris 	params.chunk_size = 16 * 1024;
12884fb22601SJim Harris 	params.backing_io_unit_size = 4096;
12894fb22601SJim Harris 	params.logical_block_size = logical_block_size;
12904fb22601SJim Harris 	spdk_uuid_generate(&params.uuid);
12914fb22601SJim Harris 
12924fb22601SJim Harris 	backing_dev_init(&backing_dev, &params, 512);
12934fb22601SJim Harris 
12944fb22601SJim Harris 	g_vol = NULL;
12954fb22601SJim Harris 	g_reduce_errno = -1;
12964fb22601SJim Harris 	spdk_reduce_vol_init(&params, &backing_dev, TEST_MD_PATH, init_cb, NULL);
12974fb22601SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
12984fb22601SJim Harris 	SPDK_CU_ASSERT_FATAL(g_vol != NULL);
12994fb22601SJim Harris 
13004fb22601SJim Harris 	/* Write 0xAA to 1 512-byte logical block. */
13014fb22601SJim Harris 	memset(buf, 0xAA, logical_block_size);
13024fb22601SJim Harris 	iov.iov_base = buf;
13034fb22601SJim Harris 	iov.iov_len = logical_block_size;
13044fb22601SJim Harris 	g_reduce_errno = -100;
13054fb22601SJim Harris 	g_defer_bdev_io = true;
13064fb22601SJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 0, 1, write_cb, NULL);
13074fb22601SJim Harris 	/* Callback should not have executed, so this should still equal -100. */
13084fb22601SJim Harris 	CU_ASSERT(g_reduce_errno == -100);
13094fb22601SJim Harris 	CU_ASSERT(!TAILQ_EMPTY(&g_pending_bdev_io));
13101679104eSJim Harris 	/* We wrote to just 512 bytes of one chunk which was previously unallocated.  This
13111679104eSJim Harris 	 * should result in 1 pending I/O since the rest of this chunk will be zeroes and
13121679104eSJim Harris 	 * very compressible.
13131679104eSJim Harris 	 */
13141679104eSJim Harris 	CU_ASSERT(g_pending_bdev_io_count == 1);
13154fb22601SJim Harris 
13164fb22601SJim Harris 	/* Now do an overlapped I/O to the same chunk. */
13174fb22601SJim Harris 	spdk_reduce_vol_writev(g_vol, &iov, 1, 1, 1, write_cb, NULL);
13184fb22601SJim Harris 	/* Callback should not have executed, so this should still equal -100. */
13194fb22601SJim Harris 	CU_ASSERT(g_reduce_errno == -100);
13204fb22601SJim Harris 	CU_ASSERT(!TAILQ_EMPTY(&g_pending_bdev_io));
13214fb22601SJim Harris 	/* The second I/O overlaps with the first one.  So we should only see pending bdev_io
13224fb22601SJim Harris 	 * related to the first I/O here - the second one won't start until the first one is completed.
13234fb22601SJim Harris 	 */
13241679104eSJim Harris 	CU_ASSERT(g_pending_bdev_io_count == 1);
13254fb22601SJim Harris 
13264fb22601SJim Harris 	backing_dev_io_execute(0);
13274fb22601SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
13284fb22601SJim Harris 
13294fb22601SJim Harris 	g_defer_bdev_io = false;
13304fb22601SJim Harris 	memset(compare_buf, 0xAA, sizeof(compare_buf));
13314fb22601SJim Harris 	memset(buf, 0xFF, sizeof(buf));
13324fb22601SJim Harris 	iov.iov_base = buf;
13334fb22601SJim Harris 	iov.iov_len = 2 * logical_block_size;
13344fb22601SJim Harris 	g_reduce_errno = -100;
13354fb22601SJim Harris 	spdk_reduce_vol_readv(g_vol, &iov, 1, 0, 2, read_cb, NULL);
13364fb22601SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
13374fb22601SJim Harris 	CU_ASSERT(memcmp(buf, compare_buf, 2 * logical_block_size) == 0);
13384fb22601SJim Harris 
13394fb22601SJim Harris 	g_reduce_errno = -1;
13404fb22601SJim Harris 	spdk_reduce_vol_unload(g_vol, unload_cb, NULL);
13414fb22601SJim Harris 	CU_ASSERT(g_reduce_errno == 0);
13424fb22601SJim Harris 
13434fb22601SJim Harris 	persistent_pm_buf_destroy();
13444fb22601SJim Harris 	backing_dev_destroy(&backing_dev);
13454fb22601SJim Harris }
13464fb22601SJim Harris 
1347ac3077c3SJim Harris #define BUFSIZE 4096
1348ac3077c3SJim Harris 
1349ac3077c3SJim Harris static void
1350ac3077c3SJim Harris compress_algorithm(void)
1351ac3077c3SJim Harris {
1352ac3077c3SJim Harris 	uint8_t original_data[BUFSIZE];
1353ac3077c3SJim Harris 	uint8_t compressed_data[BUFSIZE];
1354ac3077c3SJim Harris 	uint8_t decompressed_data[BUFSIZE];
1355c1bab69cSJim Harris 	uint32_t compressed_len, decompressed_len;
1356ac3077c3SJim Harris 	int rc;
1357ac3077c3SJim Harris 
1358c1bab69cSJim Harris 	ut_build_data_buffer(original_data, BUFSIZE, 0xAA, BUFSIZE);
1359ac3077c3SJim Harris 	compressed_len = sizeof(compressed_data);
1360ac3077c3SJim Harris 	rc = ut_compress(compressed_data, &compressed_len, original_data, UINT8_MAX);
1361ac3077c3SJim Harris 	CU_ASSERT(rc == 0);
1362ac3077c3SJim Harris 	CU_ASSERT(compressed_len == 2);
1363ac3077c3SJim Harris 	CU_ASSERT(compressed_data[0] == UINT8_MAX);
1364ac3077c3SJim Harris 	CU_ASSERT(compressed_data[1] == 0xAA);
1365ac3077c3SJim Harris 
1366ac3077c3SJim Harris 	decompressed_len = sizeof(decompressed_data);
1367ac3077c3SJim Harris 	rc = ut_decompress(decompressed_data, &decompressed_len, compressed_data, compressed_len);
1368ac3077c3SJim Harris 	CU_ASSERT(rc == 0);
1369ac3077c3SJim Harris 	CU_ASSERT(decompressed_len == UINT8_MAX);
1370ac3077c3SJim Harris 	CU_ASSERT(memcmp(original_data, decompressed_data, decompressed_len) == 0);
1371ac3077c3SJim Harris 
1372ac3077c3SJim Harris 	compressed_len = sizeof(compressed_data);
1373ac3077c3SJim Harris 	rc = ut_compress(compressed_data, &compressed_len, original_data, UINT8_MAX + 1);
1374ac3077c3SJim Harris 	CU_ASSERT(rc == 0);
1375ac3077c3SJim Harris 	CU_ASSERT(compressed_len == 4);
1376ac3077c3SJim Harris 	CU_ASSERT(compressed_data[0] == UINT8_MAX);
1377ac3077c3SJim Harris 	CU_ASSERT(compressed_data[1] == 0xAA);
1378ac3077c3SJim Harris 	CU_ASSERT(compressed_data[2] == 1);
1379ac3077c3SJim Harris 	CU_ASSERT(compressed_data[3] == 0xAA);
1380ac3077c3SJim Harris 
1381ac3077c3SJim Harris 	decompressed_len = sizeof(decompressed_data);
1382ac3077c3SJim Harris 	rc = ut_decompress(decompressed_data, &decompressed_len, compressed_data, compressed_len);
1383ac3077c3SJim Harris 	CU_ASSERT(rc == 0);
1384ac3077c3SJim Harris 	CU_ASSERT(decompressed_len == UINT8_MAX + 1);
1385ac3077c3SJim Harris 	CU_ASSERT(memcmp(original_data, decompressed_data, decompressed_len) == 0);
1386ac3077c3SJim Harris 
1387c1bab69cSJim Harris 	ut_build_data_buffer(original_data, BUFSIZE, 0x00, 1);
1388ac3077c3SJim Harris 	compressed_len = sizeof(compressed_data);
1389ac3077c3SJim Harris 	rc = ut_compress(compressed_data, &compressed_len, original_data, 2048);
1390ac3077c3SJim Harris 	CU_ASSERT(rc == 0);
1391ac3077c3SJim Harris 	CU_ASSERT(compressed_len == 4096);
1392ac3077c3SJim Harris 	CU_ASSERT(compressed_data[0] == 1);
1393ac3077c3SJim Harris 	CU_ASSERT(compressed_data[1] == 0);
1394ac3077c3SJim Harris 	CU_ASSERT(compressed_data[4094] == 1);
1395ac3077c3SJim Harris 	CU_ASSERT(compressed_data[4095] == 0xFF);
1396ac3077c3SJim Harris 
1397ac3077c3SJim Harris 	decompressed_len = sizeof(decompressed_data);
1398ac3077c3SJim Harris 	rc = ut_decompress(decompressed_data, &decompressed_len, compressed_data, compressed_len);
1399ac3077c3SJim Harris 	CU_ASSERT(rc == 0);
1400ac3077c3SJim Harris 	CU_ASSERT(decompressed_len == 2048);
1401ac3077c3SJim Harris 	CU_ASSERT(memcmp(original_data, decompressed_data, decompressed_len) == 0);
1402ac3077c3SJim Harris 
1403ac3077c3SJim Harris 	compressed_len = sizeof(compressed_data);
1404ac3077c3SJim Harris 	rc = ut_compress(compressed_data, &compressed_len, original_data, 2049);
1405ac3077c3SJim Harris 	CU_ASSERT(rc == -ENOSPC);
1406ac3077c3SJim Harris }
1407ac3077c3SJim Harris 
140842f59f50SAlexey Marchuk static void
140942f59f50SAlexey Marchuk test_prepare_compress_chunk(void)
141042f59f50SAlexey Marchuk {
141142f59f50SAlexey Marchuk 	struct spdk_reduce_vol vol = {};
141242f59f50SAlexey Marchuk 	struct spdk_reduce_backing_dev backing_dev = {};
141342f59f50SAlexey Marchuk 	struct spdk_reduce_vol_request req = {};
1414619b4dbaSAlexey Marchuk 	void *buf;
1415619b4dbaSAlexey Marchuk 	char *buffer_end, *aligned_user_buffer, *unaligned_user_buffer;
141642f59f50SAlexey Marchuk 	char decomp_buffer[16 * 1024] = {};
141742f59f50SAlexey Marchuk 	char comp_buffer[16 * 1024] = {};
141842f59f50SAlexey Marchuk 	struct iovec user_iov[2] = {};
141942f59f50SAlexey Marchuk 	size_t user_buffer_iov_len = 8192;
142042f59f50SAlexey Marchuk 	size_t remainder_bytes;
142142f59f50SAlexey Marchuk 	size_t offset_bytes;
142242f59f50SAlexey Marchuk 	size_t memcmp_offset;
142342f59f50SAlexey Marchuk 	uint32_t i;
142442f59f50SAlexey Marchuk 
142542f59f50SAlexey Marchuk 	vol.params.chunk_size = 16 * 1024;
142642f59f50SAlexey Marchuk 	vol.params.backing_io_unit_size = 4096;
142742f59f50SAlexey Marchuk 	vol.params.logical_block_size = 512;
142842f59f50SAlexey Marchuk 	backing_dev_init(&backing_dev, &vol.params, 512);
142942f59f50SAlexey Marchuk 	vol.backing_dev = &backing_dev;
143042f59f50SAlexey Marchuk 	vol.logical_blocks_per_chunk = vol.params.chunk_size / vol.params.logical_block_size;
143142f59f50SAlexey Marchuk 
1432619b4dbaSAlexey Marchuk 	/* Allocate 1 extra byte to test a case when buffer crosses huge page boundary */
1433619b4dbaSAlexey Marchuk 	SPDK_CU_ASSERT_FATAL(posix_memalign(&buf, VALUE_2MB, VALUE_2MB + 1) == 0);
1434619b4dbaSAlexey Marchuk 	buffer_end = (char *)buf + VALUE_2MB + 1;
1435619b4dbaSAlexey Marchuk 	aligned_user_buffer = (char *)buf;
1436619b4dbaSAlexey Marchuk 	memset(aligned_user_buffer, 0xc, vol.params.chunk_size);
1437619b4dbaSAlexey Marchuk 	unaligned_user_buffer = buffer_end - vol.params.chunk_size;
1438619b4dbaSAlexey Marchuk 	memset(unaligned_user_buffer, 0xc, vol.params.chunk_size);
1439619b4dbaSAlexey Marchuk 
144042f59f50SAlexey Marchuk 	req.vol = &vol;
144142f59f50SAlexey Marchuk 	req.decomp_buf = decomp_buffer;
144242f59f50SAlexey Marchuk 	req.comp_buf = comp_buffer;
144342f59f50SAlexey Marchuk 	req.iov = user_iov;
144442f59f50SAlexey Marchuk 	req.iovcnt = 2;
144542f59f50SAlexey Marchuk 	req.offset = 0;
144642f59f50SAlexey Marchuk 
144742f59f50SAlexey Marchuk 	/* Part 1 - backing dev supports sgl_in */
144842f59f50SAlexey Marchuk 	/* Test 1 - user's buffers length equals to chunk_size */
144942f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1450619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
145142f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
145242f59f50SAlexey Marchuk 	}
145342f59f50SAlexey Marchuk 
145442f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
145542f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 2);
145642f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
145742f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_base == req.iov[i].iov_base);
145842f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_len == req.iov[i].iov_len);
145942f59f50SAlexey Marchuk 	}
146042f59f50SAlexey Marchuk 
146142f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
146242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 2);
146342f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
146442f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_base == req.iov[i].iov_base);
146542f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_len == req.iov[i].iov_len);
146642f59f50SAlexey Marchuk 	}
146742f59f50SAlexey Marchuk 
146842f59f50SAlexey Marchuk 	/* Test 2 - user's buffer less than chunk_size, without offset */
146942f59f50SAlexey Marchuk 	user_buffer_iov_len = 4096;
147042f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - user_buffer_iov_len * 2;
147142f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1472619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
147342f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
147442f59f50SAlexey Marchuk 	}
147542f59f50SAlexey Marchuk 
147642f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
147742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 3);
147842f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
147942f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_base == req.iov[i].iov_base);
148042f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_len == req.iov[i].iov_len);
148142f59f50SAlexey Marchuk 	}
148242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[i].iov_base == req.decomp_buf + user_buffer_iov_len * 2);
148342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[i].iov_len == remainder_bytes);
148442f59f50SAlexey Marchuk 
148542f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
148642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 3);
148742f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
148842f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_base == req.iov[i].iov_base);
148942f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_len == req.iov[i].iov_len);
149042f59f50SAlexey Marchuk 	}
149142f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[i].iov_base == g_zero_buf + user_buffer_iov_len * 2);
149242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[i].iov_len == remainder_bytes);
149342f59f50SAlexey Marchuk 
149442f59f50SAlexey Marchuk 	/* Test 3 - user's buffer less than chunk_size, non zero offset */
149542f59f50SAlexey Marchuk 	user_buffer_iov_len = 4096;
149642f59f50SAlexey Marchuk 	req.offset = 3;
149742f59f50SAlexey Marchuk 	offset_bytes = req.offset * vol.params.logical_block_size;
149842f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - offset_bytes - user_buffer_iov_len * 2;
149942f59f50SAlexey Marchuk 
150042f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
150142f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 4);
150242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
150342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == offset_bytes);
150442f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
150542f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i + 1].iov_base == req.iov[i].iov_base);
150642f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i + 1].iov_len == req.iov[i].iov_len);
150742f59f50SAlexey Marchuk 	}
150842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[3].iov_base == req.decomp_buf + offset_bytes + user_buffer_iov_len * 2);
150942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[3].iov_len == remainder_bytes);
151042f59f50SAlexey Marchuk 
151142f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
151242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 4);
151342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == g_zero_buf);
151442f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == offset_bytes);
151542f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
151642f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i + 1].iov_base == req.iov[i].iov_base);
151742f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i + 1].iov_len == req.iov[i].iov_len);
151842f59f50SAlexey Marchuk 	}
151942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[3].iov_base == g_zero_buf + offset_bytes + user_buffer_iov_len * 2);
152042f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[3].iov_len == remainder_bytes);
152142f59f50SAlexey Marchuk 
152242f59f50SAlexey Marchuk 	/* Part 2 - backing dev doesn't support sgl_in */
152342f59f50SAlexey Marchuk 	/* Test 1 - user's buffers length equals to chunk_size
152442f59f50SAlexey Marchuk 	 * user's buffers are copied */
152542f59f50SAlexey Marchuk 	vol.backing_dev->sgl_in = false;
152642f59f50SAlexey Marchuk 	req.offset = 0;
152742f59f50SAlexey Marchuk 	user_buffer_iov_len = 8192;
152842f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1529619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
153042f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
153142f59f50SAlexey Marchuk 		memset(req.iov[i].iov_base, 0xb + i, req.iov[i].iov_len);
153242f59f50SAlexey Marchuk 	}
153342f59f50SAlexey Marchuk 
153442f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
153542f59f50SAlexey Marchuk 
153642f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
153742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
153842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
153942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
154042f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base, req.iov[0].iov_base, req.iov[0].iov_len) == 0);
154142f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + req.iov[0].iov_len, req.iov[1].iov_base,
154242f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
154342f59f50SAlexey Marchuk 
154442f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
154542f59f50SAlexey Marchuk 
154642f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
154742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
154842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
154942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
155042f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base, req.iov[0].iov_base, req.iov[0].iov_len) == 0);
155142f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + req.iov[0].iov_len, req.iov[1].iov_base,
155242f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
155342f59f50SAlexey Marchuk 
1554619b4dbaSAlexey Marchuk 	/* Test 2 - single user's buffer length equals to chunk_size, buffer is not aligned
1555619b4dbaSAlexey Marchuk 	* User's buffer is copied */
1556619b4dbaSAlexey Marchuk 	req.iov[0].iov_base = unaligned_user_buffer;
1557619b4dbaSAlexey Marchuk 	req.iov[0].iov_len = vol.params.chunk_size;
1558619b4dbaSAlexey Marchuk 	req.iovcnt = 1;
1559619b4dbaSAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
1560619b4dbaSAlexey Marchuk 
1561619b4dbaSAlexey Marchuk 	_prepare_compress_chunk(&req, false);
1562619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
1563619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
1564619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
1565619b4dbaSAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base, req.iov[0].iov_base,
1566619b4dbaSAlexey Marchuk 			 req.iov[0].iov_len) == 0);
1567619b4dbaSAlexey Marchuk 
1568619b4dbaSAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
1569619b4dbaSAlexey Marchuk 
1570619b4dbaSAlexey Marchuk 	_prepare_compress_chunk(&req, true);
1571619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
1572619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
1573619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
1574619b4dbaSAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base, req.iov[0].iov_base,
1575619b4dbaSAlexey Marchuk 			 req.iov[0].iov_len) == 0);
1576619b4dbaSAlexey Marchuk 
1577619b4dbaSAlexey Marchuk 	/* Test 3 - single user's buffer length equals to chunk_size
157842f59f50SAlexey Marchuk 	 * User's buffer is not copied */
1579619b4dbaSAlexey Marchuk 	req.iov[0].iov_base = aligned_user_buffer;
158042f59f50SAlexey Marchuk 	req.iov[0].iov_len = vol.params.chunk_size;
158142f59f50SAlexey Marchuk 	req.iovcnt = 1;
158242f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
158342f59f50SAlexey Marchuk 
158442f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
158542f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
158642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.iov[0].iov_base);
158742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
158842f59f50SAlexey Marchuk 
158942f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
159042f59f50SAlexey Marchuk 
159142f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
159242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
159342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.iov[0].iov_base);
159442f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
159542f59f50SAlexey Marchuk 
1596619b4dbaSAlexey Marchuk 	/* Test 4 - user's buffer less than chunk_size, without offset
159742f59f50SAlexey Marchuk 	 * User's buffers are copied */
159842f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
159942f59f50SAlexey Marchuk 	user_buffer_iov_len = 4096;
160042f59f50SAlexey Marchuk 	req.iovcnt = 2;
160142f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - user_buffer_iov_len * 2;
160242f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1603619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
160442f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
160542f59f50SAlexey Marchuk 	}
160642f59f50SAlexey Marchuk 
160742f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
160842f59f50SAlexey Marchuk 
160942f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
161042f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
161142f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
161242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
161342f59f50SAlexey Marchuk 	memcmp_offset = 0;
161442f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[0].iov_base,
161542f59f50SAlexey Marchuk 			 req.iov[0].iov_len) == 0);
161642f59f50SAlexey Marchuk 	memcmp_offset += req.iov[0].iov_len;
161742f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[1].iov_base,
161842f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
161942f59f50SAlexey Marchuk 	memcmp_offset += req.iov[0].iov_len;
162042f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.decomp_buf + memcmp_offset,
162142f59f50SAlexey Marchuk 			 remainder_bytes) == 0);
162242f59f50SAlexey Marchuk 
162342f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
162442f59f50SAlexey Marchuk 
162542f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
162642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
162742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
162842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
162942f59f50SAlexey Marchuk 	memcmp_offset = 0;
163042f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[0].iov_base,
163142f59f50SAlexey Marchuk 			 req.iov[0].iov_len) == 0);
163242f59f50SAlexey Marchuk 	memcmp_offset += req.iov[0].iov_len;
163342f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[1].iov_base,
163442f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
163542f59f50SAlexey Marchuk 	memcmp_offset += req.iov[0].iov_len;
163642f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, g_zero_buf + memcmp_offset,
163742f59f50SAlexey Marchuk 			 remainder_bytes) == 0);
163842f59f50SAlexey Marchuk 
1639619b4dbaSAlexey Marchuk 	/* Test 5 - user's buffer less than chunk_size, non zero offset
164042f59f50SAlexey Marchuk 	 * user's buffers are copied */
164142f59f50SAlexey Marchuk 	req.offset = 3;
164242f59f50SAlexey Marchuk 	offset_bytes = req.offset * vol.params.logical_block_size;
164342f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - offset_bytes - user_buffer_iov_len * 2;
164442f59f50SAlexey Marchuk 
164542f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
164642f59f50SAlexey Marchuk 
164742f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
164842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
164942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
165042f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
165142f59f50SAlexey Marchuk 	memcmp_offset = 0;
165242f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.decomp_buf, offset_bytes) == 0);
165342f59f50SAlexey Marchuk 	memcmp_offset += offset_bytes;
165442f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[0].iov_base,
165542f59f50SAlexey Marchuk 			 req.iov[0].iov_len) == 0);
165642f59f50SAlexey Marchuk 	memcmp_offset += req.iov[0].iov_len;
165742f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[1].iov_base,
165842f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
165942f59f50SAlexey Marchuk 	memcmp_offset += req.iov[1].iov_len;
166042f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.decomp_buf + memcmp_offset,
166142f59f50SAlexey Marchuk 			 remainder_bytes) == 0);
166242f59f50SAlexey Marchuk 
166342f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
166442f59f50SAlexey Marchuk 
166542f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, true);
166642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
166742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
166842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
166942f59f50SAlexey Marchuk 	memcmp_offset = 0;
167042f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, g_zero_buf, offset_bytes) == 0);
167142f59f50SAlexey Marchuk 	memcmp_offset += offset_bytes;
167242f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[0].iov_base,
167342f59f50SAlexey Marchuk 			 req.iov[0].iov_len) == 0);
167442f59f50SAlexey Marchuk 	memcmp_offset += req.iov[0].iov_len;
167542f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, req.iov[1].iov_base,
167642f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
167742f59f50SAlexey Marchuk 	memcmp_offset += req.iov[1].iov_len;
167842f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + memcmp_offset, g_zero_buf + memcmp_offset,
167942f59f50SAlexey Marchuk 			 remainder_bytes) == 0);
16807e55f977Spaul luse 	backing_dev_destroy(&backing_dev);
1681619b4dbaSAlexey Marchuk 	free(buf);
168242f59f50SAlexey Marchuk }
168342f59f50SAlexey Marchuk 
16848dd1cd21SBen Walker static void
16858dd1cd21SBen Walker _reduce_vol_op_complete(void *ctx, int reduce_errno)
168642f59f50SAlexey Marchuk {
168742f59f50SAlexey Marchuk 	g_reduce_errno = reduce_errno;
168842f59f50SAlexey Marchuk }
168942f59f50SAlexey Marchuk 
169042f59f50SAlexey Marchuk static void
169142f59f50SAlexey Marchuk dummy_backing_dev_decompress(struct spdk_reduce_backing_dev *backing_dev,
169242f59f50SAlexey Marchuk 			     struct iovec *src_iov, int src_iovcnt,
169342f59f50SAlexey Marchuk 			     struct iovec *dst_iov, int dst_iovcnt,
169442f59f50SAlexey Marchuk 			     struct spdk_reduce_vol_cb_args *args)
169542f59f50SAlexey Marchuk {
1696bb5083a8Spaul luse 	args->output_size = g_decompressed_len;
1697bb5083a8Spaul luse 	args->cb_fn(args->cb_arg, 0);
169842f59f50SAlexey Marchuk }
16998dd1cd21SBen Walker static void
17008dd1cd21SBen Walker test_reduce_decompress_chunk(void)
170142f59f50SAlexey Marchuk {
170242f59f50SAlexey Marchuk 	struct spdk_reduce_vol vol = {};
170342f59f50SAlexey Marchuk 	struct spdk_reduce_backing_dev backing_dev = {};
170442f59f50SAlexey Marchuk 	struct spdk_reduce_vol_request req = {};
1705619b4dbaSAlexey Marchuk 	void *buf;
1706619b4dbaSAlexey Marchuk 	char *buffer_end, *aligned_user_buffer, *unaligned_user_buffer;
170742f59f50SAlexey Marchuk 	char decomp_buffer[16 * 1024] = {};
170842f59f50SAlexey Marchuk 	char comp_buffer[16 * 1024] = {};
170942f59f50SAlexey Marchuk 	struct iovec user_iov[2] = {};
171042f59f50SAlexey Marchuk 	struct iovec comp_buf_iov = {};
171142f59f50SAlexey Marchuk 	struct spdk_reduce_chunk_map chunk = {};
171242f59f50SAlexey Marchuk 	size_t user_buffer_iov_len = 8192;
171342f59f50SAlexey Marchuk 	size_t remainder_bytes;
171442f59f50SAlexey Marchuk 	size_t offset_bytes;
171542f59f50SAlexey Marchuk 	uint32_t i;
171642f59f50SAlexey Marchuk 
171742f59f50SAlexey Marchuk 	vol.params.chunk_size = 16 * 1024;
171842f59f50SAlexey Marchuk 	vol.params.backing_io_unit_size = 4096;
171942f59f50SAlexey Marchuk 	vol.params.logical_block_size = 512;
172042f59f50SAlexey Marchuk 	backing_dev_init(&backing_dev, &vol.params, 512);
172142f59f50SAlexey Marchuk 	backing_dev.decompress = dummy_backing_dev_decompress;
172242f59f50SAlexey Marchuk 	vol.backing_dev = &backing_dev;
172342f59f50SAlexey Marchuk 	vol.logical_blocks_per_chunk = vol.params.chunk_size / vol.params.logical_block_size;
17246881fb40SYalong Wang 	RB_INIT(&vol.executing_requests);
172542f59f50SAlexey Marchuk 	TAILQ_INIT(&vol.queued_requests);
172642f59f50SAlexey Marchuk 	TAILQ_INIT(&vol.free_requests);
172742f59f50SAlexey Marchuk 
1728619b4dbaSAlexey Marchuk 	/* Allocate 1 extra byte to test a case when buffer crosses huge page boundary */
1729619b4dbaSAlexey Marchuk 	SPDK_CU_ASSERT_FATAL(posix_memalign(&buf, VALUE_2MB, VALUE_2MB + 1) == 0);
1730619b4dbaSAlexey Marchuk 	buffer_end = (char *)buf + VALUE_2MB + 1;
1731619b4dbaSAlexey Marchuk 	aligned_user_buffer = (char *)buf;
1732619b4dbaSAlexey Marchuk 	unaligned_user_buffer = buffer_end - vol.params.chunk_size;
1733619b4dbaSAlexey Marchuk 
173442f59f50SAlexey Marchuk 	chunk.compressed_size = user_buffer_iov_len / 2;
173542f59f50SAlexey Marchuk 	req.chunk = &chunk;
173642f59f50SAlexey Marchuk 	req.vol = &vol;
173742f59f50SAlexey Marchuk 	req.decomp_buf = decomp_buffer;
173842f59f50SAlexey Marchuk 	req.comp_buf = comp_buffer;
173942f59f50SAlexey Marchuk 	req.comp_buf_iov = &comp_buf_iov;
174042f59f50SAlexey Marchuk 	req.iov = user_iov;
174142f59f50SAlexey Marchuk 	req.iovcnt = 2;
174242f59f50SAlexey Marchuk 	req.offset = 0;
174342f59f50SAlexey Marchuk 	req.cb_fn = _reduce_vol_op_complete;
174442f59f50SAlexey Marchuk 
174542f59f50SAlexey Marchuk 	/* Part 1 - backing dev supports sgl_out */
174642f59f50SAlexey Marchuk 	/* Test 1 - user's buffers length equals to chunk_size */
174742f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1748619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
174942f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
175042f59f50SAlexey Marchuk 		memset(req.iov[i].iov_base, 0, req.iov[i].iov_len);
175142f59f50SAlexey Marchuk 	}
17526881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
175342f59f50SAlexey Marchuk 	g_reduce_errno = -1;
175442f59f50SAlexey Marchuk 	g_decompressed_len = vol.params.chunk_size;
175542f59f50SAlexey Marchuk 
175642f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
175742f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
175842f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == false);
175942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 2);
176042f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
176142f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_base == req.iov[i].iov_base);
176242f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_len == req.iov[i].iov_len);
176342f59f50SAlexey Marchuk 	}
17646881fb40SYalong Wang 	CU_ASSERT(RB_EMPTY(&vol.executing_requests));
176542f59f50SAlexey Marchuk 	CU_ASSERT(TAILQ_FIRST(&vol.free_requests) == &req);
176642f59f50SAlexey Marchuk 
176742f59f50SAlexey Marchuk 	/* Test 2 - user's buffer less than chunk_size, without offset */
17686881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
176942f59f50SAlexey Marchuk 	g_reduce_errno = -1;
177042f59f50SAlexey Marchuk 	user_buffer_iov_len = 4096;
177142f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1772619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
177342f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
177442f59f50SAlexey Marchuk 		memset(req.iov[i].iov_base, 0, req.iov[i].iov_len);
177542f59f50SAlexey Marchuk 	}
177642f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - user_buffer_iov_len * 2;
177742f59f50SAlexey Marchuk 
177842f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
177942f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
178042f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == false);
178142f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 3);
178242f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
178342f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_base == req.iov[i].iov_base);
178442f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i].iov_len == req.iov[i].iov_len);
178542f59f50SAlexey Marchuk 	}
178642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[i].iov_base == req.decomp_buf + user_buffer_iov_len * 2);
178742f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[i].iov_len == remainder_bytes);
17886881fb40SYalong Wang 	CU_ASSERT(RB_EMPTY(&vol.executing_requests));
178942f59f50SAlexey Marchuk 	CU_ASSERT(TAILQ_FIRST(&vol.free_requests) == &req);
179042f59f50SAlexey Marchuk 
179142f59f50SAlexey Marchuk 	/* Test 3 - user's buffer less than chunk_size, non zero offset */
179242f59f50SAlexey Marchuk 	req.offset = 3;
179342f59f50SAlexey Marchuk 	offset_bytes = req.offset * vol.params.logical_block_size;
179442f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - offset_bytes - user_buffer_iov_len * 2;
17956881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
179642f59f50SAlexey Marchuk 	g_reduce_errno = -1;
179742f59f50SAlexey Marchuk 
179842f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
179942f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
180042f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == false);
180142f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 4);
180242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
180342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == offset_bytes);
180442f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
180542f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i + 1].iov_base == req.iov[i].iov_base);
180642f59f50SAlexey Marchuk 		CU_ASSERT(req.decomp_iov[i + 1].iov_len == req.iov[i].iov_len);
180742f59f50SAlexey Marchuk 	}
180842f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[3].iov_base == req.decomp_buf + offset_bytes + user_buffer_iov_len * 2);
180942f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[3].iov_len == remainder_bytes);
18106881fb40SYalong Wang 	CU_ASSERT(RB_EMPTY(&vol.executing_requests));
181142f59f50SAlexey Marchuk 	CU_ASSERT(TAILQ_FIRST(&vol.free_requests) == &req);
181242f59f50SAlexey Marchuk 
181342f59f50SAlexey Marchuk 	/* Part 2 - backing dev doesn't support sgl_out */
181442f59f50SAlexey Marchuk 	/* Test 1 - user's buffers length equals to chunk_size
181542f59f50SAlexey Marchuk 	 * user's buffers are copied */
181642f59f50SAlexey Marchuk 	vol.backing_dev->sgl_out = false;
181742f59f50SAlexey Marchuk 	req.offset = 0;
181842f59f50SAlexey Marchuk 	user_buffer_iov_len = 8192;
181942f59f50SAlexey Marchuk 
182042f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
182142f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1822619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
182342f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
182442f59f50SAlexey Marchuk 		memset(req.iov[i].iov_base, 0xb + i, req.iov[i].iov_len);
182542f59f50SAlexey Marchuk 	}
18266881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
182742f59f50SAlexey Marchuk 	g_reduce_errno = -1;
182842f59f50SAlexey Marchuk 
182942f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
183042f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
183142f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == true);
183242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
183342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
183442f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
183542f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.iov[0].iov_base, req.decomp_iov[0].iov_base, req.iov[0].iov_len) == 0);
183642f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.iov[1].iov_base, req.decomp_iov[0].iov_base + req.iov[0].iov_len,
183742f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
18386881fb40SYalong Wang 	CU_ASSERT(RB_EMPTY(&vol.executing_requests));
183942f59f50SAlexey Marchuk 	CU_ASSERT(TAILQ_FIRST(&vol.free_requests) == &req);
184042f59f50SAlexey Marchuk 
1841619b4dbaSAlexey Marchuk 	/* Test 2 - single user's buffer length equals to chunk_size, buffer is not aligned
1842619b4dbaSAlexey Marchuk 	* User's buffer is copied */
1843619b4dbaSAlexey Marchuk 	memset(unaligned_user_buffer, 0xc, vol.params.chunk_size);
1844619b4dbaSAlexey Marchuk 	req.iov[0].iov_base = unaligned_user_buffer;
1845619b4dbaSAlexey Marchuk 	req.iov[0].iov_len = vol.params.chunk_size;
1846619b4dbaSAlexey Marchuk 	req.iovcnt = 1;
1847619b4dbaSAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
18486881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
1849619b4dbaSAlexey Marchuk 	g_reduce_errno = -1;
1850619b4dbaSAlexey Marchuk 
1851619b4dbaSAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
1852619b4dbaSAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
1853619b4dbaSAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == true);
1854619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
1855619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
1856619b4dbaSAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
1857619b4dbaSAlexey Marchuk 	CU_ASSERT(memcmp(req.iov[0].iov_base, req.decomp_iov[0].iov_base,
1858619b4dbaSAlexey Marchuk 			 req.iov[0].iov_len) == 0);
1859619b4dbaSAlexey Marchuk 
1860619b4dbaSAlexey Marchuk 	/* Test 3 - single user's buffer length equals to chunk_size
186142f59f50SAlexey Marchuk 	* User's buffer is not copied */
1862619b4dbaSAlexey Marchuk 	req.iov[0].iov_base = aligned_user_buffer;
186342f59f50SAlexey Marchuk 	req.iov[0].iov_len = vol.params.chunk_size;
186442f59f50SAlexey Marchuk 	req.iovcnt = 1;
186542f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
18666881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
186742f59f50SAlexey Marchuk 	g_reduce_errno = -1;
186842f59f50SAlexey Marchuk 
186942f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
187042f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
187142f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == false);
187242f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
187342f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.iov[0].iov_base);
187442f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
187542f59f50SAlexey Marchuk 
1876619b4dbaSAlexey Marchuk 	/* Test 4 - user's buffer less than chunk_size, without offset
187742f59f50SAlexey Marchuk 	 * User's buffers are copied */
187842f59f50SAlexey Marchuk 	user_buffer_iov_len = 4096;
187942f59f50SAlexey Marchuk 	req.iovcnt = 2;
188042f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - user_buffer_iov_len * 2;
188142f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1882619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
188342f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
188442f59f50SAlexey Marchuk 		memset(req.iov[i].iov_base, 0xb + i, req.iov[i].iov_len);
188542f59f50SAlexey Marchuk 	}
188642f59f50SAlexey Marchuk 
188742f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
18886881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
188942f59f50SAlexey Marchuk 	g_reduce_errno = -1;
189042f59f50SAlexey Marchuk 
189142f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
189242f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
189342f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == true);
189442f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
189542f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
189642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
189742f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.iov[0].iov_base, req.decomp_iov[0].iov_base,
189842f59f50SAlexey Marchuk 			 req.iov[0].iov_len) == 0);
189942f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.iov[1].iov_base, req.decomp_iov[0].iov_base + req.iov[0].iov_len,
190042f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
19016881fb40SYalong Wang 	CU_ASSERT(RB_EMPTY(&vol.executing_requests));
190242f59f50SAlexey Marchuk 	CU_ASSERT(TAILQ_FIRST(&vol.free_requests) == &req);
190342f59f50SAlexey Marchuk 
1904619b4dbaSAlexey Marchuk 	/* Test 5 - user's buffer less than chunk_size, non zero offset
190542f59f50SAlexey Marchuk 	* user's buffers are copied */
190642f59f50SAlexey Marchuk 	req.offset = 3;
190742f59f50SAlexey Marchuk 	offset_bytes = req.offset * vol.params.logical_block_size;
190842f59f50SAlexey Marchuk 	remainder_bytes = vol.params.chunk_size - offset_bytes - user_buffer_iov_len * 2;
190942f59f50SAlexey Marchuk 
191042f59f50SAlexey Marchuk 	for (i = 0; i < 2; i++) {
1911619b4dbaSAlexey Marchuk 		req.iov[i].iov_base = aligned_user_buffer + i * user_buffer_iov_len;
191242f59f50SAlexey Marchuk 		req.iov[i].iov_len = user_buffer_iov_len;
191342f59f50SAlexey Marchuk 		memset(req.iov[i].iov_base, 0xb + i, req.iov[i].iov_len);
191442f59f50SAlexey Marchuk 	}
191542f59f50SAlexey Marchuk 
191642f59f50SAlexey Marchuk 	memset(req.decomp_buf, 0xa, vol.params.chunk_size);
19176881fb40SYalong Wang 	RB_INSERT(executing_req_tree, &vol.executing_requests, &req);
191842f59f50SAlexey Marchuk 	g_reduce_errno = -1;
191942f59f50SAlexey Marchuk 
192042f59f50SAlexey Marchuk 	_prepare_compress_chunk(&req, false);
192142f59f50SAlexey Marchuk 	_reduce_vol_decompress_chunk(&req, _read_decompress_done);
192242f59f50SAlexey Marchuk 	CU_ASSERT(g_reduce_errno == 0);
192342f59f50SAlexey Marchuk 	CU_ASSERT(req.copy_after_decompress == true);
192442f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iovcnt == 1);
192542f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_base == req.decomp_buf);
192642f59f50SAlexey Marchuk 	CU_ASSERT(req.decomp_iov[0].iov_len == vol.params.chunk_size);
192742f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + offset_bytes, req.iov[0].iov_base,
192842f59f50SAlexey Marchuk 			 req.iov[0].iov_len) == 0);
192942f59f50SAlexey Marchuk 	CU_ASSERT(memcmp(req.decomp_iov[0].iov_base + offset_bytes + req.iov[0].iov_len,
193042f59f50SAlexey Marchuk 			 req.iov[1].iov_base,
193142f59f50SAlexey Marchuk 			 req.iov[1].iov_len) == 0);
19326881fb40SYalong Wang 	CU_ASSERT(RB_EMPTY(&vol.executing_requests));
193342f59f50SAlexey Marchuk 	CU_ASSERT(TAILQ_FIRST(&vol.free_requests) == &req);
1934619b4dbaSAlexey Marchuk 
1935619b4dbaSAlexey Marchuk 	free(buf);
193642f59f50SAlexey Marchuk }
193742f59f50SAlexey Marchuk 
19388dd1cd21SBen Walker static void
19398dd1cd21SBen Walker test_allocate_vol_requests(void)
1940b86e85f5SAlexey Marchuk {
1941b86e85f5SAlexey Marchuk 	struct spdk_reduce_vol *vol;
1942245271b6SYankun Li 	struct spdk_reduce_backing_dev backing_dev = {};
1943b86e85f5SAlexey Marchuk 	/* include chunk_sizes which are not power of 2 */
1944b86e85f5SAlexey Marchuk 	uint32_t chunk_sizes[] = {8192, 8320, 16384, 16416, 32768};
1945b86e85f5SAlexey Marchuk 	uint32_t io_unit_sizes[] = {512, 520, 4096, 4104, 4096};
1946b86e85f5SAlexey Marchuk 	uint32_t i;
1947b86e85f5SAlexey Marchuk 
1948245271b6SYankun Li 	/* bdev compress module can specify how big the user_ctx_size needs to be */
1949245271b6SYankun Li 	backing_dev.user_ctx_size = 64;
1950b86e85f5SAlexey Marchuk 	for (i = 0; i < 4; i++) {
1951b86e85f5SAlexey Marchuk 		vol = calloc(1, sizeof(*vol));
1952b86e85f5SAlexey Marchuk 		SPDK_CU_ASSERT_FATAL(vol);
1953b86e85f5SAlexey Marchuk 
1954b86e85f5SAlexey Marchuk 		vol->params.chunk_size = chunk_sizes[i];
1955b86e85f5SAlexey Marchuk 		vol->params.logical_block_size = io_unit_sizes[i];
1956b86e85f5SAlexey Marchuk 		vol->params.backing_io_unit_size = io_unit_sizes[i];
1957b86e85f5SAlexey Marchuk 		vol->backing_io_units_per_chunk = vol->params.chunk_size / vol->params.backing_io_unit_size;
1958b86e85f5SAlexey Marchuk 		vol->logical_blocks_per_chunk = vol->params.chunk_size / vol->params.logical_block_size;
1959245271b6SYankun Li 		vol->backing_dev = &backing_dev;
1960b86e85f5SAlexey Marchuk 
1961b86e85f5SAlexey Marchuk 		CU_ASSERT(_validate_vol_params(&vol->params) == 0);
1962b86e85f5SAlexey Marchuk 		CU_ASSERT(_allocate_vol_requests(vol) == 0);
1963b86e85f5SAlexey Marchuk 		_init_load_cleanup(vol, NULL);
1964b86e85f5SAlexey Marchuk 	}
1965b86e85f5SAlexey Marchuk }
1966b86e85f5SAlexey Marchuk 
19676bf35070SJim Harris int
19686bf35070SJim Harris main(int argc, char **argv)
19696bf35070SJim Harris {
19706bf35070SJim Harris 	CU_pSuite	suite = NULL;
19716bf35070SJim Harris 	unsigned int	num_failures;
19726bf35070SJim Harris 
197378b696bcSVitaliy Mysak 	CU_initialize_registry();
19746bf35070SJim Harris 
19756bf35070SJim Harris 	suite = CU_add_suite("reduce", NULL, NULL);
19766bf35070SJim Harris 
1977*412fced1SYalong Wang 	spdk_thread_lib_init(NULL, 0);
1978*412fced1SYalong Wang 	g_thread = spdk_thread_create(NULL, NULL);
1979*412fced1SYalong Wang 	spdk_set_thread(g_thread);
1980*412fced1SYalong Wang 
1981dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, get_pm_file_size);
1982dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, get_vol_size);
1983dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, init_failure);
1984dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, init_md);
1985dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, init_backing_dev);
1986dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, load);
1987dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, write_maps);
1988dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, read_write);
1989dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, readv_writev);
1990*412fced1SYalong Wang 	CU_ADD_TEST(suite, write_unmap_verify);
1991dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, destroy);
1992dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, defer_bdev_io);
1993dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, overlapped);
1994dcf0ca15SVitaliy Mysak 	CU_ADD_TEST(suite, compress_algorithm);
199542f59f50SAlexey Marchuk 	CU_ADD_TEST(suite, test_prepare_compress_chunk);
199642f59f50SAlexey Marchuk 	CU_ADD_TEST(suite, test_reduce_decompress_chunk);
1997b86e85f5SAlexey Marchuk 	CU_ADD_TEST(suite, test_allocate_vol_requests);
19986bf35070SJim Harris 
1999cfb65ba6SJim Harris 	g_unlink_path = g_path;
2000cfb65ba6SJim Harris 	g_unlink_callback = unlink_cb;
2001cfb65ba6SJim Harris 
2002ea941caeSKonrad Sztyber 	num_failures = spdk_ut_run_tests(argc, argv, NULL);
2003*412fced1SYalong Wang 
2004*412fced1SYalong Wang 	spdk_thread_exit(g_thread);
2005*412fced1SYalong Wang 	while (!spdk_thread_is_exited(g_thread)) {
2006*412fced1SYalong Wang 		spdk_thread_poll(g_thread, 0, 0);
2007*412fced1SYalong Wang 	}
2008*412fced1SYalong Wang 	spdk_thread_destroy(g_thread);
2009*412fced1SYalong Wang 	spdk_thread_lib_fini();
2010*412fced1SYalong Wang 
20116bf35070SJim Harris 	CU_cleanup_registry();
20126bf35070SJim Harris 	return num_failures;
20136bf35070SJim Harris }
2014