| /* |
| * Copyright (c) 2012-2014 Wind River Systems, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Test memory slab APIs |
| * |
| * 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 <tc_util.h> |
| #include <stdbool.h> |
| #include <zephyr.h> |
| #include <ztest.h> |
| |
| /* size of stack area used by each thread */ |
| #define STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE) |
| |
| /* Number of memory blocks. The minimum number of blocks needed to run the |
| * test is 2 |
| */ |
| #define NUMBLOCKS 2 |
| |
| static int tc_rc = TC_PASS; /* test case return code */ |
| |
| int test_slab_get_all_blocks(void **P); |
| int 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 Verify return value |
| * |
| * This routine verifies current value against expected value |
| * and returns true if they are the same. |
| * |
| * @param expect_ret_value expect value |
| * @param current_ret_current current value |
| * |
| * @return true, false |
| */ |
| |
| bool verify_ret_value(int expect_ret_value, int current_ret_current) |
| { |
| return (expect_ret_value == current_ret_current); |
| |
| } /* verify_ret_value */ |
| |
| /** |
| * |
| * @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. |
| * |
| * @return N/A |
| */ |
| |
| void helper_thread(void) |
| { |
| void *ptr[NUMBLOCKS]; /* Pointer to memory block */ |
| |
| 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("Starts %s\n", __func__); |
| PRINT_LINE; |
| TC_PRINT("(2) - Allocate %d blocks in <%s>\n", NUMBLOCKS, __func__); |
| PRINT_LINE; |
| |
| /* Test k_mem_slab_alloc */ |
| tc_rc = test_slab_get_all_blocks(ptr); |
| if (tc_rc == TC_FAIL) { |
| TC_ERROR("Failed test_slab_get_all_blocks function\n"); |
| goto exittest1; /* terminate test */ |
| } |
| |
| 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. |
| */ |
| PRINT_LINE; |
| TC_PRINT("(4) - Free a block in <%s> to unblock the other task " |
| "from alloc timeout\n", __func__); |
| PRINT_LINE; |
| |
| 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); |
| PRINT_LINE; |
| TC_PRINT("(5) <%s> freeing the next block\n", __func__); |
| PRINT_LINE; |
| 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__); |
| |
| exittest1: |
| |
| TC_END_RESULT(tc_rc); |
| 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 |
| * |
| * @return TC_PASS, TC_FAIL |
| */ |
| |
| int test_slab_get_all_blocks(void **p) |
| { |
| int ret_value; /* task_mem_map_xxx interface return value */ |
| void *errptr; /* Pointer to block */ |
| |
| TC_PRINT("Function %s\n", __func__); |
| |
| for (int i = 0; i < NUMBLOCKS; i++) { |
| /* Verify number of used blocks in the map */ |
| ret_value = k_mem_slab_num_used_get(&map_lgblks); |
| if (verify_ret_value(i, ret_value)) { |
| TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value); |
| } else { |
| TC_ERROR("Failed task_mem_map_used_get for " |
| "MAP_LgBlks, i=%d, retValue=%d\n", |
| i, ret_value); |
| return TC_FAIL; |
| } |
| |
| /* Get memory block */ |
| ret_value = k_mem_slab_alloc(&map_lgblks, &p[i], K_NO_WAIT); |
| if (verify_ret_value(0, ret_value)) { |
| TC_PRINT(" k_mem_slab_alloc OK, p[%d] = %p\n", |
| i, p[i]); |
| } else { |
| TC_ERROR("Failed k_mem_slab_alloc, i=%d, " |
| "ret_value %d\n", i, ret_value); |
| return TC_FAIL; |
| } |
| |
| } /* for */ |
| |
| /* Verify number of used blocks in the map - expect all blocks are |
| * used |
| */ |
| ret_value = k_mem_slab_num_used_get(&map_lgblks); |
| if (verify_ret_value(NUMBLOCKS, ret_value)) { |
| TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value); |
| } else { |
| TC_ERROR("Failed task_mem_map_used_get for MAP_LgBlks, " |
| "retValue %d\n", ret_value); |
| return TC_FAIL; |
| } |
| |
| /* Try to get one more block and it should fail */ |
| ret_value = k_mem_slab_alloc(&map_lgblks, &errptr, K_NO_WAIT); |
| if (verify_ret_value(-ENOMEM, ret_value)) { |
| TC_PRINT(" k_mem_slab_alloc RC_FAIL expected as all (%d) " |
| "blocks are used.\n", NUMBLOCKS); |
| } else { |
| TC_ERROR("Failed k_mem_slab_alloc, expect RC_FAIL, got %d\n", |
| ret_value); |
| return TC_FAIL; |
| } |
| |
| PRINT_LINE; |
| |
| return TC_PASS; |
| } /* 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 |
| * |
| * @return TC_PASS, TC_FAIL |
| */ |
| |
| int test_slab_free_all_blocks(void **p) |
| { |
| int ret_value; /* task_mem_map_xxx interface return value */ |
| |
| TC_PRINT("Function %s\n", __func__); |
| |
| for (int i = 0; i < NUMBLOCKS; i++) { |
| /* Verify number of used blocks in the map */ |
| ret_value = k_mem_slab_num_used_get(&map_lgblks); |
| if (verify_ret_value(NUMBLOCKS - i, ret_value)) { |
| TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value); |
| } else { |
| TC_ERROR("Failed task_mem_map_used_get for " |
| "MAP_LgBlks, expect %d, got %d\n", |
| NUMBLOCKS - i, ret_value); |
| return TC_FAIL; |
| } |
| |
| 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 |
| */ |
| |
| ret_value = k_mem_slab_num_used_get(&map_lgblks); |
| if (verify_ret_value(0, ret_value)) { |
| TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value); |
| } else { |
| TC_ERROR("Failed task_mem_map_used_get for MAP_LgBlks, " |
| "retValue %d\n", ret_value); |
| return TC_FAIL; |
| } |
| |
| PRINT_LINE; |
| return TC_PASS; |
| } /* testSlabFreeAllBlocks */ |
| |
| /** |
| * |
| * @brief Print the pointers |
| * |
| * This routine prints out the pointers. |
| * |
| * @param pointer pointer to pointer of allocated blocks |
| * |
| * @return N/A |
| */ |
| void print_pointers(void **pointer) |
| { |
| TC_PRINT("%s: ", __func__); |
| for (int i = 0; i < NUMBLOCKS; i++) { |
| TC_PRINT("p[%d] = %p, ", i, pointer[i]); |
| } |
| |
| TC_PRINT("\n"); |
| PRINT_LINE; |
| |
| } /* print_pointers */ |
| |
| /** |
| * |
| * @brief Main task to test memory slab interfaces |
| * |
| * 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. |
| * |
| * This routine tests the following: |
| * |
| * k_mem_slab_alloc |
| * |
| * @return N/A |
| */ |
| |
| void testing_mslab(void) |
| { |
| 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 */ |
| memset(ptr, 0, sizeof(ptr)); |
| |
| /* Part 1 of test */ |
| |
| TC_PRINT("Starts %s\n", __func__); |
| PRINT_LINE; |
| TC_PRINT("(1) - Allocate and free %d blocks " |
| "in <%s>\n", NUMBLOCKS, __func__); |
| PRINT_LINE; |
| |
| /* Test k_mem_slab_alloc */ |
| tc_rc = test_slab_get_all_blocks(ptr); |
| zassert_not_equal(tc_rc, TC_FAIL, |
| "Failed test_slab_get_all_blocks function\n"); |
| |
| print_pointers(ptr); |
| /* Test task_mem_map_free */ |
| tc_rc = test_slab_free_all_blocks(ptr); |
| zassert_not_equal(tc_rc, TC_FAIL, |
| "Failed testalab_freeall_blocks function\n"); |
| |
| 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. |
| */ |
| |
| PRINT_LINE; |
| TC_PRINT("(3) - Further allocation results in timeout " |
| "in <%s>\n", __func__); |
| PRINT_LINE; |
| |
| ret_value = k_mem_slab_alloc(&map_lgblks, &b, 20); |
| zassert_true(verify_ret_value(-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, 50); |
| zassert_true(verify_ret_value(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_true(verify_ret_value(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, K_NO_WAIT); |
| |
| /*test case main entry*/ |
| void test_main(void) |
| { |
| ztest_test_suite(test_mslab, ztest_unit_test(testing_mslab)); |
| ztest_run_test_suite(test_mslab); |
| } |