/* * Copyright (c) 2012-2014 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 */ /** * @brief Test memory slab APIs * * @defgroup kernel_memory_slab_tests Memory Slab Tests * * @ingroup all_tests * * This module tests the following memory slab routines: * * k_mem_slab_alloc * k_mem_slab_free * k_mem_slab_num_used_get * * @note * One should ensure that the block is released to the same memory slab from * which it was allocated, and is only released once. Using an invalid pointer * will have unpredictable side effects. */ #include #include #include #include /* size of stack area used by each thread */ #define STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACK_SIZE) /* Number of memory blocks. The minimum number of blocks needed to run the * test is 2 */ #define NUMBLOCKS 4 void test_slab_get_all_blocks(void **p); void test_slab_free_all_blocks(void **p); K_SEM_DEFINE(SEM_HELPERDONE, 0, 1); K_SEM_DEFINE(SEM_REGRESSDONE, 0, 1); K_MEM_SLAB_DEFINE(map_lgblks, 1024, NUMBLOCKS, 4); /** * * @brief Helper task * * This routine gets all blocks from the memory slab. It uses semaphores * SEM_REGRESDONE and SEM_HELPERDONE to synchronize between different parts * of the test. * */ void helper_thread(void) { void *ptr[NUMBLOCKS]; /* Pointer to memory block */ (void)memset(ptr, 0, sizeof(ptr)); /* keep static checkers happy */ /* Wait for part 1 to complete */ k_sem_take(&SEM_REGRESSDONE, K_FOREVER); /* Part 2 of test */ TC_PRINT("(2) - Allocate %d blocks in <%s>\n", NUMBLOCKS, __func__); /* Test k_mem_slab_alloc */ test_slab_get_all_blocks(ptr); k_sem_give(&SEM_HELPERDONE); /* Indicate part 2 is complete */ /* Wait for part 3 to complete */ k_sem_take(&SEM_REGRESSDONE, K_FOREVER); /* * Part 4 of test. * Free the first memory block. RegressionTask is currently blocked * waiting (with a timeout) for a memory block. Freeing the memory * block will unblock RegressionTask. */ TC_PRINT("(4) - Free a block in <%s> to unblock the other task " "from alloc timeout\n", __func__); TC_PRINT("%s: About to free a memory block\n", __func__); k_mem_slab_free(&map_lgblks, ptr[0]); k_sem_give(&SEM_HELPERDONE); /* Part 5 of test */ k_sem_take(&SEM_REGRESSDONE, K_FOREVER); TC_PRINT("(5) <%s> freeing the next block\n", __func__); TC_PRINT("%s: About to free another memory block\n", __func__); k_mem_slab_free(&map_lgblks, ptr[1]); /* * Free all the other blocks. The first 2 blocks are freed by this task */ for (int i = 2; i < NUMBLOCKS; i++) { k_mem_slab_free(&map_lgblks, ptr[i]); } TC_PRINT("%s: freed all blocks allocated by this task\n", __func__); k_sem_give(&SEM_HELPERDONE); } /* helper thread */ /** * * @brief Get all blocks from the memory slab * * Get all blocks from the memory slab. It also tries to get one more block * from the map after the map is empty to verify the error return code. * * This routine tests the following: * * k_mem_slab_alloc(), k_mem_slab_num_used_get() * * @param p pointer to pointer of allocated blocks * */ void test_slab_get_all_blocks(void **p) { void *errptr; /* Pointer to block */ for (int i = 0; i < NUMBLOCKS; i++) { /* Verify number of used blocks in the map */ zassert_equal(k_mem_slab_num_used_get(&map_lgblks), i, "Failed k_mem_slab_num_used_get"); /* Get memory block */ zassert_equal(k_mem_slab_alloc(&map_lgblks, &p[i], K_NO_WAIT), 0, "Failed k_mem_slab_alloc"); } /* for */ /* * Verify number of used blocks in the map - expect all blocks are * used */ zassert_equal(k_mem_slab_num_used_get(&map_lgblks), NUMBLOCKS, "Failed k_mem_slab_num_used_get"); /* Try to get one more block and it should fail */ zassert_equal(k_mem_slab_alloc(&map_lgblks, &errptr, K_NO_WAIT), -ENOMEM, "Failed k_mem_slab_alloc"); } /* test_slab_get_all_blocks */ /** * * @brief Free all memory blocks * * This routine frees all memory blocks and also verifies that the number of * blocks used are correct. * * This routine tests the following: * * k_mem_slab_free(&), k_mem_slab_num_used_get(&) * * @param p pointer to pointer of allocated blocks * */ void test_slab_free_all_blocks(void **p) { for (int i = 0; i < NUMBLOCKS; i++) { /* Verify number of used blocks in the map */ zassert_equal(k_mem_slab_num_used_get(&map_lgblks), NUMBLOCKS - i, "Failed k_mem_slab_num_used_get"); TC_PRINT(" block ptr to free p[%d] = %p\n", i, p[i]); /* Free memory block */ k_mem_slab_free(&map_lgblks, p[i]); TC_PRINT("map_lgblks freed %d block\n", i + 1); } /* for */ /* * Verify number of used blocks in the map * - should be 0 as no blocks are used */ zassert_equal(k_mem_slab_num_used_get(&map_lgblks), 0, "Failed k_mem_slab_num_used_get"); } /* test_slab_free_all_blocks */ /** * * @brief Main task to test memory slab interfaces * * @ingroup kernel_memory_slab_tests * * @details Verify that system allows for the definitions of boot-time * memory regions. * This routine calls test_slab_get_all_blocks() to get all * memory blocks from the map and calls test_slab_free_all_blocks() * to free all memory blocks. It also tries to wait (with and without * timeout) for a memory block. * * @see k_mem_slab_alloc(), k_mem_slab_num_used_get(), * memset(), k_mem_slab_free() */ ZTEST(memory_slab_1cpu, test_mslab) { int ret_value; /* task_mem_map_xxx interface return value */ void *b; /* Pointer to memory block */ void *ptr[NUMBLOCKS]; /* Pointer to memory block */ /* not strictly necessary, but keeps coverity checks happy */ (void)memset(ptr, 0, sizeof(ptr)); /* Part 1 of test */ TC_PRINT("(1) - Allocate and free %d blocks " "in <%s>\n", NUMBLOCKS, __func__); /* Test k_mem_slab_alloc */ test_slab_get_all_blocks(ptr); /* Test task_mem_map_free */ test_slab_free_all_blocks(ptr); k_sem_give(&SEM_REGRESSDONE); /* Allow helper thread to run */ /* Wait for helper thread to finish */ k_sem_take(&SEM_HELPERDONE, K_FOREVER); /* * Part 3 of test. * * helper thread got all memory blocks. There is no free block left. * The call will timeout. Note that control does not switch back to * helper thread as it is waiting for SEM_REGRESSDONE. */ TC_PRINT("(3) - Further allocation results in timeout " "in <%s>\n", __func__); ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_MSEC(20)); zassert_equal(-EAGAIN, ret_value, "Failed k_mem_slab_alloc, retValue %d\n", ret_value); TC_PRINT("%s: start to wait for block\n", __func__); k_sem_give(&SEM_REGRESSDONE); /* Allow helper thread to run part 4 */ ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_MSEC(50)); zassert_equal(0, ret_value, "Failed k_mem_slab_alloc, ret_value %d\n", ret_value); /* Wait for helper thread to complete */ k_sem_take(&SEM_HELPERDONE, K_FOREVER); TC_PRINT("%s: start to wait for block\n", __func__); k_sem_give(&SEM_REGRESSDONE); /* Allow helper thread to run part 5 */ ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_FOREVER); zassert_equal(0, ret_value, "Failed k_mem_slab_alloc, ret_value %d\n", ret_value); /* Wait for helper thread to complete */ k_sem_take(&SEM_HELPERDONE, K_FOREVER); /* Free memory block */ TC_PRINT("%s: Used %d block\n", __func__, k_mem_slab_num_used_get(&map_lgblks)); k_mem_slab_free(&map_lgblks, b); TC_PRINT("%s: 1 block freed, used %d block\n", __func__, k_mem_slab_num_used_get(&map_lgblks)); } K_THREAD_DEFINE(HELPER, STACKSIZE, helper_thread, NULL, NULL, NULL, 7, 0, 0); /*test case main entry*/ ZTEST_SUITE(memory_slab_1cpu, NULL, NULL, ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);