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