/* memory pool kernel services */ /* * Copyright (c) 1997-2010, 2013-2014 Wind River Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3) Neither the name of Wind River Systems nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include /* Auto-Defrag settings */ #define AD_NONE 0 #define AD_BEFORE_SEARCH4BIGGERBLOCK 1 #define AD_AFTER_SEARCH4BIGGERBLOCK 2 #define AUTODEFRAG AD_AFTER_SEARCH4BIGGERBLOCK /******************************************************************************* * * InitPools - initialize kernel memory pool subsystem * * Perform any initialization of memory pool that wasn't done at build time. * * RETURNS: N/A */ void InitPools(void) { int i, j, k; struct pool_struct *P; char *memptr; /* for all pools initialise largest blocks */ for (i = 0, P = K_PoolList; i < K_PoolCount; 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 */ } } } /******************************************************************************* * * search_bp - ??? * * marks ptr as free block in the given list [MYSTERIOUS LEGACY COMMENT] * * RETURNS: 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++; } } /******************************************************************************* * * defrag - defragmentation algorithm for local memory pool * * RETURNS: 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--; } } /******************************************************************************* * * K_Defrag - perform defragment local memory pool request * * RETURNS: N/A */ void K_Defrag(struct k_args *A) { struct pool_struct *P = K_PoolList + OBJ_INDEX(A->Args.p1.poolid); defrag(P, P->nr_of_frags - 1, /* start from smallest blocks */ 0 /* and defragment till fragment level 0 */ ); /* reschedule waiters, everything happens locally */ 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 = GET_BLOCK_WAIT; TO_ALIST(&K_Args, NewGet); /*push on command stack */ } } /******************************************************************************* * * task_mem_pool_defragment - defragment local memory pool request * * This routine concatenates unused memory in a local memory pool. * (Must not be called for a remote memory pool or a system crash may result.) * * RETURNS: N/A */ void task_mem_pool_defragment(kmemory_pool_t Pid /* pool to defragment */ ) { struct k_args A; A.Comm = POOL_DEFRAG; A.Args.p1.poolid = Pid; KERNEL_ENTRY(&A); } /******************************************************************************* * * searchblock_onfraglevel - allocate block using specified fragmentation level * * This routine attempts to allocate a free block. [NEED TO EXPAND THIS] * * RETURNS: pointer to allocated block, or NULL if none available */ static char *searchblock_onfraglevel(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; } /******************************************************************************* * * rgetblock - 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 * * RETURNS: pointer to allocated block, or NULL if none available */ static char *rgetblock(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 */ found = searchblock_onfraglevel(&(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 = searchblock_onfraglevel(&(fr_table[index]), &i); if (found != NULL) { return found; } } /* partial defrag did not release a block of level */ #endif /* end of list and i is index of first empty entry in blocktable */ { larger_block = rgetblock( P, index - 1, startindex); /* get a block of one size larger */ } 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 = searchblock_onfraglevel(&(fr_table[index]), &i); if (found != NULL) { return found; } } /* partial defrag did not release a block of level */ #endif return NULL; /* now we have to report failure: no block available */ } /******************************************************************************* * * K_GetBlock_Waiters - examine tasks that are waiting for memory pool blocks * * This routine attempts to satisfy any incomplete block allocation requests * for the specified local 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). * * RETURNS: N/A */ void K_GetBlock_Waiters(struct k_args *A) { struct pool_struct *P = K_PoolList + OBJ_INDEX(A->Args.p1.poolid); char *found_block; struct k_args *curr_task, *prev_task; int start_size, offset; curr_task = P->Waiters; prev_task = (struct k_args *)&(P->Waiters); /* forw is first field in struct */ /* 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 = rgetblock( 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 */ if (curr_task->Time.timer) { delist_timeout(curr_task->Time.timer); } curr_task->Time.rcode = RC_OK; reset_state_bit(curr_task->Ctxt.proc, TF_GTBL); /* remove from list */ prev_task->Forw = curr_task->Forw; /* and get next task */ curr_task = curr_task->Forw; } else { /* else just get next task */ prev_task = curr_task; curr_task = curr_task->Forw; } } /* put used command packet on the empty packet list */ FREEARGS(A); } /******************************************************************************* * * K_gtbltmo - finish handling an allocate block request that timed out * * RETURNS: N/A */ void K_gtbltmo(struct k_args *A) { delist_timeout(A->Time.timer); REMOVE_ELM(A); A->Time.rcode = RC_TIME; reset_state_bit(A->Ctxt.proc, TF_GTBL); } /******************************************************************************* * * K_GetBlock - perform allocate memory pool block request * * RETURNS: N/A */ void K_GetBlock(struct k_args *A) { struct pool_struct *P = K_PoolList + OBJ_INDEX(A->Args.p1.poolid); 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 = rgetblock(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->Prio = K_Task->Prio; A->Ctxt.proc = K_Task; set_state_bit(K_Task, TF_GTBL); /* extra new statebit */ /* INSERT_ELM (P->frag_tab[offset].Waiters, A); */ INSERT_ELM(P->Waiters, A); if (A->Time.ticks == TICKS_UNLIMITED) { A->Time.timer = NULL; } else { A->Comm = GTBLTMO; enlist_timeout(A); } } else { A->Time.rcode = RC_FAIL; /* no blocks available or block too large */ } } /******************************************************************************* * * _task_mem_pool_alloc - allocate local memory pool block request * * This routine allocates a free block from the specified local memory pool, * ensuring that its size is at least as big as the size requested (in bytes). * * RETURNS: RC_OK, RC_FAIL, RC_TIME on success, failure, timeout respectively */ int _task_mem_pool_alloc(struct k_block *blockptr, /* ptr to requested block */ kmemory_pool_t poolid, /* pool from which to get block */ int reqsize, /* requested block size */ int32_t time /* maximum number of ticks to wait */ ) { struct k_args A; A.Comm = GET_BLOCK; A.Time.ticks = time; A.Args.p1.poolid = poolid; A.Args.p1.req_size = reqsize; KERNEL_ENTRY(&A); blockptr->poolid = poolid; 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; } /******************************************************************************* * * K_RelBlock - perform return memory pool block request * * Marks a block belonging to a local pool as free; if there are waiters * that can use the the block it is passed to a waiting task. * * Forwards a request to release a block in remote pool to the associated node, * which does the actual release work. * * RETURNS: N/A */ void K_RelBlock(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.poolid; P = K_PoolList + 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 = GET_BLOCK_WAIT; TO_ALIST(&K_Args, NewGet); /* push on command stack */ } if (A->Srce) { FREEARGS(A); } return; } } j++; } } /******************************************************************************* * * task_mem_pool_free - return memory pool block request * * This routine returns a block to a memory pool, which may be local or remote. * * The struct k_block structure contains the block details, including the pool to * which it should be returned. * * RETURNS: N/A */ void task_mem_pool_free(struct k_block *blockptr /* pointer to block to free */ ) { struct k_args A; A.Comm = REL_BLOCK; A.Args.p1.poolid = blockptr->poolid; 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); }