1 /*
2  * Copyright (c) 2012-2014 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @brief Test memory slab APIs
9  *
10  * @defgroup kernel_memory_slab_tests Memory Slab Tests
11  *
12  * @ingroup all_tests
13  *
14  * This module tests the following memory slab routines:
15  *
16  *     k_mem_slab_alloc
17  *     k_mem_slab_free
18  *     k_mem_slab_num_used_get
19  *
20  * @note
21  * One should ensure that the block is released to the same memory slab from
22  * which it was allocated, and is only released once.  Using an invalid pointer
23  * will have unpredictable side effects.
24  */
25 
26 #include <tc_util.h>
27 #include <stdbool.h>
28 #include <zephyr.h>
29 #include <ztest.h>
30 
31 /* size of stack area used by each thread */
32 #define STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
33 
34 /* Number of memory blocks. The minimum number of blocks needed to run the
35  * test is 2
36  */
37 #define NUMBLOCKS   4
38 
39 void test_slab_get_all_blocks(void **p);
40 void test_slab_free_all_blocks(void **p);
41 
42 
43 K_SEM_DEFINE(SEM_HELPERDONE, 0, 1);
44 K_SEM_DEFINE(SEM_REGRESSDONE, 0, 1);
45 
46 K_MEM_SLAB_DEFINE(map_lgblks, 1024, NUMBLOCKS, 4);
47 
48 
49 /**
50  *
51  * @brief Helper task
52  *
53  * This routine gets all blocks from the memory slab.  It uses semaphores
54  * SEM_REGRESDONE and SEM_HELPERDONE to synchronize between different parts
55  * of the test.
56  *
57  * @return  N/A
58  */
59 
helper_thread(void)60 void helper_thread(void)
61 {
62 	void *ptr[NUMBLOCKS];           /* Pointer to memory block */
63 
64 	(void)memset(ptr, 0, sizeof(ptr));    /* keep static checkers happy */
65 	/* Wait for part 1 to complete */
66 	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
67 
68 	/* Part 2 of test */
69 
70 	TC_PRINT("(2) - Allocate %d blocks in <%s>\n", NUMBLOCKS, __func__);
71 
72 	/* Test k_mem_slab_alloc */
73 	test_slab_get_all_blocks(ptr);
74 
75 	k_sem_give(&SEM_HELPERDONE);  /* Indicate part 2 is complete */
76 	/* Wait for part 3 to complete */
77 	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
78 
79 	/*
80 	 * Part 4 of test.
81 	 * Free the first memory block.  RegressionTask is currently blocked
82 	 * waiting (with a timeout) for a memory block.  Freeing the memory
83 	 * block will unblock RegressionTask.
84 	 */
85 	TC_PRINT("(4) - Free a block in <%s> to unblock the other task "
86 		 "from alloc timeout\n", __func__);
87 
88 	TC_PRINT("%s: About to free a memory block\n", __func__);
89 	k_mem_slab_free(&map_lgblks, &ptr[0]);
90 	k_sem_give(&SEM_HELPERDONE);
91 
92 	/* Part 5 of test */
93 	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
94 	TC_PRINT("(5) <%s> freeing the next block\n", __func__);
95 	TC_PRINT("%s: About to free another memory block\n", __func__);
96 	k_mem_slab_free(&map_lgblks, &ptr[1]);
97 
98 	/*
99 	 * Free all the other blocks.  The first 2 blocks are freed by this task
100 	 */
101 	for (int i = 2; i < NUMBLOCKS; i++) {
102 		k_mem_slab_free(&map_lgblks, &ptr[i]);
103 	}
104 	TC_PRINT("%s: freed all blocks allocated by this task\n", __func__);
105 
106 
107 	k_sem_give(&SEM_HELPERDONE);
108 
109 }  /* helper thread */
110 
111 
112 /**
113  *
114  * @brief Get all blocks from the memory slab
115  *
116  * Get all blocks from the memory slab.  It also tries to get one more block
117  * from the map after the map is empty to verify the error return code.
118  *
119  * This routine tests the following:
120  *
121  *   k_mem_slab_alloc(), k_mem_slab_num_used_get()
122  *
123  * @param p    pointer to pointer of allocated blocks
124  *
125  * @return  TC_PASS, TC_FAIL
126  */
127 
test_slab_get_all_blocks(void ** p)128 void test_slab_get_all_blocks(void **p)
129 {
130 	void *errptr;   /* Pointer to block */
131 
132 	for (int i = 0; i < NUMBLOCKS; i++) {
133 		/* Verify number of used blocks in the map */
134 		zassert_equal(k_mem_slab_num_used_get(&map_lgblks), i,
135 			      "Failed k_mem_slab_num_used_get");
136 
137 		/* Get memory block */
138 		zassert_equal(k_mem_slab_alloc(&map_lgblks, &p[i], K_NO_WAIT), 0,
139 			      "Failed k_mem_slab_alloc");
140 	} /* for */
141 
142 	/*
143 	 * Verify number of used blocks in the map - expect all blocks are
144 	 * used
145 	 */
146 	zassert_equal(k_mem_slab_num_used_get(&map_lgblks), NUMBLOCKS,
147 		      "Failed k_mem_slab_num_used_get");
148 
149 	/* Try to get one more block and it should fail */
150 	zassert_equal(k_mem_slab_alloc(&map_lgblks, &errptr, K_NO_WAIT), -ENOMEM,
151 		      "Failed k_mem_slab_alloc");
152 
153 }  /* test_slab_get_all_blocks */
154 
155 /**
156  *
157  * @brief Free all memory blocks
158  *
159  * This routine frees all memory blocks and also verifies that the number of
160  * blocks used are correct.
161  *
162  * This routine tests the following:
163  *
164  *   k_mem_slab_free(&), k_mem_slab_num_used_get(&)
165  *
166  * @param p    pointer to pointer of allocated blocks
167  *
168  * @return  TC_PASS, TC_FAIL
169  */
170 
test_slab_free_all_blocks(void ** p)171 void test_slab_free_all_blocks(void **p)
172 {
173 	for (int i = 0; i < NUMBLOCKS; i++) {
174 		/* Verify number of used blocks in the map */
175 		zassert_equal(k_mem_slab_num_used_get(&map_lgblks), NUMBLOCKS - i,
176 			      "Failed k_mem_slab_num_used_get");
177 
178 		TC_PRINT("  block ptr to free p[%d] = %p\n", i, p[i]);
179 		/* Free memory block */
180 		k_mem_slab_free(&map_lgblks, &p[i]);
181 
182 		TC_PRINT("map_lgblks freed %d block\n", i + 1);
183 
184 	} /* for */
185 
186 	/*
187 	 * Verify number of used blocks in the map
188 	 *  - should be 0 as no blocks are used
189 	 */
190 
191 	zassert_equal(k_mem_slab_num_used_get(&map_lgblks), 0,
192 		      "Failed k_mem_slab_num_used_get");
193 
194 }   /* test_slab_free_all_blocks */
195 
196 /**
197  *
198  * @brief Main task to test memory slab interfaces
199  *
200  * @ingroup kernel_memory_slab_tests
201  *
202  * @details Verify that system allows for the definitions of boot-time
203  * memory regions.
204  * This routine calls test_slab_get_all_blocks() to get all
205  * memory blocks from the map and calls test_slab_free_all_blocks()
206  * to free all memory blocks. It also tries to wait (with and without
207  * timeout) for a memory block.
208  *
209  * @see k_mem_slab_alloc(), k_mem_slab_num_used_get(),
210  * memset(), k_mem_slab_free()
211  */
212 
test_mslab(void)213 void test_mslab(void)
214 {
215 	int ret_value;                  /* task_mem_map_xxx interface return value */
216 	void *b;                        /* Pointer to memory block */
217 	void *ptr[NUMBLOCKS];           /* Pointer to memory block */
218 
219 	/* not strictly necessary, but keeps coverity checks happy */
220 	(void)memset(ptr, 0, sizeof(ptr));
221 
222 	/* Part 1 of test */
223 
224 	TC_PRINT("(1) - Allocate and free %d blocks "
225 		 "in <%s>\n", NUMBLOCKS, __func__);
226 
227 	/* Test k_mem_slab_alloc */
228 	test_slab_get_all_blocks(ptr);
229 
230 	/* Test task_mem_map_free */
231 	test_slab_free_all_blocks(ptr);
232 
233 	k_sem_give(&SEM_REGRESSDONE);   /* Allow helper thread to run */
234 	/* Wait for helper thread to finish */
235 	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
236 
237 	/*
238 	 * Part 3 of test.
239 	 *
240 	 * helper thread got all memory blocks.  There is no free block left.
241 	 * The call will timeout.  Note that control does not switch back to
242 	 * helper thread as it is waiting for SEM_REGRESSDONE.
243 	 */
244 
245 	TC_PRINT("(3) - Further allocation results in  timeout "
246 		 "in <%s>\n", __func__);
247 
248 	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_MSEC(20));
249 	zassert_equal(-EAGAIN, ret_value,
250 		      "Failed k_mem_slab_alloc, retValue %d\n", ret_value);
251 
252 	TC_PRINT("%s: start to wait for block\n", __func__);
253 	k_sem_give(&SEM_REGRESSDONE);    /* Allow helper thread to run part 4 */
254 	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_MSEC(50));
255 	zassert_equal(0, ret_value,
256 		      "Failed k_mem_slab_alloc, ret_value %d\n", ret_value);
257 
258 	/* Wait for helper thread to complete */
259 	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
260 
261 	TC_PRINT("%s: start to wait for block\n", __func__);
262 	k_sem_give(&SEM_REGRESSDONE);    /* Allow helper thread to run part 5 */
263 	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_FOREVER);
264 	zassert_equal(0, ret_value,
265 		      "Failed k_mem_slab_alloc, ret_value %d\n", ret_value);
266 
267 	/* Wait for helper thread to complete */
268 	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
269 
270 
271 	/* Free memory block */
272 	TC_PRINT("%s: Used %d block\n", __func__,
273 		 k_mem_slab_num_used_get(&map_lgblks));
274 	k_mem_slab_free(&map_lgblks, &b);
275 	TC_PRINT("%s: 1 block freed, used %d block\n",
276 		 __func__,  k_mem_slab_num_used_get(&map_lgblks));
277 }
278 
279 K_THREAD_DEFINE(HELPER, STACKSIZE, helper_thread, NULL, NULL, NULL,
280 		7, 0, 0);
281 
282 /*test case main entry*/
test_main(void)283 void test_main(void)
284 {
285 	ztest_test_suite(memory_slab,
286 			 ztest_1cpu_unit_test(test_mslab));
287 	ztest_run_test_suite(memory_slab);
288 }
289