/*************************************************************************/ /*! @Title Resource Allocator @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved @Description Implements generic resource allocation. The resource allocator was originally intended to manage address spaces in practice the resource allocator is generic and can manages arbitrary sets of integers. Resources are allocated from arenas. Arena's can be created with an initial span of resources. Further resources spans can be added to arenas. A call back mechanism allows an arena to request further resource spans on demand. Each arena maintains an ordered list of resource segments each described by a boundary tag. Each boundary tag describes a segment of resources which are either 'free', available for allocation, or 'busy' currently allocated. Adjacent 'free' segments are always coallesced to avoid fragmentation. For allocation, all 'free' segments are kept on lists of 'free' segments in a table index by pvr_log2(segment size). ie Each table index n holds 'free' segments in the size range 2**(n-1) -> 2**n. Allocation policy is based on an *almost* best fit stratedy. Choosing any segment from the appropriate table entry guarantees that we choose a segment which is with a power of 2 of the size we are allocating. Allocated segments are inserted into a self scaling hash table which maps the base resource of the span to the relevant boundary tag. This allows the code to get back to the bounary tag without exporting explicit boundary tag references through the API. Each arena has an associated quantum size, all allocations from the arena are made in multiples of the basic quantum. On resource exhaustion in an arena, a callback if provided will be used to request further resources. Resouces spans allocated by the callback mechanism are delimited by special boundary tag markers of zero span, 'span' markers. Span markers are never coallesced. Span markers are used to detect when an imported span is completely free and can be deallocated by the callback mechanism. @License Dual MIT/GPLv2 The contents of this file are subject to the MIT license as set out below. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Alternatively, the contents of this file may be used under the terms of the GNU General Public License Version 2 ("GPL") in which case the provisions of GPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of GPL, and not to allow others to use your version of this file under the terms of the MIT license, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by GPL as set out in the file called "GPL-COPYING" included in this distribution. If you do not delete the provisions above, a recipient may use your version of this file under the terms of either the MIT license or GPL. This License is also included in this distribution in the file called "MIT-COPYING". EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ /* Issues: * - flags, flags are passed into the resource allocator but are not currently used. * - determination, of import size, is currently braindead. * - debug code should be moved out to own module and #ifdef'd */ #include "services_headers.h" #include "hash.h" #include "ra.h" #include "buffer_manager.h" #include "osfunc.h" #if defined(__linux__) && defined(__KERNEL__) #include #include "proc.h" #endif #ifdef USE_BM_FREESPACE_CHECK #include #endif /* The initial, and minimum size of the live address -> boundary tag structure hash table. The value 64 is a fairly arbitrary choice. The hash table resizes on demand so the value choosen is not critical. */ #define MINIMUM_HASH_SIZE (64) #if defined(VALIDATE_ARENA_TEST) /* This test validates the doubly linked ordered list of boundary tags, by checking that adjacent members of the list have compatible eResourceSpan and eResourceType values. */ typedef enum RESOURCE_DESCRIPTOR_TAG { RESOURCE_SPAN_LIVE = 10, RESOURCE_SPAN_FREE, IMPORTED_RESOURCE_SPAN_START, IMPORTED_RESOURCE_SPAN_LIVE, IMPORTED_RESOURCE_SPAN_FREE, IMPORTED_RESOURCE_SPAN_END, } RESOURCE_DESCRIPTOR; typedef enum RESOURCE_TYPE_TAG { IMPORTED_RESOURCE_TYPE = 20, NON_IMPORTED_RESOURCE_TYPE } RESOURCE_TYPE; static IMG_UINT32 ui32BoundaryTagID = 0; IMG_UINT32 ValidateArena(RA_ARENA *pArena); #endif /* boundary tags, used to describe a resource segment */ struct _BT_ { enum bt_type { btt_span, /* span markers */ btt_free, /* free resource segment */ btt_live /* allocated resource segment */ } type; /* The base resource and extent of this segment */ IMG_UINTPTR_T base; IMG_SIZE_T uSize; /* doubly linked ordered list of all segments within the arena */ struct _BT_ *pNextSegment; struct _BT_ *pPrevSegment; /* doubly linked un-ordered list of free segments. */ struct _BT_ *pNextFree; struct _BT_ *pPrevFree; /* a user reference associated with this span, user references are * currently only provided in the callback mechanism */ BM_MAPPING *psMapping; #if defined(VALIDATE_ARENA_TEST) RESOURCE_DESCRIPTOR eResourceSpan; RESOURCE_TYPE eResourceType; /* This variable provides a reference (used in debug messages) to incompatible boundary tags within the doubly linked ordered list. */ IMG_UINT32 ui32BoundaryTagID; #endif }; typedef struct _BT_ BT; /* resource allocation arena */ struct _RA_ARENA_ { /* arena name for diagnostics output */ IMG_CHAR *name; /* allocations within this arena are quantum sized */ IMG_SIZE_T uQuantum; /* import interface, if provided */ IMG_BOOL (*pImportAlloc)(IMG_VOID *, IMG_SIZE_T uSize, IMG_SIZE_T *pActualSize, BM_MAPPING **ppsMapping, IMG_UINT32 uFlags, IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength, IMG_UINTPTR_T *pBase); IMG_VOID (*pImportFree) (IMG_VOID *, IMG_UINTPTR_T, BM_MAPPING *psMapping); IMG_VOID (*pBackingStoreFree) (IMG_VOID *, IMG_SIZE_T, IMG_SIZE_T, IMG_HANDLE); /* arbitrary handle provided by arena owner to be passed into the * import alloc and free hooks */ IMG_VOID *pImportHandle; /* head of list of free boundary tags for indexed by pvr_log2 of the boundary tag size */ #define FREE_TABLE_LIMIT 32 /* power-of-two table of free lists */ BT *aHeadFree [FREE_TABLE_LIMIT]; /* resource ordered segment list */ BT *pHeadSegment; BT *pTailSegment; /* segment address to boundary tag hash table */ HASH_TABLE *pSegmentHash; #ifdef RA_STATS RA_STATISTICS sStatistics; #endif #if defined(CONFIG_PROC_FS) && defined(DEBUG) #define PROC_NAME_SIZE 64 struct pvr_proc_dir_entry* pProcInfo; struct pvr_proc_dir_entry* pProcSegs; IMG_BOOL bInitProcEntry; #endif }; /* #define ENABLE_RA_DUMP 1 */ #if defined(ENABLE_RA_DUMP) IMG_VOID RA_Dump (RA_ARENA *pArena); #endif #if defined(CONFIG_PROC_FS) && defined(DEBUG) static void RA_ProcSeqShowInfo(struct seq_file *sfile, void* el); static void* RA_ProcSeqOff2ElementInfo(struct seq_file * sfile, loff_t off); static void RA_ProcSeqShowRegs(struct seq_file *sfile, void* el); static void* RA_ProcSeqOff2ElementRegs(struct seq_file * sfile, loff_t off); #endif /* defined(CONFIG_PROC_FS) && defined(DEBUG) */ #ifdef USE_BM_FREESPACE_CHECK IMG_VOID CheckBMFreespace(IMG_VOID); #endif #if defined(CONFIG_PROC_FS) && defined(DEBUG) static IMG_CHAR *ReplaceSpaces(IMG_CHAR * const pS) { IMG_CHAR *pT; for(pT = pS; *pT != 0; pT++) { if (*pT == ' ' || *pT == '\t') { *pT = '_'; } } return pS; } #endif /*! ****************************************************************************** @Function _RequestAllocFail @Description Default callback allocator used if no callback is specified, always fails to allocate further resources to the arena. @Input _h - callback handle @Input _uSize - requested allocation size @Output _pActualSize - actual allocation size @Input _pRef - user reference @Input _uflags - allocation flags @Input _pvPrivData - private data @Input _ui32PrivDataLength - private data length @Input _pBase - receives allocated base @Return IMG_FALSE, this function always fails to allocate. ******************************************************************************/ static IMG_BOOL _RequestAllocFail (IMG_VOID *_h, IMG_SIZE_T _uSize, IMG_SIZE_T *_pActualSize, BM_MAPPING **_ppsMapping, IMG_UINT32 _uFlags, IMG_PVOID _pvPrivData, IMG_UINT32 _ui32PrivDataLength, IMG_UINTPTR_T *_pBase) { PVR_UNREFERENCED_PARAMETER (_h); PVR_UNREFERENCED_PARAMETER (_uSize); PVR_UNREFERENCED_PARAMETER (_pActualSize); PVR_UNREFERENCED_PARAMETER (_ppsMapping); PVR_UNREFERENCED_PARAMETER (_uFlags); PVR_UNREFERENCED_PARAMETER (_pBase); PVR_UNREFERENCED_PARAMETER (_pvPrivData); PVR_UNREFERENCED_PARAMETER (_ui32PrivDataLength); return IMG_FALSE; } /*! ****************************************************************************** @Function pvr_log2 @Description Computes the floor of the log base 2 of a unsigned integer @Input n - unsigned integer @Return Floor(Log2(n)) ******************************************************************************/ static IMG_UINT32 pvr_log2 (IMG_SIZE_T n) { IMG_UINT32 l = 0; n>>=1; while (n>0) { n>>=1; l++; } return l; } /*! ****************************************************************************** @Function _SegmentListInsertAfter @Description Insert a boundary tag into an arena segment list after a specified boundary tag. @Input pArena - the arena. @Input pInsertionPoint - the insertion point. @Input pBT - the boundary tag to insert. @Return PVRSRV_ERROR ******************************************************************************/ static PVRSRV_ERROR _SegmentListInsertAfter (RA_ARENA *pArena, BT *pInsertionPoint, BT *pBT) { PVR_ASSERT (pArena != IMG_NULL); PVR_ASSERT (pInsertionPoint != IMG_NULL); if ((pInsertionPoint == IMG_NULL) || (pArena == IMG_NULL)) { PVR_DPF ((PVR_DBG_ERROR,"_SegmentListInsertAfter: invalid parameters")); return PVRSRV_ERROR_INVALID_PARAMS; } pBT->pNextSegment = pInsertionPoint->pNextSegment; pBT->pPrevSegment = pInsertionPoint; if (pInsertionPoint->pNextSegment == IMG_NULL) pArena->pTailSegment = pBT; else pInsertionPoint->pNextSegment->pPrevSegment = pBT; pInsertionPoint->pNextSegment = pBT; return PVRSRV_OK; } /*! ****************************************************************************** @Function _SegmentListInsert @Description Insert a boundary tag into an arena segment list at the appropriate point. @Input pArena - the arena. @Input pBT - the boundary tag to insert. @Return None ******************************************************************************/ static PVRSRV_ERROR _SegmentListInsert (RA_ARENA *pArena, BT *pBT) { PVRSRV_ERROR eError = PVRSRV_OK; /* insert into the segment chain */ if (pArena->pHeadSegment == IMG_NULL) { pArena->pHeadSegment = pArena->pTailSegment = pBT; pBT->pNextSegment = pBT->pPrevSegment = IMG_NULL; } else { BT *pBTScan; if (pBT->base < pArena->pHeadSegment->base) { /* The base address of pBT is less than the base address of the boundary tag at the head of the list - so insert this boundary tag at the head. */ pBT->pNextSegment = pArena->pHeadSegment; pArena->pHeadSegment->pPrevSegment = pBT; pArena->pHeadSegment = pBT; pBT->pPrevSegment = IMG_NULL; } else { /* The base address of pBT is greater than or equal to that of the boundary tag at the head of the list. Search for the insertion point: pBT must be inserted before the first boundary tag with a greater base value - or at the end of the list. */ pBTScan = pArena->pHeadSegment; while ((pBTScan->pNextSegment != IMG_NULL) && (pBT->base >= pBTScan->pNextSegment->base)) { pBTScan = pBTScan->pNextSegment; } eError = _SegmentListInsertAfter (pArena, pBTScan, pBT); if (eError != PVRSRV_OK) { return eError; } } } return eError; } /*! ****************************************************************************** @Function _SegmentListRemove @Description Remove a boundary tag from an arena segment list. @Input pArena - the arena. @Input pBT - the boundary tag to remove. @Return None ******************************************************************************/ static IMG_VOID _SegmentListRemove (RA_ARENA *pArena, BT *pBT) { if (pBT->pPrevSegment == IMG_NULL) pArena->pHeadSegment = pBT->pNextSegment; else pBT->pPrevSegment->pNextSegment = pBT->pNextSegment; if (pBT->pNextSegment == IMG_NULL) pArena->pTailSegment = pBT->pPrevSegment; else pBT->pNextSegment->pPrevSegment = pBT->pPrevSegment; } /*! ****************************************************************************** @Function _SegmentSplit @Description Split a segment into two, maintain the arena segment list. The boundary tag should not be in the free table. Neither the original or the new neighbour bounary tag will be in the free table. @Input pArena - the arena. @Input pBT - the boundary tag to split. @Input uSize - the required segment size of boundary tag after splitting. @Return New neighbour boundary tag. ******************************************************************************/ static BT * _SegmentSplit (RA_ARENA *pArena, BT *pBT, IMG_SIZE_T uSize) { BT *pNeighbour; PVR_ASSERT (pArena != IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"_SegmentSplit: invalid parameter - pArena")); return IMG_NULL; } if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), (IMG_VOID **)&pNeighbour, IMG_NULL, "Boundary Tag") != PVRSRV_OK) { return IMG_NULL; } OSMemSet(pNeighbour, 0, sizeof(BT)); #if defined(VALIDATE_ARENA_TEST) pNeighbour->ui32BoundaryTagID = ++ui32BoundaryTagID; #endif pNeighbour->pPrevSegment = pBT; pNeighbour->pNextSegment = pBT->pNextSegment; if (pBT->pNextSegment == IMG_NULL) pArena->pTailSegment = pNeighbour; else pBT->pNextSegment->pPrevSegment = pNeighbour; pBT->pNextSegment = pNeighbour; pNeighbour->type = btt_free; pNeighbour->uSize = pBT->uSize - uSize; pNeighbour->base = pBT->base + uSize; pNeighbour->psMapping = pBT->psMapping; pBT->uSize = uSize; #if defined(VALIDATE_ARENA_TEST) if (pNeighbour->pPrevSegment->eResourceType == IMPORTED_RESOURCE_TYPE) { pNeighbour->eResourceType = IMPORTED_RESOURCE_TYPE; pNeighbour->eResourceSpan = IMPORTED_RESOURCE_SPAN_FREE; } else if (pNeighbour->pPrevSegment->eResourceType == NON_IMPORTED_RESOURCE_TYPE) { pNeighbour->eResourceType = NON_IMPORTED_RESOURCE_TYPE; pNeighbour->eResourceSpan = RESOURCE_SPAN_FREE; } else { PVR_DPF ((PVR_DBG_ERROR,"_SegmentSplit: pNeighbour->pPrevSegment->eResourceType unrecognized")); PVR_DBG_BREAK; } #endif return pNeighbour; } /*! ****************************************************************************** @Function _FreeListInsert @Description Insert a boundary tag into an arena free table. @Input pArena - the arena. @Input pBT - the boundary tag. @Return None ******************************************************************************/ static IMG_VOID _FreeListInsert (RA_ARENA *pArena, BT *pBT) { IMG_UINT32 uIndex; uIndex = pvr_log2 (pBT->uSize); pBT->type = btt_free; pBT->pNextFree = pArena->aHeadFree [uIndex]; pBT->pPrevFree = IMG_NULL; if (pArena->aHeadFree[uIndex] != IMG_NULL) pArena->aHeadFree[uIndex]->pPrevFree = pBT; pArena->aHeadFree [uIndex] = pBT; } /*! ****************************************************************************** @Function _FreeListRemove @Description Remove a boundary tag from an arena free table. @Input pArena - the arena. @Input pBT - the boundary tag. @Return None ******************************************************************************/ static IMG_VOID _FreeListRemove (RA_ARENA *pArena, BT *pBT) { IMG_UINT32 uIndex; uIndex = pvr_log2 (pBT->uSize); if (pBT->pNextFree != IMG_NULL) pBT->pNextFree->pPrevFree = pBT->pPrevFree; if (pBT->pPrevFree == IMG_NULL) pArena->aHeadFree[uIndex] = pBT->pNextFree; else pBT->pPrevFree->pNextFree = pBT->pNextFree; } /*! ****************************************************************************** @Function _BuildSpanMarker @Description Construct a span marker boundary tag. @Input pArena - arena to contain span marker @Input base - the base of the bounary tag. @Return span marker boundary tag ******************************************************************************/ static BT * _BuildSpanMarker (IMG_UINTPTR_T base, IMG_SIZE_T uSize) { BT *pBT; if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), (IMG_VOID **)&pBT, IMG_NULL, "Boundary Tag") != PVRSRV_OK) { return IMG_NULL; } OSMemSet(pBT, 0, sizeof(BT)); #if defined(VALIDATE_ARENA_TEST) pBT->ui32BoundaryTagID = ++ui32BoundaryTagID; #endif pBT->type = btt_span; pBT->base = base; pBT->uSize = uSize; pBT->psMapping = IMG_NULL; return pBT; } /*! ****************************************************************************** @Function _BuildBT @Description Construct a boundary tag for a free segment. @Input base - the base of the resource segment. @Input uSize - the extent of the resouce segment. @Return boundary tag ******************************************************************************/ static BT * _BuildBT (IMG_UINTPTR_T base, IMG_SIZE_T uSize) { BT *pBT; if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), (IMG_VOID **)&pBT, IMG_NULL, "Boundary Tag") != PVRSRV_OK) { return IMG_NULL; } OSMemSet(pBT, 0, sizeof(BT)); #if defined(VALIDATE_ARENA_TEST) pBT->ui32BoundaryTagID = ++ui32BoundaryTagID; #endif pBT->type = btt_free; pBT->base = base; pBT->uSize = uSize; return pBT; } /*! ****************************************************************************** @Function _InsertResource @Description Add a free resource segment to an arena. @Input pArena - the arena. @Input base - the base of the resource segment. @Input uSize - the extent of the resource segment. @Return New bucket pointer IMG_NULL failure ******************************************************************************/ static BT * _InsertResource (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_SIZE_T uSize) { BT *pBT; PVR_ASSERT (pArena!=IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"_InsertResource: invalid parameter - pArena")); return IMG_NULL; } pBT = _BuildBT (base, uSize); if (pBT != IMG_NULL) { #if defined(VALIDATE_ARENA_TEST) pBT->eResourceSpan = RESOURCE_SPAN_FREE; pBT->eResourceType = NON_IMPORTED_RESOURCE_TYPE; #endif if (_SegmentListInsert (pArena, pBT) != PVRSRV_OK) { PVR_DPF ((PVR_DBG_ERROR,"_InsertResource: call to _SegmentListInsert failed")); return IMG_NULL; } _FreeListInsert (pArena, pBT); #ifdef RA_STATS pArena->sStatistics.uTotalResourceCount+=uSize; pArena->sStatistics.uFreeResourceCount+=uSize; pArena->sStatistics.uSpanCount++; #endif } return pBT; } /*! ****************************************************************************** @Function _InsertResourceSpan @Description Add a free resource span to an arena, complete with span markers. @Input pArena - the arena. @Input base - the base of the resource segment. @Input uSize - the extent of the resource segment. @Return the boundary tag representing the free resource segment, or IMG_NULL on failure. ******************************************************************************/ static BT * _InsertResourceSpan (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_SIZE_T uSize) { PVRSRV_ERROR eError; BT *pSpanStart; BT *pSpanEnd; BT *pBT; PVR_ASSERT (pArena != IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"_InsertResourceSpan: invalid parameter - pArena")); return IMG_NULL; } PVR_DPF ((PVR_DBG_MESSAGE, "RA_InsertResourceSpan: arena='%s', base=0x" UINTPTR_FMT ", size=0x%" SIZE_T_FMT_LEN "x", pArena->name, base, uSize)); pSpanStart = _BuildSpanMarker (base, uSize); if (pSpanStart == IMG_NULL) { goto fail_start; } #if defined(VALIDATE_ARENA_TEST) pSpanStart->eResourceSpan = IMPORTED_RESOURCE_SPAN_START; pSpanStart->eResourceType = IMPORTED_RESOURCE_TYPE; #endif pSpanEnd = _BuildSpanMarker (base + uSize, 0); if (pSpanEnd == IMG_NULL) { goto fail_end; } #if defined(VALIDATE_ARENA_TEST) pSpanEnd->eResourceSpan = IMPORTED_RESOURCE_SPAN_END; pSpanEnd->eResourceType = IMPORTED_RESOURCE_TYPE; #endif pBT = _BuildBT (base, uSize); if (pBT == IMG_NULL) { goto fail_bt; } #if defined(VALIDATE_ARENA_TEST) pBT->eResourceSpan = IMPORTED_RESOURCE_SPAN_FREE; pBT->eResourceType = IMPORTED_RESOURCE_TYPE; #endif eError = _SegmentListInsert (pArena, pSpanStart); if (eError != PVRSRV_OK) { goto fail_SegListInsert; } eError = _SegmentListInsertAfter (pArena, pSpanStart, pBT); if (eError != PVRSRV_OK) { goto fail_SegListInsert; } _FreeListInsert (pArena, pBT); eError = _SegmentListInsertAfter (pArena, pBT, pSpanEnd); if (eError != PVRSRV_OK) { goto fail_SegListInsert; } #ifdef RA_STATS pArena->sStatistics.uTotalResourceCount+=uSize; /* pArena->sStatistics.uFreeResourceCount+=uSize; This has got to be wrong as uFreeResourceCount ends up larger than uTotalResourceCount by uTotalResourceCount - allocated memory */ #endif return pBT; fail_SegListInsert: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pBT, IMG_NULL); /*not nulling pointer, out of scope*/ fail_bt: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pSpanEnd, IMG_NULL); /*not nulling pointer, out of scope*/ fail_end: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pSpanStart, IMG_NULL); /*not nulling pointer, out of scope*/ fail_start: return IMG_NULL; } /*! ****************************************************************************** @Function _FreeBT @Description Free a boundary tag taking care of the segment list and the boundary tag free table. @Input pArena - the arena. @Input pBT - the boundary tag to free. @Input bFreeBackingStore - Should backing for the memory be freed as well. @Return None ******************************************************************************/ static IMG_VOID _FreeBT (RA_ARENA *pArena, BT *pBT, IMG_BOOL bFreeBackingStore) { BT *pNeighbour; IMG_UINTPTR_T uOrigBase; IMG_SIZE_T uOrigSize; PVR_ASSERT (pArena!=IMG_NULL); PVR_ASSERT (pBT!=IMG_NULL); if ((pArena == IMG_NULL) || (pBT == IMG_NULL)) { PVR_DPF ((PVR_DBG_ERROR,"_FreeBT: invalid parameter")); return; } #ifdef RA_STATS pArena->sStatistics.uLiveSegmentCount--; pArena->sStatistics.uFreeSegmentCount++; pArena->sStatistics.uFreeResourceCount+=pBT->uSize; #endif uOrigBase = pBT->base; uOrigSize = pBT->uSize; /* try and coalesce with left neighbour */ pNeighbour = pBT->pPrevSegment; if (pNeighbour!=IMG_NULL && pNeighbour->type == btt_free && pNeighbour->base + pNeighbour->uSize == pBT->base) { _FreeListRemove (pArena, pNeighbour); _SegmentListRemove (pArena, pNeighbour); pBT->base = pNeighbour->base; pBT->uSize += pNeighbour->uSize; OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pNeighbour, IMG_NULL); /*not nulling original pointer, already overwritten*/ #ifdef RA_STATS pArena->sStatistics.uFreeSegmentCount--; #endif } /* try to coalesce with right neighbour */ pNeighbour = pBT->pNextSegment; if (pNeighbour!=IMG_NULL && pNeighbour->type == btt_free && pBT->base + pBT->uSize == pNeighbour->base) { _FreeListRemove (pArena, pNeighbour); _SegmentListRemove (pArena, pNeighbour); pBT->uSize += pNeighbour->uSize; OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pNeighbour, IMG_NULL); /*not nulling original pointer, already overwritten*/ #ifdef RA_STATS pArena->sStatistics.uFreeSegmentCount--; #endif } /* try to free backing store memory. */ if (pArena->pBackingStoreFree != IMG_NULL && bFreeBackingStore) { IMG_UINTPTR_T uRoundedStart, uRoundedEnd; /* Work out the first address we might be able to free. */ uRoundedStart = (uOrigBase / pArena->uQuantum) * pArena->uQuantum; /* If a span is still using that address then leave it. */ if (uRoundedStart < pBT->base) { uRoundedStart += pArena->uQuantum; } /* Work out the last address we might be able to free. */ uRoundedEnd = ((uOrigBase + uOrigSize + pArena->uQuantum - 1) / pArena->uQuantum) * pArena->uQuantum; /* If a span is still using that addres then leave it. */ if (uRoundedEnd > (pBT->base + pBT->uSize)) { uRoundedEnd -= pArena->uQuantum; } if (uRoundedStart < uRoundedEnd) { pArena->pBackingStoreFree(pArena->pImportHandle, (IMG_SIZE_T)uRoundedStart, (IMG_SIZE_T)uRoundedEnd, (IMG_HANDLE)0); } } if (pBT->pNextSegment!=IMG_NULL && pBT->pNextSegment->type == btt_span && pBT->pPrevSegment!=IMG_NULL && pBT->pPrevSegment->type == btt_span) { BT *next = pBT->pNextSegment; BT *prev = pBT->pPrevSegment; _SegmentListRemove (pArena, next); _SegmentListRemove (pArena, prev); _SegmentListRemove (pArena, pBT); pArena->pImportFree (pArena->pImportHandle, pBT->base, pBT->psMapping); #ifdef RA_STATS pArena->sStatistics.uSpanCount--; pArena->sStatistics.uExportCount++; pArena->sStatistics.uFreeSegmentCount--; pArena->sStatistics.uFreeResourceCount-=pBT->uSize; pArena->sStatistics.uTotalResourceCount-=pBT->uSize; #endif OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), next, IMG_NULL); /*not nulling original pointer, already overwritten*/ OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), prev, IMG_NULL); /*not nulling original pointer, already overwritten*/ OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pBT, IMG_NULL); /*not nulling pointer, copy on stack*/ } else _FreeListInsert (pArena, pBT); } /*! ****************************************************************************** @Function _AttemptAllocAligned @Description Attempt an allocation from an arena. @Input pArena - the arena. @Input uSize - the requested allocation size. @Output ppsMapping - the user references associated with the allocated segment. @Input flags - allocation flags @Input uAlignment - required uAlignment, or 0 @Input uAlignmentOffset @Output base - allocated resource base @Return IMG_FALSE failure IMG_TRUE success ******************************************************************************/ static IMG_BOOL _AttemptAllocAligned (RA_ARENA *pArena, IMG_SIZE_T uSize, BM_MAPPING **ppsMapping, IMG_UINT32 uFlags, IMG_UINT32 uAlignment, IMG_UINT32 uAlignmentOffset, IMG_UINTPTR_T *base) { IMG_UINT32 uIndex; PVR_ASSERT (pArena!=IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned: invalid parameter - pArena")); return IMG_FALSE; } if (uAlignment>1) uAlignmentOffset %= uAlignment; /* search for a near fit free boundary tag, start looking at the pvr_log2 free table for our required size and work on up the table. */ uIndex = pvr_log2 (uSize); while (uIndex < FREE_TABLE_LIMIT && pArena->aHeadFree[uIndex]==IMG_NULL) uIndex++; while (uIndex < FREE_TABLE_LIMIT) { if (pArena->aHeadFree[uIndex]!=IMG_NULL) { /* we have a cached free boundary tag */ BT *pBT; pBT = pArena->aHeadFree [uIndex]; while (pBT!=IMG_NULL) { IMG_UINTPTR_T aligned_base; if (uAlignment>1) aligned_base = (pBT->base + uAlignmentOffset + uAlignment - 1) / uAlignment * uAlignment - uAlignmentOffset; else aligned_base = pBT->base; PVR_DPF ((PVR_DBG_MESSAGE, "RA_AttemptAllocAligned: pBT-base=0x" UINTPTR_FMT " " "pBT-size=0x%" SIZE_T_FMT_LEN "x alignedbase=0x" UINTPTR_FMT " size=0x%" SIZE_T_FMT_LEN "x", pBT->base, pBT->uSize, aligned_base, uSize)); if (pBT->base + pBT->uSize >= aligned_base + uSize) { if(!pBT->psMapping || pBT->psMapping->ui32Flags == uFlags) { _FreeListRemove (pArena, pBT); PVR_ASSERT (pBT->type == btt_free); #ifdef RA_STATS pArena->sStatistics.uLiveSegmentCount++; pArena->sStatistics.uFreeSegmentCount--; pArena->sStatistics.uFreeResourceCount-=pBT->uSize; #endif /* with uAlignment we might need to discard the front of this segment */ if (aligned_base > pBT->base) { BT *pNeighbour; pNeighbour = _SegmentSplit (pArena, pBT, (IMG_SIZE_T)(aligned_base - pBT->base)); /* partition the buffer, create a new boundary tag */ if (pNeighbour==IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned: Front split failed")); /* Put pBT back in the list */ _FreeListInsert (pArena, pBT); return IMG_FALSE; } _FreeListInsert (pArena, pBT); #ifdef RA_STATS pArena->sStatistics.uFreeSegmentCount++; pArena->sStatistics.uFreeResourceCount+=pBT->uSize; #endif pBT = pNeighbour; } /* the segment might be too big, if so, discard the back of the segment */ if (pBT->uSize > uSize) { BT *pNeighbour; pNeighbour = _SegmentSplit (pArena, pBT, uSize); /* partition the buffer, create a new boundary tag */ if (pNeighbour==IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned: Back split failed")); /* Put pBT back in the list */ _FreeListInsert (pArena, pBT); return IMG_FALSE; } _FreeListInsert (pArena, pNeighbour); #ifdef RA_STATS pArena->sStatistics.uFreeSegmentCount++; pArena->sStatistics.uFreeResourceCount+=pNeighbour->uSize; #endif } pBT->type = btt_live; #if defined(VALIDATE_ARENA_TEST) if (pBT->eResourceType == IMPORTED_RESOURCE_TYPE) { pBT->eResourceSpan = IMPORTED_RESOURCE_SPAN_LIVE; } else if (pBT->eResourceType == NON_IMPORTED_RESOURCE_TYPE) { pBT->eResourceSpan = RESOURCE_SPAN_LIVE; } else { PVR_DPF ((PVR_DBG_ERROR,"_AttemptAllocAligned ERROR: pBT->eResourceType unrecognized")); PVR_DBG_BREAK; } #endif if (!HASH_Insert (pArena->pSegmentHash, pBT->base, (IMG_UINTPTR_T) pBT)) { _FreeBT (pArena, pBT, IMG_FALSE); return IMG_FALSE; } if (ppsMapping!=IMG_NULL) *ppsMapping = pBT->psMapping; *base = pBT->base; return IMG_TRUE; } else { PVR_DPF ((PVR_DBG_MESSAGE, "AttemptAllocAligned: mismatch in flags. Import has %x, request was %x", pBT->psMapping->ui32Flags, uFlags)); } } pBT = pBT->pNextFree; } } uIndex++; } return IMG_FALSE; } /*! ****************************************************************************** @Function RA_Create @Description To create a resource arena. @Input name - the name of the arena for diagnostic purposes. @Input base - the base of an initial resource span or 0. @Input uSize - the size of an initial resource span or 0. @Input uQuantum - the arena allocation quantum. @Input alloc - a resource allocation callback or 0. @Input free - a resource de-allocation callback or 0. @Input backingstore_free - a callback to free resources for spans or 0. @Input pImportHandle - handle passed to alloc and free or 0. @Return arena handle, or IMG_NULL. ******************************************************************************/ RA_ARENA * RA_Create (IMG_CHAR *name, IMG_UINTPTR_T base, IMG_SIZE_T uSize, BM_MAPPING *psMapping, IMG_SIZE_T uQuantum, IMG_BOOL (*imp_alloc)(IMG_VOID *, IMG_SIZE_T uSize, IMG_SIZE_T *pActualSize, BM_MAPPING **ppsMapping, IMG_UINT32 _flags, IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength, IMG_UINTPTR_T *pBase), IMG_VOID (*imp_free) (IMG_VOID *, IMG_UINTPTR_T, BM_MAPPING *), IMG_VOID (*backingstore_free) (IMG_VOID*, IMG_SIZE_T, IMG_SIZE_T, IMG_HANDLE), IMG_VOID *pImportHandle) { RA_ARENA *pArena; BT *pBT; IMG_INT i; PVR_DPF ((PVR_DBG_MESSAGE, "RA_Create: name='%s', base=0x" UINTPTR_FMT ", uSize=0x%" SIZE_T_FMT_LEN "x, alloc=0x%p, free=0x%p", name, base, uSize, imp_alloc, imp_free)); if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof (*pArena), (IMG_VOID **)&pArena, IMG_NULL, "Resource Arena") != PVRSRV_OK) { goto arena_fail; } pArena->name = name; pArena->pImportAlloc = (imp_alloc!=IMG_NULL) ? imp_alloc : &_RequestAllocFail; pArena->pImportFree = imp_free; pArena->pBackingStoreFree = backingstore_free; pArena->pImportHandle = pImportHandle; for (i=0; iaHeadFree[i] = IMG_NULL; pArena->pHeadSegment = IMG_NULL; pArena->pTailSegment = IMG_NULL; pArena->uQuantum = uQuantum; #ifdef RA_STATS pArena->sStatistics.uSpanCount = 0; pArena->sStatistics.uLiveSegmentCount = 0; pArena->sStatistics.uFreeSegmentCount = 0; pArena->sStatistics.uFreeResourceCount = 0; pArena->sStatistics.uTotalResourceCount = 0; pArena->sStatistics.uCumulativeAllocs = 0; pArena->sStatistics.uCumulativeFrees = 0; pArena->sStatistics.uImportCount = 0; pArena->sStatistics.uExportCount = 0; #endif #if defined(CONFIG_PROC_FS) && defined(DEBUG) if(strcmp(pArena->name,"") != 0) { IMG_INT ret; IMG_CHAR szProcInfoName[PROC_NAME_SIZE]; IMG_CHAR szProcSegsName[PROC_NAME_SIZE]; struct pvr_proc_dir_entry* (*pfnCreateProcEntrySeq)(const IMG_CHAR *, IMG_VOID*, pvr_next_proc_seq_t, pvr_show_proc_seq_t, pvr_off2element_proc_seq_t, pvr_startstop_proc_seq_t, pvr_proc_write_t); pArena->bInitProcEntry = !PVRSRVGetInitServerState(PVRSRV_INIT_SERVER_SUCCESSFUL); /* Don't put shared heap info into a per process /proc subdirectory */ pfnCreateProcEntrySeq = pArena->bInitProcEntry ? CreateProcEntrySeq : CreatePerProcessProcEntrySeq; ret = snprintf(szProcInfoName, sizeof(szProcInfoName), "ra_info_%s", pArena->name); if (ret > 0 && ret < sizeof(szProcInfoName)) { pArena->pProcInfo = pfnCreateProcEntrySeq(ReplaceSpaces(szProcInfoName), pArena, NULL, RA_ProcSeqShowInfo, RA_ProcSeqOff2ElementInfo, NULL, NULL); } else { pArena->pProcInfo = 0; PVR_DPF((PVR_DBG_ERROR, "RA_Create: couldn't create ra_info proc entry for arena %s", pArena->name)); } ret = snprintf(szProcSegsName, sizeof(szProcSegsName), "ra_segs_%s", pArena->name); if (ret > 0 && ret < sizeof(szProcInfoName)) { pArena->pProcSegs = pfnCreateProcEntrySeq(ReplaceSpaces(szProcSegsName), pArena, NULL, RA_ProcSeqShowRegs, RA_ProcSeqOff2ElementRegs, NULL, NULL); } else { pArena->pProcSegs = 0; PVR_DPF((PVR_DBG_ERROR, "RA_Create: couldn't create ra_segs proc entry for arena %s", pArena->name)); } } #endif /* defined(CONFIG_PROC_FS) && defined(DEBUG) */ pArena->pSegmentHash = HASH_Create (MINIMUM_HASH_SIZE); if (pArena->pSegmentHash==IMG_NULL) { goto hash_fail; } if (uSize>0) { uSize = (uSize + uQuantum - 1) / uQuantum * uQuantum; pBT = _InsertResource (pArena, base, uSize); if (pBT == IMG_NULL) { goto insert_fail; } pBT->psMapping = psMapping; } return pArena; insert_fail: HASH_Delete (pArena->pSegmentHash); hash_fail: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RA_ARENA), pArena, IMG_NULL); /*not nulling pointer, out of scope*/ arena_fail: return IMG_NULL; } /*! ****************************************************************************** @Function RA_Delete @Description To delete a resource arena. All resources allocated from the arena must be freed before deleting the arena. @Input pArena - the arena to delete. @Return None ******************************************************************************/ IMG_VOID RA_Delete (RA_ARENA *pArena) { IMG_UINT32 uIndex; PVR_ASSERT(pArena != IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"RA_Delete: invalid parameter - pArena")); return; } PVR_DPF ((PVR_DBG_MESSAGE, "RA_Delete: name='%s'", pArena->name)); for (uIndex=0; uIndexaHeadFree[uIndex] = IMG_NULL; while (pArena->pHeadSegment != IMG_NULL) { BT *pBT = pArena->pHeadSegment; if (pBT->type != btt_free) { PVR_DPF ((PVR_DBG_ERROR,"RA_Delete: allocations still exist in the arena that is being destroyed")); PVR_DPF ((PVR_DBG_ERROR,"Likely Cause: client drivers not freeing allocations before destroying devmemcontext")); PVR_DPF ((PVR_DBG_ERROR,"RA_Delete: base = 0x" UINTPTR_FMT " size=0x%" SIZE_T_FMT_LEN "x", pBT->base, pBT->uSize)); } _SegmentListRemove (pArena, pBT); OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(BT), pBT, IMG_NULL); /*not nulling original pointer, it has changed*/ #ifdef RA_STATS pArena->sStatistics.uSpanCount--; #endif } #if defined(CONFIG_PROC_FS) && defined(DEBUG) { IMG_VOID (*pfnRemoveProcEntrySeq)(struct pvr_proc_dir_entry*); pfnRemoveProcEntrySeq = pArena->bInitProcEntry ? RemoveProcEntrySeq : RemovePerProcessProcEntrySeq; if (pArena->pProcInfo != 0) { pfnRemoveProcEntrySeq( pArena->pProcInfo ); } if (pArena->pProcSegs != 0) { pfnRemoveProcEntrySeq( pArena->pProcSegs ); } } #endif HASH_Delete (pArena->pSegmentHash); OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RA_ARENA), pArena, IMG_NULL); /*not nulling pointer, copy on stack*/ } /*! ****************************************************************************** @Function RA_TestDelete @Description To test whether it is safe to delete a resource arena. If any allocations have not been freed, the RA must not be deleted. @Input pArena - the arena to test. @Return IMG_BOOL - IMG_TRUE if is safe to go on and call RA_Delete. ******************************************************************************/ IMG_BOOL RA_TestDelete (RA_ARENA *pArena) { PVR_ASSERT(pArena != IMG_NULL); if (pArena != IMG_NULL) { while (pArena->pHeadSegment != IMG_NULL) { BT *pBT = pArena->pHeadSegment; if (pBT->type != btt_free) { PVR_DPF ((PVR_DBG_ERROR,"RA_TestDelete: detected resource leak!")); PVR_DPF ((PVR_DBG_ERROR,"RA_TestDelete: base = 0x" UINTPTR_FMT " size=0x%" SIZE_T_FMT_LEN "x", pBT->base, pBT->uSize)); return IMG_FALSE; } } } return IMG_TRUE; } /*! ****************************************************************************** @Function RA_Add @Description To add a resource span to an arena. The span must not overlapp with any span previously added to the arena. @Input pArena - the arena to add a span into. @Input base - the base of the span. @Input uSize - the extent of the span. @Return IMG_TRUE - Success IMG_FALSE - failure ******************************************************************************/ IMG_BOOL RA_Add (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_SIZE_T uSize) { PVR_ASSERT (pArena != IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"RA_Add: invalid parameter - pArena")); return IMG_FALSE; } PVR_DPF ((PVR_DBG_MESSAGE, "RA_Add: name='%s', base=0x" UINTPTR_FMT ", size=0x%" SIZE_T_FMT_LEN "x", pArena->name, base, uSize)); uSize = (uSize + pArena->uQuantum - 1) / pArena->uQuantum * pArena->uQuantum; return ((IMG_BOOL)(_InsertResource (pArena, base, uSize) != IMG_NULL)); } /*! ****************************************************************************** @Function RA_Alloc @Description To allocate resource from an arena. @Input pArena - the arena @Input uRequestSize - the size of resource segment requested. @Output pActualSize - the actual size of resource segment allocated, typcially rounded up by quantum. @Output ppsMapping - the user reference associated with allocated resource span. @Input uFlags - flags influencing allocation policy. @Input uAlignment - the uAlignment constraint required for the allocated segment, use 0 if uAlignment not required. @Input uAlignmentOffset @Input pvPrivData - opaque private data passed through to allocator @Input ui32PrivDataLength - length of opaque private data @Output base - allocated base resource @Return IMG_TRUE - success IMG_FALSE - failure ******************************************************************************/ IMG_BOOL RA_Alloc (RA_ARENA *pArena, IMG_SIZE_T uRequestSize, IMG_SIZE_T *pActualSize, BM_MAPPING **ppsMapping, IMG_UINT32 uFlags, IMG_UINT32 uAlignment, IMG_UINT32 uAlignmentOffset, IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength, IMG_UINTPTR_T *base) { IMG_BOOL bResult; IMG_SIZE_T uSize = uRequestSize; PVR_ASSERT (pArena!=IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"RA_Alloc: invalid parameter - pArena")); return IMG_FALSE; } #if defined(VALIDATE_ARENA_TEST) ValidateArena(pArena); #endif #ifdef USE_BM_FREESPACE_CHECK CheckBMFreespace(); #endif if (pActualSize != IMG_NULL) { *pActualSize = uSize; } PVR_DPF ((PVR_DBG_MESSAGE, "RA_Alloc: arena='%s', size=0x%" SIZE_T_FMT_LEN "x(0x%" SIZE_T_FMT_LEN "x), alignment=0x%x, offset=0x%x", pArena->name, uSize, uRequestSize, uAlignment, uAlignmentOffset)); /* if allocation failed then we might have an import source which can provide more resource, else we will have to fail the allocation to the caller. */ bResult = _AttemptAllocAligned (pArena, uSize, ppsMapping, uFlags, uAlignment, uAlignmentOffset, base); if (!bResult) { BM_MAPPING *psImportMapping; IMG_UINTPTR_T import_base; IMG_SIZE_T uImportSize = uSize; /* Ensure that we allocate sufficient space to meet the uAlignment constraint */ if (uAlignment > pArena->uQuantum) { uImportSize += (uAlignment - 1); } /* ensure that we import according to the quanta of this arena */ uImportSize = ((uImportSize + pArena->uQuantum - 1)/pArena->uQuantum)*pArena->uQuantum; bResult = pArena->pImportAlloc (pArena->pImportHandle, uImportSize, &uImportSize, &psImportMapping, uFlags, pvPrivData, ui32PrivDataLength, &import_base); if (bResult) { BT *pBT; pBT = _InsertResourceSpan (pArena, import_base, uImportSize); /* successfully import more resource, create a span to represent it and retry the allocation attempt */ if (pBT == IMG_NULL) { /* insufficient resources to insert the newly acquired span, so free it back again */ pArena->pImportFree(pArena->pImportHandle, import_base, psImportMapping); PVR_DPF ((PVR_DBG_MESSAGE, "RA_Alloc: name='%s', size=0x%" SIZE_T_FMT_LEN "x failed!", pArena->name, uSize)); /* RA_Dump (arena); */ return IMG_FALSE; } pBT->psMapping = psImportMapping; #ifdef RA_STATS pArena->sStatistics.uFreeSegmentCount++; pArena->sStatistics.uFreeResourceCount += uImportSize; pArena->sStatistics.uImportCount++; pArena->sStatistics.uSpanCount++; #endif bResult = _AttemptAllocAligned(pArena, uSize, ppsMapping, uFlags, uAlignment, uAlignmentOffset, base); if (!bResult) { PVR_DPF ((PVR_DBG_MESSAGE, "RA_Alloc: name='%s' uAlignment failed!", pArena->name)); } } } #if defined PVRSRV_DEVMEM_TIME_STATS else { /* If sub-allocation succeeded, the allocation was already mapped to Device MMU. So change timing to '0' */ if (ppsMapping) { (*ppsMapping)->ui32TimeToDevMap = 0; } } #endif #ifdef RA_STATS if (bResult) { pArena->sStatistics.uCumulativeAllocs++; } #endif PVR_DPF ((PVR_DBG_MESSAGE, "RA_Alloc: name='%s', size=0x%" SIZE_T_FMT_LEN "x, *base=0x" UINTPTR_FMT " = %d", pArena->name, uSize, *base, bResult)); /* RA_Dump (pArena); ra_stats (pArena); */ #if defined(VALIDATE_ARENA_TEST) ValidateArena(pArena); #endif return bResult; } #if defined(VALIDATE_ARENA_TEST) /*! ****************************************************************************** @Function ValidateArena @Description Validate an arena by checking that adjacent members of the double linked ordered list are compatible. PVR_DBG_BREAK and PVR_DPF messages are used when an error is detected. NOTE: A DEBUG build is required for PVR_DBG_BREAK to operate. @Input pArena - the arena @Return 0 ******************************************************************************/ IMG_UINT32 ValidateArena(RA_ARENA *pArena) { BT* pSegment; RESOURCE_DESCRIPTOR eNextSpan; pSegment = pArena->pHeadSegment; if (pSegment == IMG_NULL) { return 0; } if (pSegment->eResourceType == IMPORTED_RESOURCE_TYPE) { PVR_ASSERT(pSegment->eResourceSpan == IMPORTED_RESOURCE_SPAN_START); while (pSegment->pNextSegment) { eNextSpan = pSegment->pNextSegment->eResourceSpan; switch (pSegment->eResourceSpan) { case IMPORTED_RESOURCE_SPAN_LIVE: if (!((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || (eNextSpan == IMPORTED_RESOURCE_SPAN_FREE) || (eNextSpan == IMPORTED_RESOURCE_SPAN_END))) { /* error - next span must be live, free or end */ PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; } break; case IMPORTED_RESOURCE_SPAN_FREE: if (!((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || (eNextSpan == IMPORTED_RESOURCE_SPAN_END))) { /* error - next span must be live or end */ PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; } break; case IMPORTED_RESOURCE_SPAN_END: if ((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || (eNextSpan == IMPORTED_RESOURCE_SPAN_FREE) || (eNextSpan == IMPORTED_RESOURCE_SPAN_END)) { /* error - next span cannot be live, free or end */ PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; } break; case IMPORTED_RESOURCE_SPAN_START: if (!((eNextSpan == IMPORTED_RESOURCE_SPAN_LIVE) || (eNextSpan == IMPORTED_RESOURCE_SPAN_FREE))) { /* error - next span must be live or free */ PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; } break; default: PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; break; } pSegment = pSegment->pNextSegment; } } else if (pSegment->eResourceType == NON_IMPORTED_RESOURCE_TYPE) { PVR_ASSERT((pSegment->eResourceSpan == RESOURCE_SPAN_FREE) || (pSegment->eResourceSpan == RESOURCE_SPAN_LIVE)); while (pSegment->pNextSegment) { eNextSpan = pSegment->pNextSegment->eResourceSpan; switch (pSegment->eResourceSpan) { case RESOURCE_SPAN_LIVE: if (!((eNextSpan == RESOURCE_SPAN_FREE) || (eNextSpan == RESOURCE_SPAN_LIVE))) { /* error - next span must be free or live */ PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; } break; case RESOURCE_SPAN_FREE: if (!((eNextSpan == RESOURCE_SPAN_FREE) || (eNextSpan == RESOURCE_SPAN_LIVE))) { /* error - next span must be free or live */ PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; } break; default: PVR_DPF((PVR_DBG_ERROR, "ValidateArena ERROR: adjacent boundary tags %d (base=0x" UINTPTR_FMT ") and %d (base=0x" UINTPTR_FMT ") are incompatible (arena: %s)", pSegment->ui32BoundaryTagID, pSegment->base, pSegment->pNextSegment->ui32BoundaryTagID, pSegment->pNextSegment->base, pArena->name)); PVR_DBG_BREAK; break; } pSegment = pSegment->pNextSegment; } } else { PVR_DPF ((PVR_DBG_ERROR,"ValidateArena ERROR: pSegment->eResourceType unrecognized")); PVR_DBG_BREAK; } return 0; } #endif /*! ****************************************************************************** @Function RA_Free @Description To free a resource segment. @Input pArena - the arena the segment was originally allocated from. @Input base - the base of the resource span to free. @Input bFreeBackingStore - Should backing store memory be freed. @Return None ******************************************************************************/ IMG_VOID RA_Free (RA_ARENA *pArena, IMG_UINTPTR_T base, IMG_BOOL bFreeBackingStore) { BT *pBT; PVR_ASSERT (pArena != IMG_NULL); if (pArena == IMG_NULL) { PVR_DPF ((PVR_DBG_ERROR,"RA_Free: invalid parameter - pArena")); return; } #ifdef USE_BM_FREESPACE_CHECK CheckBMFreespace(); #endif PVR_DPF ((PVR_DBG_MESSAGE, "RA_Free: name='%s', base=0x" UINTPTR_FMT, pArena->name, base)); pBT = (BT *) HASH_Remove (pArena->pSegmentHash, base); PVR_ASSERT (pBT != IMG_NULL); if (pBT) { PVR_ASSERT (pBT->base == base); #ifdef RA_STATS pArena->sStatistics.uCumulativeFrees++; #endif #ifdef USE_BM_FREESPACE_CHECK { IMG_BYTE* p; IMG_BYTE* endp; p = (IMG_BYTE*)pBT->base + SysGetDevicePhysOffset(); endp = (IMG_BYTE*)((IMG_UINT32)(p + pBT->uSize)); while ((IMG_UINT32)p & 3) { *p++ = 0xAA; } while (p < (IMG_BYTE*)((IMG_UINT32)endp & 0xfffffffc)) { *(IMG_UINT32*)p = 0xAAAAAAAA; p += sizeof(IMG_UINT32); } while (p < endp) { *p++ = 0xAA; } PVR_DPF((PVR_DBG_MESSAGE, "BM_FREESPACE_CHECK: RA_Free Cleared %p to %p (size=0x%" SIZE_T_FMT_LEN "x)", (IMG_BYTE*)pBT->base + SysGetDevicePhysOffset(), endp - 1, pBT->uSize)); } #endif _FreeBT (pArena, pBT, bFreeBackingStore); } } /*! ****************************************************************************** @Function RA_GetNextLiveSegment @Description Returns details of the next live resource segments @Input pArena - the arena the segment was originally allocated from. @InOut psSegDetails - rtn details of segments @Return IMG_TRUE if operation succeeded ******************************************************************************/ IMG_BOOL RA_GetNextLiveSegment(IMG_HANDLE hArena, RA_SEGMENT_DETAILS *psSegDetails) { BT *pBT; if (psSegDetails->hSegment) { pBT = (BT *)psSegDetails->hSegment; } else { RA_ARENA *pArena = (RA_ARENA *)hArena; pBT = pArena->pHeadSegment; } /* walk the arena segments and write live one to the buffer */ while (pBT != IMG_NULL) { if (pBT->type == btt_live) { psSegDetails->uiSize = pBT->uSize; psSegDetails->sCpuPhyAddr.uiAddr = pBT->base; psSegDetails->hSegment = (IMG_HANDLE)pBT->pNextSegment; return IMG_TRUE; } pBT = pBT->pNextSegment; } psSegDetails->uiSize = 0; psSegDetails->sCpuPhyAddr.uiAddr = 0; psSegDetails->hSegment = (IMG_HANDLE)IMG_UNDEF; return IMG_FALSE; } #ifdef USE_BM_FREESPACE_CHECK RA_ARENA* pJFSavedArena = IMG_NULL; IMG_VOID CheckBMFreespace(IMG_VOID) { BT *pBT; IMG_BYTE* p; IMG_BYTE* endp; if (pJFSavedArena != IMG_NULL) { for (pBT=pJFSavedArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) { if (pBT->type == btt_free) { p = (IMG_BYTE*)pBT->base + SysGetDevicePhysOffset(); endp = (IMG_BYTE*)((IMG_UINT32)(p + pBT->uSize) & 0xfffffffc); while ((IMG_UINT32)p & 3) { if (*p++ != 0xAA) { fprintf(stderr,"BM_FREESPACE_CHECK: Blank space at %08X has changed to 0x%x\n",p,*(IMG_UINT32*)p); for (;;); break; } } while (p < endp) { if (*(IMG_UINT32*)p != 0xAAAAAAAA) { fprintf(stderr,"BM_FREESPACE_CHECK: Blank space at %08X has changed to 0x%x\n",p,*(IMG_UINT32*)p); for (;;); break; } p += 4; } } } } } #endif #if (defined(CONFIG_PROC_FS) && defined(DEBUG)) || defined (RA_STATS) static IMG_CHAR * _BTType (IMG_INT eType) { switch (eType) { case btt_span: return "span"; case btt_free: return "free"; case btt_live: return "live"; } return "junk"; } #endif /*defined(CONFIG_PROC_FS) && defined(DEBUG)*/ #if defined(ENABLE_RA_DUMP) /*! ****************************************************************************** @Function RA_Dump @Description To dump a readable description of an arena. Diagnostic only. @Input pArena - the arena to dump. @Return None ******************************************************************************/ IMG_VOID RA_Dump (RA_ARENA *pArena) { BT *pBT; PVR_ASSERT (pArena != IMG_NULL); PVR_DPF ((PVR_DBG_MESSAGE,"Arena '%s':", pArena->name)); PVR_DPF ((PVR_DBG_MESSAGE," alloc=%p free=%p handle=%p quantum=%d", pArena->pImportAlloc, pArena->pImportFree, pArena->pImportHandle, pArena->uQuantum)); PVR_DPF ((PVR_DBG_MESSAGE," segment Chain:")); if (pArena->pHeadSegment != IMG_NULL && pArena->pHeadSegment->pPrevSegment != IMG_NULL) PVR_DPF ((PVR_DBG_MESSAGE," error: head boundary tag has invalid pPrevSegment")); if (pArena->pTailSegment != IMG_NULL && pArena->pTailSegment->pNextSegment != IMG_NULL) PVR_DPF ((PVR_DBG_MESSAGE," error: tail boundary tag has invalid pNextSegment")); for (pBT=pArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) { PVR_DPF ((PVR_DBG_MESSAGE,"\tbase=0x" UINTPTR_FMT " size=0x%" SIZE_T_FMT_LEN "x type=%s", pBT->base, pBT->uSize, _BTType (pBT->type))); } #ifdef HASH_TRACE HASH_Dump (pArena->pSegmentHash); #endif } #endif /* #if defined(ENABLE_RA_DUMP) */ #if defined(CONFIG_PROC_FS) && defined(DEBUG) static void RA_ProcSeqShowInfo(struct seq_file *sfile, void* el) { RA_ARENA *pArena = (RA_ARENA *)PVRProcGetData(sfile->private); IMG_UINTPTR_T off = (IMG_UINTPTR_T)el; switch (off) { case 1: seq_printf(sfile, "quantum\t\t\t%" SIZE_T_FMT_LEN "u\n", pArena->uQuantum); break; case 2: seq_printf(sfile, "import_handle\t\t%p\n", pArena->pImportHandle); break; #ifdef RA_STATS case 3: seq_printf(sfile,"span count\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uSpanCount); break; case 4: seq_printf(sfile, "live segment count\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uLiveSegmentCount); break; case 5: seq_printf(sfile, "free segment count\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uFreeSegmentCount); break; case 6: seq_printf(sfile, "free resource count\t%" SIZE_T_FMT_LEN "u (0x%" SIZE_T_FMT_LEN "x)\n", pArena->sStatistics.uFreeResourceCount, pArena->sStatistics.uFreeResourceCount); break; case 7: seq_printf(sfile, "total allocs\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uCumulativeAllocs); break; case 8: seq_printf(sfile, "total frees\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uCumulativeFrees); break; case 9: seq_printf(sfile, "import count\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uImportCount); break; case 10: seq_printf(sfile, "export count\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uExportCount); break; #endif } } static void* RA_ProcSeqOff2ElementInfo(struct seq_file * sfile, loff_t off) { #ifdef RA_STATS if(off <= 9) #else if(off <= 1) #endif return (void*)(IMG_UINTPTR_T)(off+1); return 0; } static void RA_ProcSeqShowRegs(struct seq_file *sfile, void* el) { RA_ARENA *pArena = (RA_ARENA *)PVRProcGetData(sfile->private); BT *pBT = (BT*)el; if (el == PVR_PROC_SEQ_START_TOKEN) { seq_printf(sfile, "Arena \"%s\"\nBase Size Type Ref\n", pArena->name); return; } if (pBT) { seq_printf(sfile, "%p %" SIZE_T_FMT_LEN "x %4s %p\n", (IMG_PVOID)pBT->base, pBT->uSize, _BTType (pBT->type), pBT->psMapping); } } static void* RA_ProcSeqOff2ElementRegs(struct seq_file * sfile, loff_t off) { RA_ARENA *pArena = (RA_ARENA *)PVRProcGetData(sfile->private); BT *pBT = 0; if(off == 0) return PVR_PROC_SEQ_START_TOKEN; for (pBT=pArena->pHeadSegment; --off && pBT; pBT=pBT->pNextSegment); return (void*)pBT; } #endif /* defined(CONFIG_PROC_FS) && defined(DEBUG) */ #ifdef RA_STATS /*! ****************************************************************************** @Function RA_GetStats @Description Gets the arena stats and places in client buffer @Input pArena - the arena to print statistics for. @Input ppszStr - caller string to fill @Input pui32StrLen - length of caller string @Return PVRSRV_ERROR ******************************************************************************/ PVRSRV_ERROR RA_GetStats(RA_ARENA *pArena, IMG_CHAR **ppszStr, IMG_UINT32 *pui32StrLen) { IMG_CHAR *pszStr = *ppszStr; IMG_UINT32 ui32StrLen = *pui32StrLen; IMG_INT32 i32Count; BT *pBT; CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "\nArena '%s':\n", pArena->name); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, " allocCB=%p freeCB=%p handle=%p quantum=%" SIZE_T_FMT_LEN "u\n", pArena->pImportAlloc, pArena->pImportFree, pArena->pImportHandle, pArena->uQuantum); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "span count\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uSpanCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "live segment count\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uLiveSegmentCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "free segment count\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uFreeSegmentCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "free resource count\t%" SIZE_T_FMT_LEN "u (0x%" SIZE_T_FMT_LEN "x)\n", pArena->sStatistics.uFreeResourceCount, pArena->sStatistics.uFreeResourceCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "total allocs\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uCumulativeAllocs); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "total frees\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uCumulativeFrees); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "import count\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uImportCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "export count\t\t%" SIZE_T_FMT_LEN "u\n", pArena->sStatistics.uExportCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, " segment Chain:\n"); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); if (pArena->pHeadSegment != IMG_NULL && pArena->pHeadSegment->pPrevSegment != IMG_NULL) { CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, " error: head boundary tag has invalid pPrevSegment\n"); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); } if (pArena->pTailSegment != IMG_NULL && pArena->pTailSegment->pNextSegment != IMG_NULL) { CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, " error: tail boundary tag has invalid pNextSegment\n"); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); } for (pBT=pArena->pHeadSegment; pBT!=IMG_NULL; pBT=pBT->pNextSegment) { CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "\tbase=0x%p size=0x%" SIZE_T_FMT_LEN "x type=%s ref=%p\n", (void *)pBT->base, pBT->uSize, _BTType(pBT->type), pBT->psMapping); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); } *ppszStr = pszStr; *pui32StrLen = ui32StrLen; return PVRSRV_OK; } PVRSRV_ERROR RA_GetStatsFreeMem(RA_ARENA *pArena, IMG_CHAR **ppszStr, IMG_UINT32 *pui32StrLen) { IMG_CHAR *pszStr = *ppszStr; IMG_UINT32 ui32StrLen = *pui32StrLen; IMG_INT32 i32Count; CHECK_SPACE(ui32StrLen); i32Count = OSSNPrintf(pszStr, 100, "Bytes free: Arena %-30s: %" SIZE_T_FMT_LEN "u (0x%" SIZE_T_FMT_LEN "x)\n", pArena->name, pArena->sStatistics.uFreeResourceCount, pArena->sStatistics.uFreeResourceCount); UPDATE_SPACE(pszStr, i32Count, ui32StrLen); *ppszStr = pszStr; *pui32StrLen = ui32StrLen; return PVRSRV_OK; } #endif /****************************************************************************** End of file (ra.c) ******************************************************************************/