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