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 <zephyr/tc_util.h>
27 #include <stdbool.h>
28 #include <zephyr/kernel.h>
29 #include <zephyr/ztest.h>
30 
31 /* size of stack area used by each thread */
32 #define STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACK_SIZE)
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  */
58 
helper_thread(void)59 void helper_thread(void)
60 {
61 	void *ptr[NUMBLOCKS];           /* Pointer to memory block */
62 
63 	(void)memset(ptr, 0, sizeof(ptr));    /* keep static checkers happy */
64 	/* Wait for part 1 to complete */
65 	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
66 
67 	/* Part 2 of test */
68 
69 	TC_PRINT("(2) - Allocate %d blocks in <%s>\n", NUMBLOCKS, __func__);
70 
71 	/* Test k_mem_slab_alloc */
72 	test_slab_get_all_blocks(ptr);
73 
74 	k_sem_give(&SEM_HELPERDONE);  /* Indicate part 2 is complete */
75 	/* Wait for part 3 to complete */
76 	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
77 
78 	/*
79 	 * Part 4 of test.
80 	 * Free the first memory block.  RegressionTask is currently blocked
81 	 * waiting (with a timeout) for a memory block.  Freeing the memory
82 	 * block will unblock RegressionTask.
83 	 */
84 	TC_PRINT("(4) - Free a block in <%s> to unblock the other task "
85 		 "from alloc timeout\n", __func__);
86 
87 	TC_PRINT("%s: About to free a memory block\n", __func__);
88 	k_mem_slab_free(&map_lgblks, ptr[0]);
89 	k_sem_give(&SEM_HELPERDONE);
90 
91 	/* Part 5 of test */
92 	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
93 	TC_PRINT("(5) <%s> freeing the next block\n", __func__);
94 	TC_PRINT("%s: About to free another memory block\n", __func__);
95 	k_mem_slab_free(&map_lgblks, ptr[1]);
96 
97 	/*
98 	 * Free all the other blocks.  The first 2 blocks are freed by this task
99 	 */
100 	for (int i = 2; i < NUMBLOCKS; i++) {
101 		k_mem_slab_free(&map_lgblks, ptr[i]);
102 	}
103 	TC_PRINT("%s: freed all blocks allocated by this task\n", __func__);
104 
105 
106 	k_sem_give(&SEM_HELPERDONE);
107 
108 }  /* helper thread */
109 
110 
111 /**
112  *
113  * @brief Get all blocks from the memory slab
114  *
115  * Get all blocks from the memory slab.  It also tries to get one more block
116  * from the map after the map is empty to verify the error return code.
117  *
118  * This routine tests the following:
119  *
120  *   k_mem_slab_alloc(), k_mem_slab_num_used_get()
121  *
122  * @param p    pointer to pointer of allocated blocks
123  *
124  */
125 
test_slab_get_all_blocks(void ** p)126 void test_slab_get_all_blocks(void **p)
127 {
128 	void *errptr;   /* Pointer to block */
129 
130 	for (int i = 0; i < NUMBLOCKS; i++) {
131 		/* Verify number of used blocks in the map */
132 		zassert_equal(k_mem_slab_num_used_get(&map_lgblks), i,
133 			      "Failed k_mem_slab_num_used_get");
134 
135 		/* Get memory block */
136 		zassert_equal(k_mem_slab_alloc(&map_lgblks, &p[i], K_NO_WAIT), 0,
137 			      "Failed k_mem_slab_alloc");
138 	} /* for */
139 
140 	/*
141 	 * Verify number of used blocks in the map - expect all blocks are
142 	 * used
143 	 */
144 	zassert_equal(k_mem_slab_num_used_get(&map_lgblks), NUMBLOCKS,
145 		      "Failed k_mem_slab_num_used_get");
146 
147 	/* Try to get one more block and it should fail */
148 	zassert_equal(k_mem_slab_alloc(&map_lgblks, &errptr, K_NO_WAIT), -ENOMEM,
149 		      "Failed k_mem_slab_alloc");
150 
151 }  /* test_slab_get_all_blocks */
152 
153 /**
154  *
155  * @brief Free all memory blocks
156  *
157  * This routine frees all memory blocks and also verifies that the number of
158  * blocks used are correct.
159  *
160  * This routine tests the following:
161  *
162  *   k_mem_slab_free(&), k_mem_slab_num_used_get(&)
163  *
164  * @param p    pointer to pointer of allocated blocks
165  *
166  */
167 
test_slab_free_all_blocks(void ** p)168 void test_slab_free_all_blocks(void **p)
169 {
170 	for (int i = 0; i < NUMBLOCKS; i++) {
171 		/* Verify number of used blocks in the map */
172 		zassert_equal(k_mem_slab_num_used_get(&map_lgblks), NUMBLOCKS - i,
173 			      "Failed k_mem_slab_num_used_get");
174 
175 		TC_PRINT("  block ptr to free p[%d] = %p\n", i, p[i]);
176 		/* Free memory block */
177 		k_mem_slab_free(&map_lgblks, p[i]);
178 
179 		TC_PRINT("map_lgblks freed %d block\n", i + 1);
180 
181 	} /* for */
182 
183 	/*
184 	 * Verify number of used blocks in the map
185 	 *  - should be 0 as no blocks are used
186 	 */
187 
188 	zassert_equal(k_mem_slab_num_used_get(&map_lgblks), 0,
189 		      "Failed k_mem_slab_num_used_get");
190 
191 }   /* test_slab_free_all_blocks */
192 
193 /**
194  *
195  * @brief Main task to test memory slab interfaces
196  *
197  * @ingroup kernel_memory_slab_tests
198  *
199  * @details Verify that system allows for the definitions of boot-time
200  * memory regions.
201  * This routine calls test_slab_get_all_blocks() to get all
202  * memory blocks from the map and calls test_slab_free_all_blocks()
203  * to free all memory blocks. It also tries to wait (with and without
204  * timeout) for a memory block.
205  *
206  * @see k_mem_slab_alloc(), k_mem_slab_num_used_get(),
207  * memset(), k_mem_slab_free()
208  */
ZTEST(memory_slab_1cpu,test_mslab)209 ZTEST(memory_slab_1cpu, test_mslab)
210 {
211 	int ret_value;                  /* task_mem_map_xxx interface return value */
212 	void *b;                        /* Pointer to memory block */
213 	void *ptr[NUMBLOCKS];           /* Pointer to memory block */
214 
215 	/* not strictly necessary, but keeps coverity checks happy */
216 	(void)memset(ptr, 0, sizeof(ptr));
217 
218 	/* Part 1 of test */
219 
220 	TC_PRINT("(1) - Allocate and free %d blocks "
221 		 "in <%s>\n", NUMBLOCKS, __func__);
222 
223 	/* Test k_mem_slab_alloc */
224 	test_slab_get_all_blocks(ptr);
225 
226 	/* Test task_mem_map_free */
227 	test_slab_free_all_blocks(ptr);
228 
229 	k_sem_give(&SEM_REGRESSDONE);   /* Allow helper thread to run */
230 	/* Wait for helper thread to finish */
231 	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
232 
233 	/*
234 	 * Part 3 of test.
235 	 *
236 	 * helper thread got all memory blocks.  There is no free block left.
237 	 * The call will timeout.  Note that control does not switch back to
238 	 * helper thread as it is waiting for SEM_REGRESSDONE.
239 	 */
240 
241 	TC_PRINT("(3) - Further allocation results in  timeout "
242 		 "in <%s>\n", __func__);
243 
244 	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_MSEC(20));
245 	zassert_equal(-EAGAIN, ret_value,
246 		      "Failed k_mem_slab_alloc, retValue %d\n", ret_value);
247 
248 	TC_PRINT("%s: start to wait for block\n", __func__);
249 	k_sem_give(&SEM_REGRESSDONE);    /* Allow helper thread to run part 4 */
250 	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_MSEC(50));
251 	zassert_equal(0, ret_value,
252 		      "Failed k_mem_slab_alloc, ret_value %d\n", ret_value);
253 
254 	/* Wait for helper thread to complete */
255 	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
256 
257 	TC_PRINT("%s: start to wait for block\n", __func__);
258 	k_sem_give(&SEM_REGRESSDONE);    /* Allow helper thread to run part 5 */
259 	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_FOREVER);
260 	zassert_equal(0, ret_value,
261 		      "Failed k_mem_slab_alloc, ret_value %d\n", ret_value);
262 
263 	/* Wait for helper thread to complete */
264 	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
265 
266 
267 	/* Free memory block */
268 	TC_PRINT("%s: Used %d block\n", __func__,
269 		 k_mem_slab_num_used_get(&map_lgblks));
270 	k_mem_slab_free(&map_lgblks, b);
271 	TC_PRINT("%s: 1 block freed, used %d block\n",
272 		 __func__,  k_mem_slab_num_used_get(&map_lgblks));
273 }
274 
275 K_THREAD_DEFINE(HELPER, STACKSIZE, helper_thread, NULL, NULL, NULL,
276 		7, 0, 0);
277 
278 /*test case main entry*/
279 ZTEST_SUITE(memory_slab_1cpu, NULL, NULL, ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
280