blob: b7d71db438dcbc3d45083dd86bc5a6bb87fe22d4 [file] [log] [blame]
/*
* 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);
}