Linux Audio

Check our new training course

Loading...
/*
 * 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   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.
 *
 * @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("(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
 *
 * @return  TC_PASS, TC_FAIL
 */

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
 *
 * @return  TC_PASS, TC_FAIL
 */

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
 *
 * 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 test_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("(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, 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, 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, K_NO_WAIT);

/*test case main entry*/
void test_main(void)
{
	ztest_test_suite(test_memory_slab,
			ztest_unit_test(test_mslab));
	ztest_run_test_suite(test_memory_slab);
}