/* pool.c - test microkernel memory pool APIs */ /* * Copyright (c) 2012-2014 Wind River Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* DESCRIPTION This modules tests the following memory pool routines: task_mem_pool_alloc(), task_mem_pool_free() */ #include #include #include #define ONE_SECOND (sys_clock_ticks_per_sec) #define TENTH_SECOND (sys_clock_ticks_per_sec / 10) #define NUM_BLOCKS 64 #define DEFRAG_BLK_TEST 2222 typedef struct { struct k_block *block; /* pointer to block data */ kmemory_pool_t poolId; /* pool ID */ int size; /* request size in bytes */ int32_t timeout; /* # of ticks to wait */ int rcode; /* expected return code */ } TEST_CASE; typedef int (*poolBlockGetFunc_t)(struct k_block *, kmemory_pool_t, int, int32_t); typedef int (*poolMoveBlockFunc_t)(struct k_block *, kmemory_pool_t); static volatile int evidence = 0; static struct k_block blockList[NUM_BLOCKS]; static struct k_block helperBlock; static TEST_CASE getSet[] = { {&blockList[0], POOL_ID, 0, 0, RC_OK}, {&blockList[1], POOL_ID, 1, 0, RC_OK}, {&blockList[2], POOL_ID, 32, 0, RC_OK}, {&blockList[3], POOL_ID, 64, 0, RC_OK}, {&blockList[4], POOL_ID, 128, 0, RC_OK}, {&blockList[5], POOL_ID, 256, 0, RC_OK}, {&blockList[6], POOL_ID, 512, 0, RC_OK}, {&blockList[7], POOL_ID, 1024, 0, RC_OK}, {&blockList[8], POOL_ID, 2048, 0, RC_FAIL}, {&blockList[9], POOL_ID, 4096, 0, RC_FAIL} }; static TEST_CASE getSet2[] = { {&blockList[0], POOL_ID, 4096, 0, RC_OK}, {&blockList[1], POOL_ID, 2048, 0, RC_FAIL}, {&blockList[2], POOL_ID, 1024, 0, RC_FAIL}, {&blockList[3], POOL_ID, 512, 0, RC_FAIL}, {&blockList[4], POOL_ID, 256, 0, RC_FAIL} }; static TEST_CASE getwtSet[] = { {&blockList[0], POOL_ID, 4096, TENTH_SECOND, RC_OK}, {&blockList[1], POOL_ID, 2048, TENTH_SECOND, RC_TIME}, {&blockList[2], POOL_ID, 1024, TENTH_SECOND, RC_TIME}, {&blockList[3], POOL_ID, 512, TENTH_SECOND, RC_TIME}, {&blockList[4], POOL_ID, 256, TENTH_SECOND, RC_TIME} }; static TEST_CASE defrag[] = { {&blockList[0], POOL_ID, 64, 0, RC_OK}, {&blockList[1], POOL_ID, 64, 0, RC_OK}, {&blockList[2], POOL_ID, 64, 0, RC_OK}, {&blockList[3], POOL_ID, 64, 0, RC_OK}, {&blockList[4], POOL_ID, 256, 0, RC_OK}, {&blockList[5], POOL_ID, 256, 0, RC_OK}, {&blockList[6], POOL_ID, 256, 0, RC_OK}, {&blockList[7], POOL_ID, 1024, 0, RC_OK}, {&blockList[8], POOL_ID, 1024, 0, RC_OK}, {&blockList[9], POOL_ID, 1024, 0, RC_OK} }; /** * * @brief Compare the two blocks * * @return 0 if the same, non-zero if not the same */ int blockCompare(struct k_block *b1, struct k_block *b2) { char *p1 = (char *) b1; char *p2 = (char *) b2; int i; int diff = 0; for (i = 0; i < sizeof(struct k_block); i++) { diff = p2[i] - p1[i]; if (diff != 0) { break; } } return diff; } /** * * @brief Wrapper for task_mem_pool_alloc() * * @return task_mem_pool_alloc() return value */ int poolBlockGetFunc(struct k_block *block, kmemory_pool_t pool, int size, int32_t unused) { ARG_UNUSED(unused); return task_mem_pool_alloc(block, pool, size, TICKS_NONE); } /** * * @brief Wrapper for task_mem_pool_alloc(TICKS_UNLIMITED) * * @return task_mem_pool_alloc() return value */ int poolBlockGetWFunc(struct k_block *block, kmemory_pool_t pool, int size, int32_t unused) { ARG_UNUSED(unused); return task_mem_pool_alloc(block, pool, size, TICKS_UNLIMITED); } /** * * @brief Wrapper for task_mem_pool_alloc(timeout) * * @return task_mem_pool_alloc(timeout) return value */ int poolBlockGetWTFunc(struct k_block *block, kmemory_pool_t pool, int size, int32_t timeout) { return task_mem_pool_alloc(block, pool, size, timeout); } /** * * @brief Free any blocks allocated in the test set * * @return N/A */ void freeBlocks(TEST_CASE *tests, int nTests) { int i; for (i = 0; i < nTests; i++) { if (tests[i].rcode == RC_OK) { task_mem_pool_free(tests[i].block); } } } /** * * @brief Perform the work of getting blocks * * @return TC_PASS on success, TC_FAIL on failure */ int poolBlockGetWork(char *string, poolBlockGetFunc_t func, TEST_CASE *tests, int nTests) { int i; int rv; for (i = 0; i < nTests; i++) { rv = func(tests[i].block, tests[i].poolId, tests[i].size, tests[i].timeout); if (rv != tests[i].rcode) { TC_ERROR("%s() expected %d, got %d\n" "size: %d, timeout: %d\n", string, tests[i].rcode, rv, tests[i].size, tests[i].timeout); return TC_FAIL; } } return TC_PASS; } /** * * @brief Test the task_mem_pool_alloc(TICKS_NONE) API * * The pool is 4 kB in size. * * @return TC_PASS on success, TC_FAIL on failure */ int poolBlockGetTest(void) { int rv; /* return value from task_mem_pool_alloc() */ int j; /* loop counter */ for (j = 0; j < 8; j++) { rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetFunc, getSet, ARRAY_SIZE(getSet)); if (rv != TC_PASS) { return TC_FAIL; } freeBlocks(getSet, ARRAY_SIZE(getSet)); rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetFunc, getSet2, ARRAY_SIZE(getSet2)); if (rv != TC_PASS) { return TC_FAIL; } freeBlocks(getSet2, ARRAY_SIZE(getSet2)); } return TC_PASS; } /** * * @brief Helper task to poolBlockGetTimeoutTest() * * @return N/A */ void HelperTask(void) { task_sem_take(HELPER_SEM, TICKS_UNLIMITED); task_sem_give(REGRESS_SEM); task_mem_pool_free(&helperBlock); } /** * * @brief Test task_mem_pool_alloc(timeout) * * @return TC_PASS on success, TC_FAIL on failure */ int poolBlockGetTimeoutTest(void) { struct k_block block; int rv; /* return value from task_mem_pool_alloc() */ int j; /* loop counter */ for (j = 0; j < 8; j++) { rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetWTFunc, getwtSet, ARRAY_SIZE(getwtSet)); if (rv != TC_PASS) { return TC_FAIL; } freeBlocks(getwtSet, ARRAY_SIZE(getwtSet)); } rv = task_mem_pool_alloc(&helperBlock, POOL_ID, 3148, 5); if (rv != RC_OK) { TC_ERROR("Failed to get size 3148 byte block from POOL_ID\n"); return TC_FAIL; } rv = task_mem_pool_alloc(&block, POOL_ID, 3148, TICKS_NONE); if (rv != RC_FAIL) { TC_ERROR("Unexpectedly got size 3148 byte block from POOL_ID\n"); return TC_FAIL; } task_sem_give(HELPER_SEM); /* Activate HelperTask */ rv = task_mem_pool_alloc(&block, POOL_ID, 3148, 20); if (rv != RC_OK) { TC_ERROR("Failed to get size 3148 byte block from POOL_ID\n"); return TC_FAIL; } rv = task_sem_take(REGRESS_SEM, TICKS_NONE); if (rv != RC_OK) { TC_ERROR("Failed to get size 3148 byte block within 20 ticks\n"); return TC_FAIL; } task_mem_pool_free(&block); return TC_PASS; } /** * * poolBlockGetWaitTest - * * @return TC_PASS on success, TC_FAIL on failure */ int poolBlockGetWaitTest(void) { int rv; rv = task_mem_pool_alloc(&blockList[0], POOL_ID, 3000, TICKS_UNLIMITED); if (rv != RC_OK) { TC_ERROR("task_mem_pool_alloc(3000) expected %d, got %d\n", RC_OK, rv); return TC_FAIL; } task_sem_give(ALTERNATE_SEM); /* Wake AlternateTask */ evidence = 0; rv = task_mem_pool_alloc(&blockList[1], POOL_ID, 128, TICKS_UNLIMITED); if (rv != RC_OK) { TC_ERROR("task_mem_pool_alloc(128) expected %d, got %d\n", RC_OK, rv); return TC_FAIL; } switch (evidence) { case 0: TC_ERROR("task_mem_pool_alloc(128) did not block!\n"); return TC_FAIL; case 1: break; case 2: default: TC_ERROR("Rescheduling did not occur after task_mem_pool_free()\n"); return TC_FAIL; } task_mem_pool_free(&blockList[1]); return TC_PASS; } /** * * @brief Task responsible for defragmenting the pool POOL_ID * * @return N/A */ void DefragTask(void) { task_sem_take(DEFRAG_SEM, TICKS_UNLIMITED); /* Wait to be activated */ task_mem_pool_defragment(POOL_ID); task_sem_give(REGRESS_SEM); /* DefragTask is finished */ } /** * * poolDefragTest - * * @return TC_PASS on success, TC_FAIL on failure */ int poolDefragTest(void) { int rv; struct k_block newBlock; /* Get a bunch of blocks */ rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetFunc, defrag, ARRAY_SIZE(defrag)); if (rv != TC_PASS) { return TC_FAIL; } task_sem_give(DEFRAG_SEM); /* Activate DefragTask */ /* * Block on getting another block from the pool. * This will allow DefragTask to execute so that we can get some * better code coverage. 50 ticks is expected to more than sufficient * time for DefragTask to finish. */ rv = task_mem_pool_alloc(&newBlock, POOL_ID, DEFRAG_BLK_TEST, 50); if (rv != RC_TIME) { TC_ERROR("task_mem_pool_alloc() returned %d, not %d\n", rv, RC_TIME); return TC_FAIL; } rv = task_sem_take(REGRESS_SEM, TICKS_NONE); if (rv != RC_OK) { TC_ERROR("DefragTask did not finish in allotted time!\n"); return TC_FAIL; } /* Free the allocated blocks */ freeBlocks(defrag, ARRAY_SIZE(defrag)); return TC_PASS; } /** * * @brief Alternate task in the test suite * * This routine runs at a lower priority than RegressionTask(). * * @return N/A */ void AlternateTask(void) { task_sem_take(ALTERNATE_SEM, TICKS_UNLIMITED); evidence = 1; task_mem_pool_free(&blockList[0]); evidence = 2; } /** * * @brief Main task in the test suite * * This is the entry point to the memory pool test suite. * * @return N/A */ void RegressionTask(void) { int tcRC; /* test case return code */ TC_START("Test Microkernel Memory Pools"); TC_PRINT("Testing task_mem_pool_alloc(TICKS_NONE) ...\n"); tcRC = poolBlockGetTest(); if (tcRC != TC_PASS) { goto doneTests; } TC_PRINT("Testing task_mem_pool_alloc(timeout) ...\n"); tcRC = poolBlockGetTimeoutTest(); if (tcRC != TC_PASS) { goto doneTests; } TC_PRINT("Testing task_mem_pool_alloc(TICKS_UNLIMITED) ...\n"); tcRC = poolBlockGetWaitTest(); if (tcRC != TC_PASS) { goto doneTests; } TC_PRINT("Testing task_mem_pool_defragment() ...\n"); tcRC = poolDefragTest(); if (tcRC != TC_PASS) { goto doneTests; } doneTests: TC_END_RESULT(tcRC); TC_END_REPORT(tcRC); }