| /* memory pool kernel services */ |
| |
| /* |
| * Copyright (c) 1997-2010, 2013-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. |
| */ |
| |
| #include <microkernel.h> |
| #include <micro_private.h> |
| #include <toolchain.h> |
| #include <sections.h> |
| |
| /* Auto-Defrag settings */ |
| |
| #define AD_NONE 0 |
| #define AD_BEFORE_SEARCH4BIGGERBLOCK 1 |
| #define AD_AFTER_SEARCH4BIGGERBLOCK 2 |
| |
| #define AUTODEFRAG AD_AFTER_SEARCH4BIGGERBLOCK |
| |
| /** |
| * |
| * @brief Initialize kernel memory pool subsystem |
| * |
| * Perform any initialization of memory pool that wasn't done at build time. |
| * |
| * @return N/A |
| */ |
| void _k_mem_pool_init(void) |
| { |
| int i, j, k; |
| struct pool_struct *P; |
| char *memptr; |
| |
| /* for all pools initialise largest blocks */ |
| for (i = 0, P = _k_mem_pool_list; i < _k_mem_pool_count; i++, P++) { |
| int remaining = P->nr_of_maxblocks; |
| int t = 0; |
| |
| /* initialise block-arrays */ |
| for (k = 0; k < P->nr_of_frags; k++) { |
| P->frag_tab[k].count = 0; |
| for (j = 0; j < P->frag_tab[k].nr_of_entries; j++) { |
| P->frag_tab[k].blocktable[j].mem_blocks = NULL; |
| P->frag_tab[k].blocktable[j].mem_status = |
| 0xF; /* all blocks in use */ |
| } |
| } |
| |
| memptr = P->bufblock; |
| |
| while (remaining >= 4) { /* while not all blocks allocated */ |
| /* - put pointer in table - */ |
| P->frag_tab[0].blocktable[t].mem_blocks = memptr; |
| P->frag_tab[0].blocktable[t].mem_status = |
| 0; /* all blocks initial free */ |
| |
| t++; /* next entry in table */ |
| remaining = remaining - 4; |
| memptr += |
| OCTET_TO_SIZEOFUNIT(P->frag_tab[0].block_size) * |
| 4; |
| } |
| |
| if (remaining != 0) { |
| |
| /* - put pointer in table - */ |
| P->frag_tab[0].blocktable[t].mem_blocks = memptr; |
| P->frag_tab[0].blocktable[t].mem_status = |
| (0xF << remaining) & |
| 0xF; /* mark unaccessible blocks as used */ |
| } |
| } |
| } |
| |
| /** |
| * |
| * @brief ??? |
| * |
| * marks ptr as free block in the given list [MYSTERIOUS LEGACY COMMENT] |
| * |
| * @return N/A |
| */ |
| static void search_bp(char *ptr, struct pool_struct *P, int index) |
| { |
| int i = 0, j, block_found = 0; |
| |
| while ((P->frag_tab[index].blocktable[i].mem_blocks != NULL) && |
| (!block_found)) { |
| for (j = 0; j < 4; j++) { |
| if (ptr == |
| ((P->frag_tab[index].blocktable[i].mem_blocks) + |
| (OCTET_TO_SIZEOFUNIT( |
| j * P->frag_tab[index].block_size)))) { |
| /* then we've found the right pointer */ |
| block_found = 1; |
| |
| /* so free it */ |
| P->frag_tab[index].blocktable[i].mem_status = |
| P->frag_tab[index] |
| .blocktable[i] |
| .mem_status & |
| (~(1 << j)); |
| } |
| } |
| i++; |
| } |
| } |
| |
| /** |
| * |
| * @brief Defragmentation algorithm for memory pool |
| * |
| * @return N/A |
| */ |
| static void defrag(struct pool_struct *P, |
| int ifraglevel_start, |
| int ifraglevel_stop) |
| { |
| int i, j, k; |
| |
| j = ifraglevel_start; |
| |
| while (j > ifraglevel_stop) { |
| i = 0; |
| while (P->frag_tab[j].blocktable[i].mem_blocks != NULL) { |
| if ((P->frag_tab[j].blocktable[i].mem_status & 0xF) == |
| 0) { /* blocks for defragmenting */ |
| search_bp( |
| P->frag_tab[j].blocktable[i].mem_blocks, |
| P, |
| j - 1); |
| |
| /* remove blocks & compact list */ |
| k = i; |
| while ((P->frag_tab[j] |
| .blocktable[k] |
| .mem_blocks != NULL) && |
| (k < |
| (P->frag_tab[j].nr_of_entries - 1))) { |
| P->frag_tab[j] |
| .blocktable[k] |
| .mem_blocks = |
| P->frag_tab[j] |
| .blocktable[k + 1] |
| .mem_blocks; |
| P->frag_tab[j] |
| .blocktable[k] |
| .mem_status = |
| P->frag_tab[j] |
| .blocktable[k + 1] |
| .mem_status; |
| k++; |
| } |
| P->frag_tab[j].blocktable[k].mem_blocks = NULL; |
| P->frag_tab[j].blocktable[k].mem_status = 0; |
| } else { |
| i++; /* take next block */ |
| } |
| } |
| j--; |
| } |
| } |
| |
| /** |
| * |
| * @brief Perform defragment memory pool request |
| * |
| * @return N/A |
| */ |
| void _k_defrag(struct k_args *A) |
| { |
| struct pool_struct *P = _k_mem_pool_list + OBJ_INDEX(A->args.p1.pool_id); |
| |
| defrag(P, |
| P->nr_of_frags - 1, /* start from smallest blocks */ |
| 0 /* and defragment till fragment level 0 */ |
| ); |
| |
| /* reschedule waiters */ |
| |
| if ( |
| P->waiters) { |
| struct k_args *NewGet; |
| |
| /* |
| * get new command packet that calls the function |
| * that reallocate blocks for the waiting tasks |
| */ |
| GETARGS(NewGet); |
| *NewGet = *A; |
| NewGet->Comm = _K_SVC_BLOCK_WAITERS_GET; |
| TO_ALIST(&_k_command_stack, NewGet); /*push on command stack */ |
| } |
| } |
| |
| |
| void task_mem_pool_defragment(kmemory_pool_t Pid) |
| { |
| struct k_args A; |
| |
| A.Comm = _K_SVC_DEFRAG; |
| A.args.p1.pool_id = Pid; |
| KERNEL_ENTRY(&A); |
| } |
| |
| /** |
| * |
| * @brief Allocate block using specified fragmentation level |
| * |
| * This routine attempts to allocate a free block. [NEED TO EXPAND THIS] |
| * |
| * @return pointer to allocated block, or NULL if none available |
| */ |
| static char *search_block_on_frag_level(struct pool_block *pfraglevelinfo, |
| int *piblockindex) |
| { |
| int i, status, end_of_list; |
| char *found; |
| |
| i = 0; |
| end_of_list = 0; |
| found = NULL; |
| |
| while (!end_of_list) {/* search list */ |
| |
| if (pfraglevelinfo->blocktable[i].mem_blocks == NULL) { |
| end_of_list = 1; |
| } else { |
| status = pfraglevelinfo->blocktable[i].mem_status; |
| if (!(status & 1)) { |
| found = pfraglevelinfo->blocktable[i] |
| .mem_blocks; |
| pfraglevelinfo->blocktable[i].mem_status |= |
| 1; /* set status used */ |
| #ifdef CONFIG_OBJECT_MONITOR |
| pfraglevelinfo->count++; |
| #endif |
| break; |
| } else if (!(status & 2)) { |
| found = pfraglevelinfo->blocktable[i] |
| .mem_blocks + |
| (OCTET_TO_SIZEOFUNIT( |
| pfraglevelinfo->block_size)); |
| pfraglevelinfo->blocktable[i].mem_status |= |
| 2; /* set status used */ |
| #ifdef CONFIG_OBJECT_MONITOR |
| pfraglevelinfo->count++; |
| #endif |
| break; |
| } else if (!(status & 4)) { |
| found = pfraglevelinfo->blocktable[i] |
| .mem_blocks + |
| (OCTET_TO_SIZEOFUNIT( |
| 2 * |
| pfraglevelinfo->block_size)); |
| pfraglevelinfo->blocktable[i].mem_status |= |
| 4; /* set status used */ |
| #ifdef CONFIG_OBJECT_MONITOR |
| pfraglevelinfo->count++; |
| #endif |
| break; |
| } else if (!(status & 8)) { |
| found = pfraglevelinfo->blocktable[i] |
| .mem_blocks + |
| (OCTET_TO_SIZEOFUNIT( |
| 3 * |
| pfraglevelinfo->block_size)); |
| pfraglevelinfo->blocktable[i].mem_status |= |
| 8; /* set status used */ |
| #ifdef CONFIG_OBJECT_MONITOR |
| pfraglevelinfo->count++; |
| #endif |
| break; |
| } |
| |
| i++; |
| if (i >= pfraglevelinfo->nr_of_entries) { |
| end_of_list = 1; |
| } |
| } |
| } /* while (...) */ |
| |
| *piblockindex = i; |
| return found; |
| } |
| |
| /** |
| * |
| * @brief Recursively get a block, doing fragmentation if necessary |
| * |
| * [NEED A BETTER DESCRIPTION HERE] |
| * |
| * not implemented: check if we go below the minimal number of blocks with |
| * the maximum size |
| * |
| * @return pointer to allocated block, or NULL if none available |
| */ |
| static char *get_block_recusive(struct pool_struct *P, int index, int startindex) |
| { |
| int i; |
| char *found, *larger_block; |
| struct pool_block *fr_table; |
| |
| if (index < 0) { |
| return NULL; /* no more free blocks in pool */ |
| } |
| |
| fr_table = P->frag_tab; |
| i = 0; |
| |
| /* let's inspect the fragmentation level <index> */ |
| found = search_block_on_frag_level(&(fr_table[index]), &i); |
| if (found != NULL) { |
| return found; |
| } |
| |
| #if AUTODEFRAG == AD_BEFORE_SEARCH4BIGGERBLOCK |
| /* maybe a partial defrag will free up a block? */ |
| if (index == startindex) { /* only for the original request */ |
| defrag(P, |
| P->nr_of_frags - 1, /* start from the smallest blocks */ |
| startindex); /* but only until the requested blocksize |
| * (fragmentation level) !! |
| */ |
| |
| found = search_block_on_frag_level(&(fr_table[index]), &i); |
| if (found != NULL) { |
| return found; |
| } |
| } |
| /* partial defrag did not release a block of level <index> */ |
| #endif |
| |
| /* end of list and i is index of first empty entry in blocktable */ |
| { |
| /* get a block of one size larger */ |
| larger_block = get_block_recusive( |
| P, index - 1, startindex); |
| } |
| |
| if (larger_block != NULL) { |
| /* insert 4 found blocks and mark one block as used */ |
| fr_table[index].blocktable[i].mem_blocks = larger_block; |
| fr_table[index].blocktable[i].mem_status = 1; |
| #ifdef CONFIG_OBJECT_MONITOR |
| fr_table[index].count++; |
| #endif |
| return larger_block; /* return marked block */ |
| } |
| |
| /* trying to get a larger block did not succeed */ |
| |
| #if AUTODEFRAG == AD_AFTER_SEARCH4BIGGERBLOCK |
| /* maybe a partial defrag will free up a block? */ |
| if (index == startindex) { /* only for the original request */ |
| defrag(P, |
| P->nr_of_frags - 1, /* start from the smallest blocks */ |
| startindex); /* but only until the requested blocksize |
| * (fragmentation level) !! |
| */ |
| |
| found = search_block_on_frag_level(&(fr_table[index]), &i); |
| if (found != NULL) { |
| return found; |
| } |
| } |
| /* partial defrag did not release a block of level <index> */ |
| #endif |
| |
| return NULL; /* now we have to report failure: no block available */ |
| } |
| |
| /** |
| * |
| * @brief Examine tasks that are waiting for memory pool blocks |
| * |
| * This routine attempts to satisfy any incomplete block allocation requests for |
| * the specified memory pool. It can be invoked either by the explicit freeing |
| * of a used block or as a result of defragmenting the pool (which may create |
| * one or more new, larger blocks). |
| * |
| * @return N/A |
| */ |
| void _k_block_waiters_get(struct k_args *A) |
| { |
| struct pool_struct *P = _k_mem_pool_list + OBJ_INDEX(A->args.p1.pool_id); |
| char *found_block; |
| struct k_args *curr_task, *prev_task; |
| int start_size, offset; |
| |
| curr_task = P->waiters; |
| /* forw is first field in struct */ |
| prev_task = (struct k_args *)&(P->waiters); |
| |
| /* loop all waiters */ |
| while (curr_task != NULL) { |
| |
| /* calculate size & offset */ |
| start_size = P->minblock_size; |
| offset = P->nr_of_frags - 1; |
| while (curr_task->args.p1.req_size > start_size) { |
| start_size = start_size << 2; /* try one larger */ |
| offset--; |
| } |
| |
| /* allocate block */ |
| found_block = get_block_recusive( |
| P, offset, offset); /* allocate and fragment blocks */ |
| |
| /* if success : remove task from list and reschedule */ |
| if (found_block != NULL) { |
| /* return found block */ |
| curr_task->args.p1.rep_poolptr = found_block; |
| curr_task->args.p1.rep_dataptr = found_block; |
| |
| /* reschedule task */ |
| |
| #ifdef CONFIG_SYS_CLOCK_EXISTS |
| if (curr_task->Time.timer) { |
| _k_timeout_free(curr_task->Time.timer); |
| } |
| #endif |
| curr_task->Time.rcode = RC_OK; |
| _k_state_bit_reset(curr_task->Ctxt.task, TF_GTBL); |
| |
| /* remove from list */ |
| prev_task->next = curr_task->next; |
| |
| /* and get next task */ |
| curr_task = curr_task->next; |
| |
| } else { |
| /* else just get next task */ |
| prev_task = curr_task; |
| curr_task = curr_task->next; |
| } |
| } |
| |
| /* put used command packet on the empty packet list */ |
| FREEARGS(A); |
| } |
| |
| /** |
| * |
| * @brief Finish handling an allocate block request that timed out |
| * |
| * @return N/A |
| */ |
| void _k_mem_pool_block_get_timeout_handle(struct k_args *A) |
| { |
| _k_timeout_free(A->Time.timer); |
| REMOVE_ELM(A); |
| A->Time.rcode = RC_TIME; |
| _k_state_bit_reset(A->Ctxt.task, TF_GTBL); |
| } |
| |
| /** |
| * |
| * @brief Perform allocate memory pool block request |
| * |
| * @return N/A |
| */ |
| void _k_mem_pool_block_get(struct k_args *A) |
| { |
| struct pool_struct *P = _k_mem_pool_list + OBJ_INDEX(A->args.p1.pool_id); |
| char *found_block; |
| |
| int start_size; |
| int offset; |
| |
| /* calculate start size */ |
| start_size = P->minblock_size; |
| offset = P->nr_of_frags - 1; |
| |
| while (A->args.p1.req_size > start_size) { |
| start_size = start_size << 2; /*try one larger */ |
| offset--; |
| } |
| |
| /* startsize==the available size that can contain the requeste block size */ |
| /* offset: index in fragtable of the minimal blocksize */ |
| |
| found_block = |
| get_block_recusive(P, offset, offset); /* allocate and fragment blocks */ |
| |
| if (found_block != NULL) { |
| A->args.p1.rep_poolptr = found_block; |
| A->args.p1.rep_dataptr = found_block; |
| A->Time.rcode = RC_OK; |
| return; /* return found block */ |
| } |
| |
| if (likely( |
| (A->Time.ticks != TICKS_NONE) && |
| (A->args.p1.req_size <= |
| P->maxblock_size))) {/* timeout? but not block to large */ |
| A->priority = _k_current_task->priority; |
| A->Ctxt.task = _k_current_task; |
| _k_state_bit_set(_k_current_task, TF_GTBL); /* extra new statebit */ |
| |
| /* INSERT_ELM (P->frag_tab[offset].waiters, A); */ |
| INSERT_ELM(P->waiters, A); |
| |
| #ifdef CONFIG_SYS_CLOCK_EXISTS |
| if (A->Time.ticks == TICKS_UNLIMITED) { |
| A->Time.timer = NULL; |
| } else { |
| A->Comm = _K_SVC_MEM_POOL_BLOCK_GET_TIMEOUT_HANDLE; |
| _k_timeout_alloc(A); |
| } |
| #endif |
| } else { |
| A->Time.rcode = |
| RC_FAIL; /* no blocks available or block too large */ |
| } |
| } |
| |
| /** |
| * |
| * @brief Allocate memory pool block request |
| * |
| * This routine allocates a free block from the specified memory pool, ensuring |
| * that its size is at least as big as the size requested (in bytes). |
| * |
| * @param blockptr poitner to requested block |
| * @param pool_id pool from which to get block |
| * @param reqsize requested block size |
| * @param time maximum number of ticks to wait |
| * |
| * @return RC_OK, RC_FAIL, RC_TIME on success, failure, timeout respectively |
| */ |
| int _task_mem_pool_alloc(struct k_block *blockptr, kmemory_pool_t pool_id, |
| int reqsize, int32_t time) |
| { |
| struct k_args A; |
| |
| A.Comm = _K_SVC_MEM_POOL_BLOCK_GET; |
| A.Time.ticks = time; |
| A.args.p1.pool_id = pool_id; |
| A.args.p1.req_size = reqsize; |
| |
| KERNEL_ENTRY(&A); |
| |
| blockptr->pool_id = pool_id; |
| blockptr->address_in_pool = A.args.p1.rep_poolptr; |
| blockptr->pointer_to_data = A.args.p1.rep_dataptr; |
| blockptr->req_size = reqsize; |
| |
| return A.Time.rcode; |
| } |
| |
| /** |
| * |
| * @brief Perform return memory pool block request |
| * |
| * Marks a block belonging to a pool as free; if there are waiters that can use |
| * the the block it is passed to a waiting task. |
| * |
| * @return N/A |
| */ |
| void _k_mem_pool_block_release(struct k_args *A) |
| { |
| struct pool_struct *P; |
| struct pool_block *block; |
| struct block_stat *blockstat; |
| int Pid; |
| int start_size, offset; |
| int i, j; |
| |
| Pid = A->args.p1.pool_id; |
| |
| |
| P = _k_mem_pool_list + OBJ_INDEX(Pid); |
| |
| /* calculate size */ |
| start_size = P->minblock_size; |
| offset = P->nr_of_frags - 1; |
| |
| while (A->args.p1.req_size > start_size) { |
| start_size = start_size << 2; /* try one larger */ |
| offset--; |
| } |
| |
| /* startsize==the available size that contains the requested block size */ |
| /* offset: index in fragtable of the block */ |
| |
| j = 0; |
| block = P->frag_tab + offset; |
| |
| while ((j < block->nr_of_entries) && |
| ((blockstat = block->blocktable + j)->mem_blocks != 0)) { |
| for (i = 0; i < 4; i++) { |
| if (A->args.p1.rep_poolptr == |
| (blockstat->mem_blocks + |
| (OCTET_TO_SIZEOFUNIT(i * block->block_size)))) { |
| /* we've found the right pointer, so free it */ |
| blockstat->mem_status &= ~(1 << i); |
| |
| /* waiters? */ |
| if (P->waiters != NULL) { |
| struct k_args *NewGet; |
| /* |
| * get new command packet that calls |
| * the function that reallocate blocks |
| * for the waiting tasks |
| */ |
| GETARGS(NewGet); |
| *NewGet = *A; |
| NewGet->Comm = _K_SVC_BLOCK_WAITERS_GET; |
| /* push on command stack */ |
| TO_ALIST(&_k_command_stack, NewGet); |
| } |
| if (A->alloc) { |
| FREEARGS(A); |
| } |
| return; |
| } |
| } |
| j++; |
| } |
| } |
| |
| void task_mem_pool_free(struct k_block *blockptr) |
| { |
| struct k_args A; |
| |
| A.Comm = _K_SVC_MEM_POOL_BLOCK_RELEASE; |
| A.args.p1.pool_id = blockptr->pool_id; |
| A.args.p1.req_size = blockptr->req_size; |
| A.args.p1.rep_poolptr = blockptr->address_in_pool; |
| A.args.p1.rep_dataptr = blockptr->pointer_to_data; |
| KERNEL_ENTRY(&A); |
| } |