| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/ztest.h> |
| #include "test_mslab.h" |
| |
| /* TESTPOINT: Statically define and initialize a memory slab*/ |
| K_MEM_SLAB_DEFINE(kmslab, BLK_SIZE, BLK_NUM, BLK_ALIGN); |
| static char __aligned(BLK_ALIGN) tslab[BLK_SIZE * BLK_NUM]; |
| static struct k_mem_slab mslab; |
| K_SEM_DEFINE(SEM_HELPERDONE, 0, 1); |
| K_SEM_DEFINE(SEM_REGRESSDONE, 0, 1); |
| static K_THREAD_STACK_DEFINE(stack, STACKSIZE); |
| static struct k_thread HELPER; |
| |
| void *mslab_setup(void) |
| { |
| k_mem_slab_init(&mslab, tslab, BLK_SIZE, BLK_NUM); |
| |
| return NULL; |
| } |
| |
| void tmslab_alloc_free(void *data) |
| { |
| struct k_mem_slab *pslab = (struct k_mem_slab *)data; |
| void *block[BLK_NUM]; |
| |
| (void)memset(block, 0, sizeof(block)); |
| /* |
| * TESTPOINT: The memory slab's buffer contains @a slab_num_blocks |
| * memory blocks that are @a slab_block_size bytes long. |
| */ |
| for (int i = 0; i < BLK_NUM; i++) { |
| /* TESTPOINT: Allocate memory from a memory slab.*/ |
| /* TESTPOINT: @retval 0 Memory allocated.*/ |
| zassert_true(k_mem_slab_alloc(pslab, &block[i], K_NO_WAIT) == 0, |
| NULL); |
| /* |
| * TESTPOINT: The block address area pointed at by @a mem is set |
| * to the starting address of the memory block. |
| */ |
| zassert_not_null(block[i], NULL); |
| } |
| for (int i = 0; i < BLK_NUM; i++) { |
| /* TESTPOINT: Free memory allocated from a memory slab.*/ |
| k_mem_slab_free(pslab, &block[i]); |
| } |
| } |
| |
| static void tmslab_alloc_align(void *data) |
| { |
| struct k_mem_slab *pslab = (struct k_mem_slab *)data; |
| void *block[BLK_NUM]; |
| |
| for (int i = 0; i < BLK_NUM; i++) { |
| zassert_true(k_mem_slab_alloc(pslab, &block[i], K_NO_WAIT) == 0, |
| NULL); |
| /* |
| * TESTPOINT: To ensure that each memory block is similarly |
| * aligned to this boundary |
| */ |
| zassert_true((uintptr_t)block[i] % BLK_ALIGN == 0U); |
| } |
| for (int i = 0; i < BLK_NUM; i++) { |
| k_mem_slab_free(pslab, &block[i]); |
| } |
| } |
| |
| static void tmslab_alloc_timeout(void *data) |
| { |
| struct k_mem_slab *pslab = (struct k_mem_slab *)data; |
| void *block[BLK_NUM], *block_fail; |
| int64_t tms; |
| int err; |
| |
| for (int i = 0; i < BLK_NUM; i++) { |
| zassert_true(k_mem_slab_alloc(pslab, &block[i], K_NO_WAIT) == 0, |
| NULL); |
| } |
| |
| /* TESTPOINT: Use K_NO_WAIT to return without waiting*/ |
| /* TESTPOINT: -ENOMEM Returned without waiting.*/ |
| zassert_equal(k_mem_slab_alloc(pslab, &block_fail, K_NO_WAIT), -ENOMEM, |
| NULL); |
| tms = k_uptime_get(); |
| err = k_mem_slab_alloc(pslab, &block_fail, K_MSEC(TIMEOUT)); |
| if (IS_ENABLED(CONFIG_MULTITHREADING)) { |
| /* TESTPOINT: -EAGAIN Waiting period timed out*/ |
| zassert_equal(err, -EAGAIN); |
| /* |
| * TESTPOINT: timeout Maximum time to wait for operation to |
| * complete (in milliseconds) |
| */ |
| zassert_true(k_uptime_delta(&tms) >= TIMEOUT); |
| } else { |
| /* If no multithreading any timeout is treated as K_NO_WAIT */ |
| zassert_equal(err, -ENOMEM); |
| zassert_true(k_uptime_delta(&tms) < TIMEOUT); |
| } |
| |
| for (int i = 0; i < BLK_NUM; i++) { |
| k_mem_slab_free(pslab, &block[i]); |
| } |
| } |
| |
| static void tmslab_used_get(void *data) |
| { |
| struct k_mem_slab *pslab = (struct k_mem_slab *)data; |
| void *block[BLK_NUM], *block_fail; |
| |
| for (int i = 0; i < BLK_NUM; i++) { |
| zassert_true(k_mem_slab_alloc(pslab, &block[i], K_NO_WAIT) == 0, |
| NULL); |
| /* TESTPOINT: Get the number of used blocks in a memory slab.*/ |
| zassert_equal(k_mem_slab_num_used_get(pslab), i + 1); |
| /* |
| * TESTPOINT: Get the number of unused blocks in a memory slab. |
| */ |
| zassert_equal(k_mem_slab_num_free_get(pslab), BLK_NUM - 1 - i); |
| } |
| |
| zassert_equal(k_mem_slab_alloc(pslab, &block_fail, K_NO_WAIT), -ENOMEM, |
| NULL); |
| /* free get on allocation failure*/ |
| zassert_equal(k_mem_slab_num_free_get(pslab), 0); |
| /* used get on allocation failure*/ |
| zassert_equal(k_mem_slab_num_used_get(pslab), BLK_NUM); |
| |
| zassert_equal(k_mem_slab_alloc(pslab, &block_fail, K_MSEC(TIMEOUT)), |
| IS_ENABLED(CONFIG_MULTITHREADING) ? -EAGAIN : -ENOMEM, |
| NULL); |
| zassert_equal(k_mem_slab_num_free_get(pslab), 0); |
| zassert_equal(k_mem_slab_num_used_get(pslab), BLK_NUM); |
| |
| for (int i = 0; i < BLK_NUM; i++) { |
| k_mem_slab_free(pslab, &block[i]); |
| zassert_equal(k_mem_slab_num_free_get(pslab), i + 1); |
| zassert_equal(k_mem_slab_num_used_get(pslab), BLK_NUM - 1 - i); |
| } |
| } |
| |
| static void helper_thread(void *p0, void *p1, void *p2) |
| { |
| void *ptr[BLK_NUM]; /* Pointer to memory block */ |
| |
| ARG_UNUSED(p0); |
| ARG_UNUSED(p1); |
| ARG_UNUSED(p2); |
| |
| (void)memset(ptr, 0, sizeof(ptr)); |
| |
| k_sem_take(&SEM_REGRESSDONE, K_FOREVER); |
| |
| /* Get all blocks from the memory slab */ |
| for (int i = 0; i < BLK_NUM; i++) { |
| /* Verify number of used blocks in the map */ |
| zassert_equal(k_mem_slab_num_used_get(&kmslab), i, |
| "Failed k_mem_slab_num_used_get"); |
| |
| /* Get memory block */ |
| zassert_equal(k_mem_slab_alloc(&kmslab, &ptr[i], K_NO_WAIT), 0, |
| "Failed k_mem_slab_alloc"); |
| } |
| |
| k_sem_give(&SEM_HELPERDONE); |
| |
| k_sem_take(&SEM_REGRESSDONE, K_FOREVER); |
| k_mem_slab_free(&kmslab, &ptr[0]); |
| |
| |
| k_sem_take(&SEM_REGRESSDONE, K_FOREVER); |
| |
| /* Free all the other blocks. The first block are freed by this task */ |
| for (int i = 1; i < BLK_NUM; i++) { |
| k_mem_slab_free(&kmslab, &ptr[i]); |
| } |
| |
| k_sem_give(&SEM_HELPERDONE); |
| |
| } /* helper thread */ |
| |
| /*test cases*/ |
| /** |
| * @brief Initialize the memory slab using k_mem_slab_init() |
| * and allocates/frees blocks. |
| * |
| * @details Initialize 3 memory blocks of block size 8 bytes |
| * using @see k_mem_slab_init() and check if number of used blocks |
| * is 0 and free blocks is equal to number of blocks initialized. |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_kinit) |
| { |
| /* if a block_size is not word aligned, slab init return error */ |
| zassert_equal(k_mem_slab_init(&mslab, tslab, BLK_SIZE + 1, BLK_NUM), |
| -EINVAL, NULL); |
| k_mem_slab_init(&mslab, tslab, BLK_SIZE, BLK_NUM); |
| zassert_equal(k_mem_slab_num_used_get(&mslab), 0); |
| zassert_equal(k_mem_slab_num_free_get(&mslab), BLK_NUM); |
| } |
| |
| /** |
| * @brief Verify K_MEM_SLAB_DEFINE() with allocates/frees blocks. |
| * |
| * @details Initialize 3 memory blocks of block size 8 bytes |
| * using @see K_MEM_SLAB_DEFINE() and check if number of used blocks |
| * is 0 and free blocks is equal to number of blocks initialized. |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_kdefine) |
| { |
| zassert_equal(k_mem_slab_num_used_get(&kmslab), 0); |
| zassert_equal(k_mem_slab_num_free_get(&kmslab), BLK_NUM); |
| } |
| |
| /** |
| * @brief Verify alloc and free of blocks from mem_slab |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_alloc_free_thread) |
| { |
| |
| tmslab_alloc_free(&mslab); |
| } |
| |
| /** |
| * @brief Allocate memory blocks and check for alignment of 8 bytes |
| * |
| * @details Allocate 3 blocks of memory from 2 memory slabs |
| * respectively and check if all blocks are aligned to 8 bytes |
| * and free them. |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_alloc_align) |
| { |
| tmslab_alloc_align(&mslab); |
| tmslab_alloc_align(&kmslab); |
| } |
| |
| /** |
| * @brief Verify allocation of memory blocks with timeouts |
| * |
| * @details Allocate 3 memory blocks from memory slab. Check |
| * allocation of another memory block with NO_WAIT set, since |
| * there are no blocks left to allocate in the memory slab, |
| * the allocation fails with return value -ENOMEM. Then the |
| * system up time is obtained, memory block allocation is |
| * tried with timeout of 2000 ms. Now the allocation API |
| * returns -EAGAIN as the waiting period is timeout. The |
| * test case also checks if timeout has really happened by |
| * checking delta period between the allocation request |
| * was made and return of -EAGAIN. |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_alloc_timeout) |
| { |
| if (arch_num_cpus() != 1) { |
| ztest_test_skip(); |
| } |
| |
| tmslab_alloc_timeout(&mslab); |
| } |
| |
| /** |
| * @brief Verify count of allocated blocks |
| * |
| * @details The test case allocates 3 blocks one after the |
| * other by checking for used block and free blocks in the |
| * memory slab - mslab. Once all 3 blocks are allocated, |
| * one more block is tried to allocates, which fails with |
| * return value -ENOMEM. It also checks the allocation with |
| * timeout. Again checks for used block and free blocks |
| * number using @see k_mem_slab_num_used_get() and |
| * @see k_mem_slab_num_free_get(). |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_used_get) |
| { |
| tmslab_used_get(&mslab); |
| tmslab_used_get(&kmslab); |
| } |
| |
| /** |
| * @brief Verify pending of allocating blocks |
| * |
| * @details First, helper thread got all memory blocks, |
| * and there is no free block left. k_mem_slab_alloc() with |
| * time out will fail and return -EAGAIN. |
| * Then k_mem_slab_alloc() without timeout tries to wait for |
| * a memory block until helper thread free one. |
| * |
| * @ingroup kernel_memory_slab_tests |
| */ |
| ZTEST(mslab_api, test_mslab_pending) |
| { |
| if (!IS_ENABLED(CONFIG_MULTITHREADING)) { |
| ztest_test_skip(); |
| return; |
| } |
| |
| int ret_value; |
| void *b; /* Pointer to memory block */ |
| |
| (void)k_thread_create(&HELPER, stack, STACKSIZE, |
| helper_thread, NULL, NULL, NULL, |
| 7, 0, K_NO_WAIT); |
| |
| k_sem_give(&SEM_REGRESSDONE); /* Allow helper thread to run */ |
| |
| k_sem_take(&SEM_HELPERDONE, K_FOREVER); /* Wait for helper thread to finish */ |
| |
| ret_value = k_mem_slab_alloc(&kmslab, &b, K_MSEC(20)); |
| zassert_equal(-EAGAIN, ret_value, |
| "Failed k_mem_slab_alloc, retValue %d\n", ret_value); |
| |
| k_sem_give(&SEM_REGRESSDONE); |
| |
| /* Wait for helper thread to free a block */ |
| |
| ret_value = k_mem_slab_alloc(&kmslab, &b, K_FOREVER); |
| zassert_equal(0, ret_value, |
| "Failed k_mem_slab_alloc, ret_value %d\n", ret_value); |
| |
| k_sem_give(&SEM_REGRESSDONE); |
| |
| /* Wait for helper thread to complete */ |
| k_sem_take(&SEM_HELPERDONE, K_FOREVER); |
| |
| /* Free memory block */ |
| k_mem_slab_free(&kmslab, &b); |
| } |