diff options
Diffstat (limited to 'drivers/mxc/amd-gpu/common/gsl_memmgr.c')
-rw-r--r-- | drivers/mxc/amd-gpu/common/gsl_memmgr.c | 949 |
1 files changed, 949 insertions, 0 deletions
diff --git a/drivers/mxc/amd-gpu/common/gsl_memmgr.c b/drivers/mxc/amd-gpu/common/gsl_memmgr.c new file mode 100644 index 000000000000..75f250ae59b1 --- /dev/null +++ b/drivers/mxc/amd-gpu/common/gsl_memmgr.c @@ -0,0 +1,949 @@ +/* Copyright (c) 2008-2010, Advanced Micro Devices. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "gsl.h" +#include "gsl_hal.h" + +////////////////////////////////////////////////////////////////////////////// +// defines +////////////////////////////////////////////////////////////////////////////// +#define GSL_MEMARENAPRIV_SIGNATURE_MASK 0x0000FFFF +#define GSL_MEMARENAPRIV_APERTUREID_MASK 0xF0000000 +#define GSL_MEMARENAPRIV_MMUVIRTUALIZED_MASK 0x0F000000 + +#define GSL_MEMARENAPRIV_SIGNATURE_SHIFT 0 +#define GSL_MEMARENAPRIV_MMUVIRTUALIZED_SHIFT 24 +#define GSL_MEMARENAPRIV_APERTUREID_SHIFT 28 + +#define GSL_MEMARENA_INSTANCE_SIGNATURE 0x0000CAFE + +#ifdef GSL_STATS_MEM +#define GSL_MEMARENA_STATS(x) x +#else +#define GSL_MEMARENA_STATS(x) +#endif // GSL_STATS_MEM + + +///////////////////////////////////////////////////////////////////////////// +// macros +////////////////////////////////////////////////////////////////////////////// +#define GSL_MEMARENA_LOCK() kos_mutex_lock(memarena->mutex) +#define GSL_MEMARENA_UNLOCK() kos_mutex_unlock(memarena->mutex) + +#define GSL_MEMARENA_SET_SIGNATURE (memarena->priv |= ((GSL_MEMARENA_INSTANCE_SIGNATURE << GSL_MEMARENAPRIV_SIGNATURE_SHIFT) & GSL_MEMARENAPRIV_SIGNATURE_MASK)) +#define GSL_MEMARENA_SET_MMU_VIRTUALIZED (memarena->priv |= ((mmu_virtualized << GSL_MEMARENAPRIV_MMUVIRTUALIZED_SHIFT) & GSL_MEMARENAPRIV_MMUVIRTUALIZED_MASK)) +#define GSL_MEMARENA_SET_ID (memarena->priv |= ((aperture_id << GSL_MEMARENAPRIV_APERTUREID_SHIFT) & GSL_MEMARENAPRIV_APERTUREID_MASK)) + +#define GSL_MEMARENA_GET_SIGNATURE ((memarena->priv & GSL_MEMARENAPRIV_SIGNATURE_MASK) >> GSL_MEMARENAPRIV_SIGNATURE_SHIFT) +#define GSL_MEMARENA_IS_MMU_VIRTUALIZED ((memarena->priv & GSL_MEMARENAPRIV_MMUVIRTUALIZED_MASK) >> GSL_MEMARENAPRIV_MMUVIRTUALIZED_SHIFT) +#define GSL_MEMARENA_GET_ID ((memarena->priv & GSL_MEMARENAPRIV_APERTUREID_MASK) >> GSL_MEMARENAPRIV_APERTUREID_SHIFT) + + +////////////////////////////////////////////////////////////////////////////// +// validate +////////////////////////////////////////////////////////////////////////////// +#define GSL_MEMARENA_VALIDATE(memarena) \ + KOS_ASSERT(memarena); \ + if (GSL_MEMARENA_GET_SIGNATURE != GSL_MEMARENA_INSTANCE_SIGNATURE) \ + { \ + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, \ + "ERROR: Memarena validation failed.\n" ); \ + return (GSL_FAILURE); \ + } + +////////////////////////////////////////////////////////////////////////////// +// block alignment shift count +////////////////////////////////////////////////////////////////////////////// +OSINLINE unsigned int +gsl_memarena_alignmentshift(gsl_flags_t flags) +{ + int alignshift = ((flags & GSL_MEMFLAGS_ALIGN_MASK) >> GSL_MEMFLAGS_ALIGN_SHIFT); + if (alignshift == 0) + alignshift = 5; // 32 bytes is the minimum alignment boundary + return (alignshift); +} + +////////////////////////////////////////////////////////////////////////////// +// address alignment +////////////////////////////////////////////////////////////////////////////// +OSINLINE unsigned int +gsl_memarena_alignaddr(unsigned int address, int shift) +{ + // + // the value of the returned address is guaranteed to be an even multiple + // of the block alignment shift specified. + // + unsigned int alignedbaseaddr = ((address) >> shift) << shift; + if (alignedbaseaddr < address) + { + alignedbaseaddr += (1 << shift); + } + return (alignedbaseaddr); +} + + +////////////////////////////////////////////////////////////////////////////// +// memory management API +////////////////////////////////////////////////////////////////////////////// + +OSINLINE memblk_t* +kgsl_memarena_getmemblknode(gsl_memarena_t *memarena) +{ +#ifdef GSL_MEMARENA_NODE_POOL_ENABLED + gsl_nodepool_t *nodepool = memarena->nodepool; + memblk_t *memblk = NULL; + int allocnewpool = 1; + int i; + + if (nodepool) + { + // walk through list of existing pools + for ( ; ; ) + { + // if there is a pool with a free memblk node + if (nodepool->priv != (1 << GSL_MEMARENA_NODE_POOL_MAX)-1) + { + // get index of the first free memblk node + for (i = 0; i < GSL_MEMARENA_NODE_POOL_MAX; i++) + { + if (((nodepool->priv >> i) & 0x1) == 0) + { + break; + } + } + + // mark memblk node as used + nodepool->priv |= 1 << i; + + memblk = &nodepool->memblk[i]; + memblk->nodepoolindex = i; + memblk->blkaddr = 0; + memblk->blksize = 0; + + allocnewpool = 0; + + break; + } + else + { + nodepool = nodepool->next; + + if (nodepool == memarena->nodepool) + { + // no free memblk node found + break; + } + } + } + } + + // if no existing pool has a free memblk node + if (allocnewpool) + { + // alloc new pool of memblk nodes + nodepool = ((gsl_nodepool_t *)kos_malloc(sizeof(gsl_nodepool_t))); + if (nodepool) + { + kos_memset(nodepool, 0, sizeof(gsl_nodepool_t)); + + if (memarena->nodepool) + { + nodepool->next = memarena->nodepool->next; + nodepool->prev = memarena->nodepool; + memarena->nodepool->next->prev = nodepool; + memarena->nodepool->next = nodepool; + } + else + { + nodepool->next = nodepool; + nodepool->prev = nodepool; + } + + // reposition pool head + memarena->nodepool = nodepool; + + // mark memblk node as used + nodepool->priv |= 0x1; + + memblk = &nodepool->memblk[0]; + memblk->nodepoolindex = 0; + } + } + + KOS_ASSERT(memblk); + + return (memblk); +#else + // unreferenced formal parameter + (void) memarena; + + return ((memblk_t *)kos_malloc(sizeof(memblk_t))); +#endif // GSL_MEMARENA_NODE_POOL_ENABLED +} + +//---------------------------------------------------------------------------- + +OSINLINE void +kgsl_memarena_releasememblknode(gsl_memarena_t *memarena, memblk_t *memblk) +{ +#ifdef GSL_MEMARENA_NODE_POOL_ENABLED + gsl_nodepool_t *nodepool = memarena->nodepool; + + KOS_ASSERT(memblk); + KOS_ASSERT(nodepool); + + // locate pool to which this memblk node belongs + while (((unsigned int) memblk) < ((unsigned int) nodepool) || + ((unsigned int) memblk) > ((unsigned int) nodepool) + sizeof(gsl_nodepool_t)) + { + nodepool = nodepool->prev; + + KOS_ASSERT(nodepool != memarena->nodepool); + } + + // mark memblk node as unused + nodepool->priv &= ~(1 << memblk->nodepoolindex); + + // free pool when all its memblk nodes are unused + if (nodepool->priv == 0) + { + if (nodepool != nodepool->prev) + { + // reposition pool head + if (nodepool == memarena->nodepool) + { + memarena->nodepool = nodepool->prev; + } + + nodepool->prev->next = nodepool->next; + nodepool->next->prev = nodepool->prev; + } + else + { + memarena->nodepool = NULL; + } + + kos_free((void *)nodepool); + } + else + { + // leave pool head in last pool a memblk node was released + memarena->nodepool = nodepool; + } +#else + // unreferenced formal parameter + (void) memarena; + + kos_free((void *)memblk); +#endif // GSL_MEMARENA_NODE_POOL_ENABLED +} + +//---------------------------------------------------------------------------- + +gsl_memarena_t* +kgsl_memarena_create(int aperture_id, int mmu_virtualized, unsigned int hostbaseaddr, gpuaddr_t gpubaseaddr, int sizebytes) +{ + static int count = 0; + char name[100], id_str[2]; + int len; + gsl_memarena_t *memarena; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> gsl_memarena_t* kgsl_memarena_create(int aperture_id=%d, gpuaddr_t gpubaseaddr=0x%08x, int sizebytes=%d)\n", aperture_id, gpubaseaddr, sizebytes ); + + memarena = (gsl_memarena_t *)kos_malloc(sizeof(gsl_memarena_t)); + + if (!memarena) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, + "ERROR: Memarena allocation failed.\n" ); + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "<-- kgsl_memarena_create. Return value: 0x%08x\n", NULL ); + return (NULL); + } + + kos_memset(memarena, 0, sizeof(gsl_memarena_t)); + + GSL_MEMARENA_SET_SIGNATURE; + GSL_MEMARENA_SET_MMU_VIRTUALIZED; + GSL_MEMARENA_SET_ID; + + // define unique mutex for each memory arena instance + id_str[0] = (char) (count + '0'); + id_str[1] = '\0'; + kos_strcpy(name, "GSL_memory_arena_"); + len = kos_strlen(name); + kos_strcpy(&name[len], id_str); + + memarena->mutex = kos_mutex_create(name); + + // set up the memory arena + memarena->hostbaseaddr = hostbaseaddr; + memarena->gpubaseaddr = gpubaseaddr; + memarena->sizebytes = sizebytes; + + // allocate a memory block in free list which represents all memory in arena + memarena->freelist.head = kgsl_memarena_getmemblknode(memarena); + memarena->freelist.head->blkaddr = 0; + memarena->freelist.head->blksize = memarena->sizebytes; + memarena->freelist.head->next = memarena->freelist.head; + memarena->freelist.head->prev = memarena->freelist.head; + memarena->freelist.allocrover = memarena->freelist.head; + memarena->freelist.freerover = memarena->freelist.head; + + count++; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_create. Return value: 0x%08x\n", memarena ); + + return (memarena); +} + +//---------------------------------------------------------------------------- + +int +kgsl_memarena_destroy(gsl_memarena_t *memarena) +{ + int status = GSL_SUCCESS; + memblk_t *p, *next; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> int kgsl_memarena_destroy(gsl_memarena_t *memarena=0x%08x)\n", memarena ); + + GSL_MEMARENA_VALIDATE(memarena); + + GSL_MEMARENA_LOCK(); + +#ifdef _DEBUG + // memory leak check + if (memarena->freelist.head->blksize != memarena->sizebytes) + { + if (GSL_MEMARENA_GET_ID == GSL_APERTURE_EMEM) + { + // external memory leak detected + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_FATAL, + "ERROR: External memory leak detected.\n" ); + return (GSL_FAILURE); + } + } +#endif // _DEBUG + + p = memarena->freelist.head; + do + { + next = p->next; + kgsl_memarena_releasememblknode(memarena, p); + p = next; + } while (p != memarena->freelist.head); + + GSL_MEMARENA_UNLOCK(); + + if (memarena->mutex) + { + kos_mutex_free(memarena->mutex); + } + + kos_free((void *)memarena); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_destroy. Return value: %B\n", GSL_SUCCESS ); + + return (status); +} + +//---------------------------------------------------------------------------- + +int +kgsl_memarena_isvirtualized(gsl_memarena_t *memarena) +{ + // mmu virtualization enabled + return (GSL_MEMARENA_IS_MMU_VIRTUALIZED); +} + +//---------------------------------------------------------------------------- + +int +kgsl_memarena_checkconsistency(gsl_memarena_t *memarena) +{ + memblk_t *p; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> int kgsl_memarena_checkconsistency(gsl_memarena_t *memarena=0x%08x)\n", memarena ); + + // go through list of free blocks and make sure there are no detectable errors + + p = memarena->freelist.head; + do + { + if (p->next->blkaddr != memarena->freelist.head->blkaddr) + { + if (p->prev->next->blkaddr != p->blkaddr || + p->next->prev->blkaddr != p->blkaddr || + p->blkaddr + p->blksize >= p->next->blkaddr) + { + KOS_ASSERT(0); + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_checkconsistency. Return value: %B\n", GSL_FAILURE ); + return (GSL_FAILURE); + } + } + p = p->next; + + } while (p != memarena->freelist.head); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_checkconsistency. Return value: %B\n", GSL_SUCCESS ); + + return (GSL_SUCCESS); +} + +//---------------------------------------------------------------------------- + +int +kgsl_memarena_querystats(gsl_memarena_t *memarena, gsl_memarena_stats_t *stats) +{ +#ifdef GSL_STATS_MEM + KOS_ASSERT(stats); + GSL_MEMARENA_VALIDATE(memarena); + + kos_memcpy(stats, &memarena->stats, sizeof(gsl_memarena_stats_t)); + + return (GSL_SUCCESS); +#else + // unreferenced formal parameters + (void) memarena; + (void) stats; + + return (GSL_FAILURE_NOTSUPPORTED); +#endif // GSL_STATS_MEM +} + +//---------------------------------------------------------------------------- + +int +kgsl_memarena_checkfreeblock(gsl_memarena_t *memarena, int bytesneeded) +{ + memblk_t *p; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> int kgsl_memarena_checkfreeblock(gsl_memarena_t *memarena=0x%08x, int bytesneeded=%d)\n", memarena, bytesneeded ); + + GSL_MEMARENA_VALIDATE(memarena); + + if (bytesneeded < 1) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: Illegal number of bytes needed.\n" ); + KOS_ASSERT(0); + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_checkfreeblock. Return value: %B\n", GSL_FAILURE ); + return (GSL_FAILURE); + } + + GSL_MEMARENA_LOCK(); + + p = memarena->freelist.head; + do + { + if (p->blksize >= (unsigned int)bytesneeded) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_checkfreeblock. Return value: %B\n", GSL_SUCCESS ); + return (GSL_SUCCESS); + } + + p = p->next; + } while (p != memarena->freelist.head); + + GSL_MEMARENA_UNLOCK(); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_checkfreeblock. Return value: %B\n", GSL_FAILURE ); + + return (GSL_FAILURE); +} + +//---------------------------------------------------------------------------- + +int +kgsl_memarena_alloc(gsl_memarena_t *memarena, gsl_flags_t flags, int size, gsl_memdesc_t *memdesc) +{ + int result = GSL_FAILURE_OUTOFMEM; + memblk_t *ptrfree, *ptrlast, *p; + unsigned int blksize; + unsigned int baseaddr, alignedbaseaddr, alignfragment; + int freeblk, alignmentshift; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> int kgsl_memarena_alloc(gsl_memarena_t *memarena=0x%08x, gsl_flags_t flags=0x%08x, int size=%d, gsl_memdesc_t *memdesc=%M)\n", memarena, flags, size, memdesc ); + + GSL_MEMARENA_VALIDATE(memarena); + + if (size <= 0) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: Invalid size for memory allocation.\n" ); + KOS_ASSERT(0); + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_alloc. Return value: %B\n", GSL_FAILURE ); + return (GSL_FAILURE); + } + + // + // go through the list of free blocks. check to find block which can satisfy the alloc request + // + // if no block can satisfy the alloc request this implies that the memory is too fragmented + // and the requestor needs to free up other memory blocks and re-request the allocation + // + // if we do find a block that can satisfy the alloc request then reduce the size of free block + // by blksize and return the address after allocating the memory. if the free block size becomes + // 0 then remove this node from the free list + // + // there would be no node on the free list if all available memory were to be allocated. + // handling an empty list would require executing error checking code in the main branch which + // is not desired. instead, the free list will have at least one node at all times. This node + // could have a block size of zero + // + // we use a next fit allocation mechanism that uses a roving pointer on a circular free block list. + // the pointer is advanced along the chain when searching for a fit. Thus each allocation begins + // looking where the previous one finished. + // + + // when allocating from external memory aperture, round up size of requested block to multiple of page size if needed + if (GSL_MEMARENA_GET_ID == GSL_APERTURE_EMEM) + { + if ((flags & GSL_MEMFLAGS_FORCEPAGESIZE) || GSL_MEMARENA_IS_MMU_VIRTUALIZED) + { + if (size & (GSL_PAGESIZE-1)) + { + size = ((size >> GSL_PAGESIZE_SHIFT) + 1) << GSL_PAGESIZE_SHIFT; + } + } + } + + // determine shift count for alignment requested + alignmentshift = gsl_memarena_alignmentshift(flags); + + // adjust size of requested block to include alignment + blksize = (unsigned int)((size + ((1 << alignmentshift) - 1)) >> alignmentshift) << alignmentshift; + + GSL_MEMARENA_LOCK(); + + // check consistency, debug only + KGSL_DEBUG(GSL_DBGFLAGS_MEMMGR, kgsl_memarena_checkconsistency(memarena)); + + ptrfree = memarena->freelist.allocrover; + ptrlast = memarena->freelist.head->prev; + freeblk = 0; + + do + { + // align base address + baseaddr = ptrfree->blkaddr + memarena->gpubaseaddr; + alignedbaseaddr = gsl_memarena_alignaddr(baseaddr, alignmentshift); + + alignfragment = alignedbaseaddr - baseaddr; + + if (ptrfree->blksize >= blksize + alignfragment) + { + result = GSL_SUCCESS; + freeblk = 1; + + memdesc->gpuaddr = alignedbaseaddr; + memdesc->hostptr = kgsl_memarena_gethostptr(memarena, memdesc->gpuaddr); + memdesc->size = blksize; + + if (alignfragment > 0) + { + // insert new node to handle newly created (small) fragment + p = kgsl_memarena_getmemblknode(memarena); + p->blkaddr = ptrfree->blkaddr; + p->blksize = alignfragment; + + p->next = ptrfree; + p->prev = ptrfree->prev; + ptrfree->prev->next = p; + ptrfree->prev = p; + + if (ptrfree == memarena->freelist.head) + { + memarena->freelist.head = p; + } + } + + ptrfree->blkaddr += alignfragment + blksize; + ptrfree->blksize -= alignfragment + blksize; + + memarena->freelist.allocrover = ptrfree; + + if (ptrfree->blksize == 0 && ptrfree != ptrlast) + { + ptrfree->prev->next = ptrfree->next; + ptrfree->next->prev = ptrfree->prev; + if (ptrfree == memarena->freelist.head) + { + memarena->freelist.head = ptrfree->next; + } + if (ptrfree == memarena->freelist.allocrover) + { + memarena->freelist.allocrover = ptrfree->next; + } + if (ptrfree == memarena->freelist.freerover) + { + memarena->freelist.freerover = ptrfree->prev; + } + p = ptrfree; + ptrfree = ptrfree->prev; + kgsl_memarena_releasememblknode(memarena, p); + } + } + + ptrfree = ptrfree->next; + + } while (!freeblk && ptrfree != memarena->freelist.allocrover); + + GSL_MEMARENA_UNLOCK(); + + if (result == GSL_SUCCESS) + { + GSL_MEMARENA_STATS( + { + int i = 0; + while (memdesc->size >> (GSL_PAGESIZE_SHIFT + i)) + { + i++; + } + i = i > (GSL_MEMARENA_PAGE_DIST_MAX-1) ? (GSL_MEMARENA_PAGE_DIST_MAX-1) : i; + memarena->stats.allocs_pagedistribution[i]++; + }); + + GSL_MEMARENA_STATS(memarena->stats.allocs_success++); + } + else + { + GSL_MEMARENA_STATS(memarena->stats.allocs_fail++); + } + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_alloc. Return value: %B\n", result ); + + return (result); +} + +//---------------------------------------------------------------------------- + +void +kgsl_memarena_free(gsl_memarena_t *memarena, gsl_memdesc_t *memdesc) +{ + // + // request to free a malloc'ed block from the memory arena + // add this block to the free list + // adding a block to the free list requires the following: + // going through the list of free blocks to decide where to add this free block (based on address) + // coalesce free blocks + // + memblk_t *ptrfree, *ptrend, *p; + int mallocfreeblk, clockwise; + unsigned int addrtofree; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> void kgsl_memarena_free(gsl_memarena_t *memarena=0x%08x, gsl_memdesc_t *memdesc=%M)\n", memarena, memdesc ); + + KOS_ASSERT(memarena); + if (GSL_MEMARENA_GET_SIGNATURE != GSL_MEMARENA_INSTANCE_SIGNATURE) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_free.\n" ); + return; + } + + // check size of malloc'ed block + if (memdesc->size <= 0) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: Illegal size for the memdesc.\n" ); + KOS_ASSERT(0); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_free.\n" ); + return; + } + + // check address range + KOS_ASSERT( memarena->gpubaseaddr <= memdesc->gpuaddr); + KOS_ASSERT((memarena->gpubaseaddr + memarena->sizebytes) >= memdesc->gpuaddr + memdesc->size); + + GSL_MEMARENA_LOCK(); + + // check consistency of memory map, debug only + KGSL_DEBUG(GSL_DBGFLAGS_MEMMGR, kgsl_memarena_checkconsistency(memarena)); + + addrtofree = memdesc->gpuaddr - memarena->gpubaseaddr; + mallocfreeblk = 1; + + if (addrtofree < memarena->freelist.head->blkaddr) + { + // add node to head of free list + + if (addrtofree + memdesc->size == memarena->freelist.head->blkaddr) + { + memarena->freelist.head->blkaddr = addrtofree; + memarena->freelist.head->blksize += memdesc->size; + + mallocfreeblk = 0; + } + + ptrfree = memarena->freelist.head->prev; + } + else if (addrtofree >= memarena->freelist.head->prev->blkaddr) + { + // add node to tail of free list + + ptrfree = memarena->freelist.head->prev; + + if (ptrfree->blkaddr + ptrfree->blksize == addrtofree) + { + ptrfree->blksize += memdesc->size; + + mallocfreeblk = 0; + } + } + else + { + // determine range of free list nodes to traverse and orientation in which to traverse them + // keep this code segment unrolled for performance reasons! + if (addrtofree > memarena->freelist.freerover->blkaddr) + { + if (addrtofree - memarena->freelist.freerover->blkaddr < memarena->freelist.head->prev->blkaddr - addrtofree) + { + ptrfree = memarena->freelist.freerover; // rover + ptrend = memarena->freelist.head->prev; // tail + clockwise = 1; + } + else + { + ptrfree = memarena->freelist.head->prev->prev; // tail + ptrend = memarena->freelist.freerover->prev; // rover + clockwise = 0; + } + } + else + { + if (addrtofree - memarena->freelist.head->blkaddr < memarena->freelist.freerover->blkaddr - addrtofree) + { + ptrfree = memarena->freelist.head; // head + ptrend = memarena->freelist.freerover; // rover + clockwise = 1; + } + else + { + ptrfree = memarena->freelist.freerover->prev; // rover + ptrend = memarena->freelist.head->prev; // head + clockwise = 0; + } + } + + // traverse the nodes + do + { + if ((addrtofree >= ptrfree->blkaddr + ptrfree->blksize) && + (addrtofree + memdesc->size <= ptrfree->next->blkaddr)) + { + if (addrtofree == ptrfree->blkaddr + ptrfree->blksize) + { + memblk_t *next; + + ptrfree->blksize += memdesc->size; + next = ptrfree->next; + + if (ptrfree->blkaddr + ptrfree->blksize == next->blkaddr) + { + ptrfree->blksize += next->blksize; + ptrfree->next = next->next; + next->next->prev = ptrfree; + + if (next == memarena->freelist.allocrover) + { + memarena->freelist.allocrover = ptrfree; + } + + kgsl_memarena_releasememblknode(memarena, next); + } + + mallocfreeblk = 0; + } + else if (addrtofree + memdesc->size == ptrfree->next->blkaddr) + { + ptrfree->next->blkaddr = addrtofree; + ptrfree->next->blksize += memdesc->size; + + mallocfreeblk = 0; + } + + break; + } + + if (clockwise) + { + ptrfree = ptrfree->next; + } + else + { + ptrfree = ptrfree->prev; + } + + } while (ptrfree != ptrend); + } + + // this free block could not be coalesced, so create a new free block + // and add it to the free list in the memory arena + if (mallocfreeblk) + { + p = kgsl_memarena_getmemblknode(memarena); + p->blkaddr = addrtofree; + p->blksize = memdesc->size; + + p->next = ptrfree->next; + p->prev = ptrfree; + ptrfree->next->prev = p; + ptrfree->next = p; + + if (p->blkaddr < memarena->freelist.head->blkaddr) + { + memarena->freelist.head = p; + } + + memarena->freelist.freerover = p; + } + else + { + memarena->freelist.freerover = ptrfree; + } + + GSL_MEMARENA_UNLOCK(); + + GSL_MEMARENA_STATS( + { + int i = 0; + while (memdesc->size >> (GSL_PAGESIZE_SHIFT + i)) + { + i++; + } + i = i > (GSL_MEMARENA_PAGE_DIST_MAX-1) ? (GSL_MEMARENA_PAGE_DIST_MAX-1) : i; + memarena->stats.frees_pagedistribution[i]++; + }); + + GSL_MEMARENA_STATS(memarena->stats.frees++); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_free.\n" ); +} + +//---------------------------------------------------------------------------- + +void * +kgsl_memarena_gethostptr(gsl_memarena_t *memarena, gpuaddr_t gpuaddr) +{ + // + // get the host mapped address for a hardware device address + // + + void *hostptr = NULL; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> void* kgsl_memarena_gethostptr(gsl_memarena_t *memarena=0x%08x, gpuaddr_t gpuaddr=0x%08x)\n", memarena, gpuaddr ); + + KOS_ASSERT(memarena); + if (GSL_MEMARENA_GET_SIGNATURE != GSL_MEMARENA_INSTANCE_SIGNATURE) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_gethostptr. Return value: 0x%08x\n", NULL ); + return (NULL); + } + + // check address range + KOS_ASSERT(gpuaddr >= memarena->gpubaseaddr); + KOS_ASSERT(gpuaddr < memarena->gpubaseaddr + memarena->sizebytes); + + hostptr = (void *)((gpuaddr - memarena->gpubaseaddr) + memarena->hostbaseaddr); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_gethostptr. Return value: 0x%08x\n", hostptr ); + + return (hostptr); +} + +//---------------------------------------------------------------------------- + +gpuaddr_t +kgsl_memarena_getgpuaddr(gsl_memarena_t *memarena, void *hostptr) +{ + // + // get the hardware device address for a host mapped address + // + + gpuaddr_t gpuaddr = 0; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> int kgsl_memarena_getgpuaddr(gsl_memarena_t *memarena=0x%08x, void *hostptr=0x%08x)\n", memarena, hostptr ); + + KOS_ASSERT(memarena); + if (GSL_MEMARENA_GET_SIGNATURE != GSL_MEMARENA_INSTANCE_SIGNATURE) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_getgpuaddr. Return value: 0x%08x\n", 0 ); + return (0); + } + + // check address range + KOS_ASSERT(hostptr >= (void *)memarena->hostbaseaddr); + KOS_ASSERT(hostptr < (void *)(memarena->hostbaseaddr + memarena->sizebytes)); + + gpuaddr = ((unsigned int)hostptr - memarena->hostbaseaddr) + memarena->gpubaseaddr; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_getgpuaddr. Return value: 0x%08x\n", gpuaddr ); + + return (gpuaddr); +} + +//---------------------------------------------------------------------------- + +unsigned int +kgsl_memarena_getlargestfreeblock(gsl_memarena_t *memarena, gsl_flags_t flags) +{ + memblk_t *ptrfree; + unsigned int blocksize, largestblocksize = 0; + int alignmentshift; + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, + "--> unsigned int kgsl_memarena_getlargestfreeblock(gsl_memarena_t *memarena=0x%08x, gsl_flags_t flags=0x%08x)\n", memarena, flags ); + + KOS_ASSERT(memarena); + if (GSL_MEMARENA_GET_SIGNATURE != GSL_MEMARENA_INSTANCE_SIGNATURE) + { + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_getlargestfreeblock. Return value: %d\n", 0 ); + return (0); + } + + // determine shift count for alignment requested + alignmentshift = gsl_memarena_alignmentshift(flags); + + GSL_MEMARENA_LOCK(); + + ptrfree = memarena->freelist.head; + + do + { + blocksize = ptrfree->blksize - (ptrfree->blkaddr - ((ptrfree->blkaddr >> alignmentshift) << alignmentshift)); + + if (blocksize > largestblocksize) + { + largestblocksize = blocksize; + } + + ptrfree = ptrfree->next; + + } while (ptrfree != memarena->freelist.head); + + GSL_MEMARENA_UNLOCK(); + + kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_memarena_getlargestfreeblock. Return value: %d\n", largestblocksize ); + + return (largestblocksize); +} |