xref: /spdk/test/env/memory/memory_ut.c (revision 9889ab2dc80e40dae92dcef361d53dcba722043d)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "env_dpdk/memory.c"
35 
36 #define UNIT_TEST_NO_VTOPHYS
37 #define UNIT_TEST_NO_PCI_ADDR
38 #include "common/lib/test_env.c"
39 #include "spdk_cunit.h"
40 
41 #include "spdk/bit_array.h"
42 
43 #define PAGE_ARRAY_SIZE (100)
44 static struct spdk_bit_array *g_page_array;
45 static void *g_vaddr_to_fail = (void *)UINT64_MAX;
46 
47 static int
48 test_mem_map_notify(void *cb_ctx, struct spdk_mem_map *map,
49 		    enum spdk_mem_map_notify_action action,
50 		    void *vaddr, size_t len)
51 {
52 	uint32_t i, end;
53 
54 	SPDK_CU_ASSERT_FATAL(((uintptr_t)vaddr & MASK_2MB) == 0);
55 	SPDK_CU_ASSERT_FATAL((len & MASK_2MB) == 0);
56 
57 	/*
58 	 * This is a test requirement - the bit array we use to verify
59 	 * pages are valid is only so large.
60 	 */
61 	SPDK_CU_ASSERT_FATAL((uintptr_t)vaddr < (VALUE_2MB * PAGE_ARRAY_SIZE));
62 
63 	i = (uintptr_t)vaddr >> SHIFT_2MB;
64 	end = i + (len >> SHIFT_2MB);
65 	for (; i < end; i++) {
66 		switch (action) {
67 		case SPDK_MEM_MAP_NOTIFY_REGISTER:
68 			/* This page should not already be registered */
69 			SPDK_CU_ASSERT_FATAL(spdk_bit_array_get(g_page_array, i) == false);
70 			SPDK_CU_ASSERT_FATAL(spdk_bit_array_set(g_page_array, i) == 0);
71 			break;
72 		case SPDK_MEM_MAP_NOTIFY_UNREGISTER:
73 			SPDK_CU_ASSERT_FATAL(spdk_bit_array_get(g_page_array, i) == true);
74 			spdk_bit_array_clear(g_page_array, i);
75 			break;
76 		default:
77 			SPDK_UNREACHABLE();
78 		}
79 	}
80 
81 	return 0;
82 }
83 
84 static int
85 test_mem_map_notify_fail(void *cb_ctx, struct spdk_mem_map *map,
86 			 enum spdk_mem_map_notify_action action, void *vaddr, size_t size)
87 {
88 	struct spdk_mem_map *reg_map = cb_ctx;
89 
90 	switch (action) {
91 	case SPDK_MEM_MAP_NOTIFY_REGISTER:
92 		if (vaddr == g_vaddr_to_fail) {
93 			/* Test the error handling. */
94 			return -1;
95 		}
96 		break;
97 	case SPDK_MEM_MAP_NOTIFY_UNREGISTER:
98 		/* Clear the same region in the other mem_map to be able to
99 		 * verify that there was no memory left still registered after
100 		 * the mem_map creation failure.
101 		 */
102 		spdk_mem_map_clear_translation(reg_map, (uint64_t)vaddr, size);
103 		break;
104 	}
105 
106 	return 0;
107 }
108 
109 static int
110 test_mem_map_notify_checklen(void *cb_ctx, struct spdk_mem_map *map,
111 			     enum spdk_mem_map_notify_action action, void *vaddr, size_t size)
112 {
113 	size_t *len_arr = cb_ctx;
114 
115 	/*
116 	 * This is a test requirement - the len array we use to verify
117 	 * pages are valid is only so large.
118 	 */
119 	SPDK_CU_ASSERT_FATAL((uintptr_t)vaddr < (VALUE_2MB * PAGE_ARRAY_SIZE));
120 
121 	switch (action) {
122 	case SPDK_MEM_MAP_NOTIFY_REGISTER:
123 		assert(size == len_arr[(uintptr_t)vaddr / VALUE_2MB]);
124 		break;
125 	case SPDK_MEM_MAP_NOTIFY_UNREGISTER:
126 		CU_ASSERT(size == len_arr[(uintptr_t)vaddr / VALUE_2MB]);
127 		break;
128 	}
129 
130 	return 0;
131 }
132 
133 static int
134 test_check_regions_contiguous(uint64_t addr1, uint64_t addr2)
135 {
136 	return addr1 == addr2;
137 }
138 
139 const struct spdk_mem_map_ops test_mem_map_ops = {
140 	.notify_cb = test_mem_map_notify,
141 	.are_contiguous = test_check_regions_contiguous
142 };
143 
144 const struct spdk_mem_map_ops test_mem_map_ops_no_contig = {
145 	.notify_cb = test_mem_map_notify,
146 	.are_contiguous = NULL
147 };
148 
149 struct spdk_mem_map_ops test_map_ops_notify_fail = {
150 	.notify_cb = test_mem_map_notify_fail,
151 	.are_contiguous = NULL
152 };
153 
154 struct spdk_mem_map_ops test_map_ops_notify_checklen = {
155 	.notify_cb = test_mem_map_notify_checklen,
156 	.are_contiguous = NULL
157 };
158 
159 static void
160 test_mem_map_alloc_free(void)
161 {
162 	struct spdk_mem_map *map, *failed_map;
163 	uint64_t default_translation = 0xDEADBEEF0BADF00D;
164 	int i;
165 
166 	map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops, NULL);
167 	SPDK_CU_ASSERT_FATAL(map != NULL);
168 	spdk_mem_map_free(&map);
169 	CU_ASSERT(map == NULL);
170 
171 	map = spdk_mem_map_alloc(default_translation, NULL, NULL);
172 	SPDK_CU_ASSERT_FATAL(map != NULL);
173 
174 	/* Register some memory for the initial memory walk in
175 	 * spdk_mem_map_alloc(). We'll fail registering the last region
176 	 * and will check if the mem_map cleaned up all its previously
177 	 * initialized translations.
178 	 */
179 	for (i = 0; i < 5; i++) {
180 		spdk_mem_register((void *)(uintptr_t)(2 * i * VALUE_2MB), VALUE_2MB);
181 	}
182 
183 	/* The last region */
184 	g_vaddr_to_fail = (void *)(8 * VALUE_2MB);
185 	failed_map = spdk_mem_map_alloc(default_translation, &test_map_ops_notify_fail, map);
186 	CU_ASSERT(failed_map == NULL);
187 
188 	for (i = 0; i < 4; i++) {
189 		uint64_t reg, size = VALUE_2MB;
190 
191 		reg = spdk_mem_map_translate(map, 2 * i * VALUE_2MB, &size);
192 		/* check if `failed_map` didn't leave any translations behind */
193 		CU_ASSERT(reg == default_translation);
194 	}
195 
196 	for (i = 0; i < 5; i++) {
197 		spdk_mem_unregister((void *)(uintptr_t)(2 * i * VALUE_2MB), VALUE_2MB);
198 	}
199 
200 	spdk_mem_map_free(&map);
201 	CU_ASSERT(map == NULL);
202 }
203 
204 static void
205 test_mem_map_translation(void)
206 {
207 	struct spdk_mem_map *map;
208 	uint64_t default_translation = 0xDEADBEEF0BADF00D;
209 	uint64_t addr;
210 	uint64_t mapping_length;
211 	int rc;
212 
213 	map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops, NULL);
214 	SPDK_CU_ASSERT_FATAL(map != NULL);
215 
216 	/* Try to get translation for address with no translation */
217 	addr = spdk_mem_map_translate(map, 10, NULL);
218 	CU_ASSERT(addr == default_translation);
219 
220 	/* Set translation for region of non-2MB multiple size */
221 	rc = spdk_mem_map_set_translation(map, VALUE_2MB, 1234, VALUE_2MB);
222 	CU_ASSERT(rc == -EINVAL);
223 
224 	/* Set translation for vaddr that isn't 2MB aligned */
225 	rc = spdk_mem_map_set_translation(map, 1234, VALUE_2MB, VALUE_2MB);
226 	CU_ASSERT(rc == -EINVAL);
227 
228 	/* Set translation for one 2MB page */
229 	rc = spdk_mem_map_set_translation(map, VALUE_2MB, VALUE_2MB, VALUE_2MB);
230 	CU_ASSERT(rc == 0);
231 
232 	/* Set translation for region that overlaps the previous translation */
233 	rc = spdk_mem_map_set_translation(map, 0, 3 * VALUE_2MB, 0);
234 	CU_ASSERT(rc == 0);
235 
236 	/* Make sure we indicate that the three regions are contiguous */
237 	mapping_length = VALUE_2MB * 3;
238 	addr = spdk_mem_map_translate(map, 0, &mapping_length);
239 	CU_ASSERT(addr == 0);
240 	CU_ASSERT(mapping_length == VALUE_2MB * 3);
241 
242 	/* Translate an unaligned address */
243 	mapping_length = VALUE_2MB * 3;
244 	addr = spdk_mem_map_translate(map, VALUE_4KB, &mapping_length);
245 	CU_ASSERT(addr == 0);
246 	CU_ASSERT(mapping_length == VALUE_2MB * 3 - VALUE_4KB);
247 
248 	/* Clear translation for the middle page of the larger region. */
249 	rc = spdk_mem_map_clear_translation(map, VALUE_2MB, VALUE_2MB);
250 	CU_ASSERT(rc == 0);
251 
252 	/* Get translation for first page */
253 	addr = spdk_mem_map_translate(map, 0, NULL);
254 	CU_ASSERT(addr == 0);
255 
256 	/* Make sure we indicate that the three regions are no longer contiguous */
257 	mapping_length = VALUE_2MB * 3;
258 	addr = spdk_mem_map_translate(map, 0, &mapping_length);
259 	CU_ASSERT(addr == 0);
260 	CU_ASSERT(mapping_length == VALUE_2MB);
261 
262 	/* Get translation for an unallocated block. Make sure size is 0 */
263 	mapping_length = VALUE_2MB * 3;
264 	addr = spdk_mem_map_translate(map, VALUE_2MB, &mapping_length);
265 	CU_ASSERT(addr == default_translation);
266 	CU_ASSERT(mapping_length == VALUE_2MB);
267 
268 	/* Verify translation for 2nd page is the default */
269 	addr = spdk_mem_map_translate(map, VALUE_2MB, NULL);
270 	CU_ASSERT(addr == default_translation);
271 
272 	/* Get translation for third page */
273 	addr = spdk_mem_map_translate(map, 2 * VALUE_2MB, NULL);
274 	/*
275 	 * Note that addr should be 0, not 4MB. When we set the
276 	 * translation above, we said the whole 6MB region
277 	 * should translate to 0.
278 	 */
279 	CU_ASSERT(addr == 0);
280 
281 	/* Translate only a subset of a 2MB page */
282 	mapping_length = 543;
283 	addr = spdk_mem_map_translate(map, 0, &mapping_length);
284 	CU_ASSERT(addr == 0);
285 	CU_ASSERT(mapping_length == 543);
286 
287 	/* Translate another subset of a 2MB page */
288 	mapping_length = 543;
289 	addr = spdk_mem_map_translate(map, VALUE_4KB, &mapping_length);
290 	CU_ASSERT(addr == 0);
291 	CU_ASSERT(mapping_length == 543);
292 
293 	/* Try to translate an unaligned region that is only partially registered */
294 	mapping_length = 543;
295 	addr = spdk_mem_map_translate(map, 3 * VALUE_2MB - 196, &mapping_length);
296 	CU_ASSERT(addr == 0);
297 	CU_ASSERT(mapping_length == 196);
298 
299 	/* Clear translation for the first page */
300 	rc = spdk_mem_map_clear_translation(map, 0, VALUE_2MB);
301 	CU_ASSERT(rc == 0);
302 
303 	/* Get translation for the first page */
304 	addr = spdk_mem_map_translate(map, 0, NULL);
305 	CU_ASSERT(addr == default_translation);
306 
307 	/* Clear translation for the third page */
308 	rc = spdk_mem_map_clear_translation(map, 2 * VALUE_2MB, VALUE_2MB);
309 	CU_ASSERT(rc == 0);
310 
311 	/* Get translation for the third page */
312 	addr = spdk_mem_map_translate(map, 2 * VALUE_2MB, NULL);
313 	CU_ASSERT(addr == default_translation);
314 
315 	/* Set translation for the last valid 2MB region */
316 	rc = spdk_mem_map_set_translation(map, 0xffffffe00000ULL, VALUE_2MB, 0x1234);
317 	CU_ASSERT(rc == 0);
318 
319 	/* Verify translation for last valid 2MB region */
320 	addr = spdk_mem_map_translate(map, 0xffffffe00000ULL, NULL);
321 	CU_ASSERT(addr == 0x1234);
322 
323 	/* Attempt to set translation for the first invalid address */
324 	rc = spdk_mem_map_set_translation(map, 0x1000000000000ULL, VALUE_2MB, 0x5678);
325 	CU_ASSERT(rc == -EINVAL);
326 
327 	/* Attempt to set translation starting at a valid address but exceeding the valid range */
328 	rc = spdk_mem_map_set_translation(map, 0xffffffe00000ULL, VALUE_2MB * 2, 0x123123);
329 	CU_ASSERT(rc != 0);
330 
331 	spdk_mem_map_free(&map);
332 	CU_ASSERT(map == NULL);
333 
334 	/* Allocate a map without a contiguous region checker */
335 	map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops_no_contig, NULL);
336 	SPDK_CU_ASSERT_FATAL(map != NULL);
337 
338 	/* map three contiguous regions */
339 	rc = spdk_mem_map_set_translation(map, 0, 3 * VALUE_2MB, 0);
340 	CU_ASSERT(rc == 0);
341 
342 	/* Since we can't check their contiguity, make sure we only return the size of one page */
343 	mapping_length = VALUE_2MB * 3;
344 	addr = spdk_mem_map_translate(map, 0, &mapping_length);
345 	CU_ASSERT(addr == 0);
346 	CU_ASSERT(mapping_length == VALUE_2MB);
347 
348 	/* Translate only a subset of a 2MB page */
349 	mapping_length = 543;
350 	addr = spdk_mem_map_translate(map, 0, &mapping_length);
351 	CU_ASSERT(addr == 0);
352 	CU_ASSERT(mapping_length == 543);
353 
354 	/* Clear the translation */
355 	rc = spdk_mem_map_clear_translation(map, 0, VALUE_2MB * 3);
356 	CU_ASSERT(rc == 0);
357 
358 	spdk_mem_map_free(&map);
359 	CU_ASSERT(map == NULL);
360 }
361 
362 static void
363 test_mem_map_registration(void)
364 {
365 	int rc;
366 	struct spdk_mem_map *map;
367 	uint64_t default_translation = 0xDEADBEEF0BADF00D;
368 
369 	map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops, NULL);
370 	SPDK_CU_ASSERT_FATAL(map != NULL);
371 
372 	/* Unregister memory region that wasn't previously registered */
373 	rc =  spdk_mem_unregister((void *)VALUE_2MB, VALUE_2MB);
374 	CU_ASSERT(rc == -EINVAL);
375 
376 	/* Register non-2MB multiple size */
377 	rc = spdk_mem_register((void *)VALUE_2MB, 1234);
378 	CU_ASSERT(rc == -EINVAL);
379 
380 	/* Register region that isn't 2MB aligned */
381 	rc = spdk_mem_register((void *)1234, VALUE_2MB);
382 	CU_ASSERT(rc == -EINVAL);
383 
384 	/* Register one 2MB page */
385 	rc = spdk_mem_register((void *)VALUE_2MB, VALUE_2MB);
386 	CU_ASSERT(rc == 0);
387 
388 	/* Register an overlapping address range */
389 	rc = spdk_mem_register((void *)0, 3 * VALUE_2MB);
390 	CU_ASSERT(rc == -EBUSY);
391 
392 	/* Unregister a 2MB page */
393 	rc = spdk_mem_unregister((void *)VALUE_2MB, VALUE_2MB);
394 	CU_ASSERT(rc == 0);
395 
396 	/* Register non overlapping address range */
397 	rc = spdk_mem_register((void *)0, 3 * VALUE_2MB);
398 	CU_ASSERT(rc == 0);
399 
400 	/* Unregister the middle page of the larger region. */
401 	rc = spdk_mem_unregister((void *)VALUE_2MB, VALUE_2MB);
402 	CU_ASSERT(rc == -ERANGE);
403 
404 	/* Unregister the first page */
405 	rc = spdk_mem_unregister((void *)0, VALUE_2MB);
406 	CU_ASSERT(rc == -ERANGE);
407 
408 	/* Unregister the third page */
409 	rc = spdk_mem_unregister((void *)(2 * VALUE_2MB), VALUE_2MB);
410 	CU_ASSERT(rc == -ERANGE);
411 
412 	/* Unregister the entire address range */
413 	rc = spdk_mem_unregister((void *)0, 3 * VALUE_2MB);
414 	CU_ASSERT(rc == 0);
415 
416 	spdk_mem_map_free(&map);
417 	CU_ASSERT(map == NULL);
418 }
419 
420 static void
421 test_mem_map_registration_adjacent(void)
422 {
423 	struct spdk_mem_map *map, *newmap;
424 	uint64_t default_translation = 0xDEADBEEF0BADF00D;
425 	uintptr_t vaddr;
426 	unsigned i;
427 	size_t notify_len[PAGE_ARRAY_SIZE] = {0};
428 	size_t chunk_len[] = { 2, 1, 3, 2, 1, 1 };
429 
430 	map = spdk_mem_map_alloc(default_translation,
431 				 &test_map_ops_notify_checklen, notify_len);
432 	SPDK_CU_ASSERT_FATAL(map != NULL);
433 
434 	vaddr = 0;
435 	for (i = 0; i < SPDK_COUNTOF(chunk_len); i++) {
436 		notify_len[vaddr / VALUE_2MB] = chunk_len[i] * VALUE_2MB;
437 		spdk_mem_register((void *)vaddr, notify_len[vaddr / VALUE_2MB]);
438 		vaddr += notify_len[vaddr / VALUE_2MB];
439 	}
440 
441 	/* Verify the memory is translated in the same chunks it was registered */
442 	newmap = spdk_mem_map_alloc(default_translation,
443 				    &test_map_ops_notify_checklen, notify_len);
444 	SPDK_CU_ASSERT_FATAL(newmap != NULL);
445 	spdk_mem_map_free(&newmap);
446 	CU_ASSERT(newmap == NULL);
447 
448 	vaddr = 0;
449 	for (i = 0; i < SPDK_COUNTOF(chunk_len); i++) {
450 		notify_len[vaddr / VALUE_2MB] = chunk_len[i] * VALUE_2MB;
451 		spdk_mem_unregister((void *)vaddr, notify_len[vaddr / VALUE_2MB]);
452 		vaddr += notify_len[vaddr / VALUE_2MB];
453 	}
454 
455 	/* Register all chunks again just to unregister them again, but this
456 	 * time with only a single unregister() call.
457 	 */
458 	vaddr = 0;
459 	for (i = 0; i < SPDK_COUNTOF(chunk_len); i++) {
460 		notify_len[vaddr / VALUE_2MB] = chunk_len[i] * VALUE_2MB;
461 		spdk_mem_register((void *)vaddr, notify_len[vaddr / VALUE_2MB]);
462 		vaddr += notify_len[vaddr / VALUE_2MB];
463 	}
464 	spdk_mem_unregister(0, vaddr);
465 
466 	spdk_mem_map_free(&map);
467 	CU_ASSERT(map == NULL);
468 }
469 
470 int
471 main(int argc, char **argv)
472 {
473 	CU_pSuite	suite = NULL;
474 	unsigned int	num_failures;
475 
476 	/*
477 	 * These tests can use PAGE_ARRAY_SIZE 2MB pages of memory.
478 	 * Note that the tests just verify addresses - this memory
479 	 * is not actually allocated.
480 	  */
481 	g_page_array = spdk_bit_array_create(PAGE_ARRAY_SIZE);
482 
483 	/* Initialize the memory map */
484 	if (spdk_mem_map_init() < 0) {
485 		return CUE_NOMEMORY;
486 	}
487 
488 	if (CU_initialize_registry() != CUE_SUCCESS) {
489 		return CU_get_error();
490 	}
491 
492 	suite = CU_add_suite("memory", NULL, NULL);
493 	if (suite == NULL) {
494 		CU_cleanup_registry();
495 		return CU_get_error();
496 	}
497 
498 	if (
499 		CU_add_test(suite, "alloc and free memory map", test_mem_map_alloc_free) == NULL ||
500 		CU_add_test(suite, "mem map translation", test_mem_map_translation) == NULL ||
501 		CU_add_test(suite, "mem map registration", test_mem_map_registration) == NULL ||
502 		CU_add_test(suite, "mem map adjacent registrations", test_mem_map_registration_adjacent) == NULL
503 	) {
504 		CU_cleanup_registry();
505 		return CU_get_error();
506 	}
507 
508 	CU_basic_set_mode(CU_BRM_VERBOSE);
509 	CU_basic_run_tests();
510 	num_failures = CU_get_number_of_failures();
511 	CU_cleanup_registry();
512 
513 	spdk_bit_array_free(&g_page_array);
514 
515 	return num_failures;
516 }
517