diff options
Diffstat (limited to 'drivers/gpu/pvr/ra.c')
-rw-r--r-- | drivers/gpu/pvr/ra.c | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/drivers/gpu/pvr/ra.c b/drivers/gpu/pvr/ra.c new file mode 100644 index 00000000000..4c71af18839 --- /dev/null +++ b/drivers/gpu/pvr/ra.c @@ -0,0 +1,1164 @@ +/********************************************************************** + * + * Copyright(c) 2008 Imagination Technologies Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful but, except + * as otherwise stated in writing, 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * Imagination Technologies Ltd. <gpl-support@imgtec.com> + * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK + * + ******************************************************************************/ + +#include "services_headers.h" +#include "hash.h" +#include "ra.h" +#include "buffer_manager.h" +#include "osfunc.h" + +#include <linux/kernel.h> +#include "proc.h" + + +#define MINIMUM_HASH_SIZE 64 + +struct BT { + enum bt_type { + btt_span, + btt_free, + btt_live + } type; + + u32 base; + size_t uSize; + + struct BT *pNextSegment; + struct BT *pPrevSegment; + + struct BT *pNextFree; + struct BT *pPrevFree; + + struct BM_MAPPING *psMapping; +}; +struct BT; + +struct RA_ARENA { + char *name; + u32 uQuantum; + IMG_BOOL(*pImportAlloc)(void *, size_t uSize, size_t *pActualSize, + struct BM_MAPPING **ppsMapping, u32 uFlags, + u32 *pBase); + void (*pImportFree)(void *, u32, struct BM_MAPPING *psMapping); + void (*pBackingStoreFree)(void *, u32, u32, void *); + void *pImportHandle; +#define FREE_TABLE_LIMIT 32 + struct BT *aHeadFree[FREE_TABLE_LIMIT]; + struct BT *pHeadSegment; + struct BT *pTailSegment; + struct HASH_TABLE *pSegmentHash; +#ifdef RA_STATS + struct RA_STATISTICS sStatistics; +#endif +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_DEBUG_EXTRA) +#define PROC_NAME_SIZE 32 + char szProcInfoName[PROC_NAME_SIZE]; + char szProcSegsName[PROC_NAME_SIZE]; + IMG_BOOL bInitProcEntry; +#endif +}; + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_DEBUG_EXTRA) +static int RA_DumpSegs(char *page, char **start, off_t off, int count, int *eof, + void *data); +static int RA_DumpInfo(char *page, char **start, off_t off, int count, int *eof, + void *data); +#endif + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_DEBUG_EXTRA) +static char *ReplaceSpaces(char *const pS) +{ + char *pT; + + for (pT = pS; *pT != 0; pT++) + if (*pT == ' ' || *pT == '\t') + *pT = '_'; + + return pS; +} +#endif + +static IMG_BOOL _RequestAllocFail(void *_h, size_t _uSize, size_t *_pActualSize, + struct BM_MAPPING **_ppsMapping, + u32 _uFlags, u32 *_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); + + return IMG_FALSE; +} + +static u32 pvr_log2(size_t n) +{ + u32 l = 0; + n >>= 1; + while (n > 0) { + n >>= 1; + l++; + } + return l; +} + +static enum PVRSRV_ERROR _SegmentListInsertAfter(struct RA_ARENA *pArena, + struct BT *pInsertionPoint, + struct BT *pBT) +{ + PVR_ASSERT(pArena != NULL); + PVR_ASSERT(pInsertionPoint != NULL); + + if ((pInsertionPoint == NULL) || (pArena == NULL)) { + PVR_DPF(PVR_DBG_ERROR, + "_SegmentListInsertAfter: invalid parameters"); + return PVRSRV_ERROR_INVALID_PARAMS; + } + + pBT->pNextSegment = pInsertionPoint->pNextSegment; + pBT->pPrevSegment = pInsertionPoint; + if (pInsertionPoint->pNextSegment == NULL) + pArena->pTailSegment = pBT; + else + pInsertionPoint->pNextSegment->pPrevSegment = pBT; + pInsertionPoint->pNextSegment = pBT; + + return PVRSRV_OK; +} + +static enum PVRSRV_ERROR _SegmentListInsert(struct RA_ARENA *pArena, + struct BT *pBT) +{ + enum PVRSRV_ERROR eError = PVRSRV_OK; + + if (pArena->pHeadSegment == NULL) { + pArena->pHeadSegment = pArena->pTailSegment = pBT; + pBT->pNextSegment = pBT->pPrevSegment = NULL; + } else { + struct BT *pBTScan; + if (pBT->base < pArena->pHeadSegment->base) { + pBT->pNextSegment = pArena->pHeadSegment; + pArena->pHeadSegment->pPrevSegment = pBT; + pArena->pHeadSegment = pBT; + pBT->pPrevSegment = NULL; + } else { + pBTScan = pArena->pHeadSegment; + + while ((pBTScan->pNextSegment != NULL) && + (pBT->base >= pBTScan->pNextSegment->base)) + pBTScan = pBTScan->pNextSegment; + + eError = _SegmentListInsertAfter(pArena, pBTScan, pBT); + if (eError != PVRSRV_OK) + return eError; + } + } + return eError; +} + +static void _SegmentListRemove(struct RA_ARENA *pArena, struct BT *pBT) +{ + if (pBT->pPrevSegment == NULL) + pArena->pHeadSegment = pBT->pNextSegment; + else + pBT->pPrevSegment->pNextSegment = pBT->pNextSegment; + + if (pBT->pNextSegment == NULL) + pArena->pTailSegment = pBT->pPrevSegment; + else + pBT->pNextSegment->pPrevSegment = pBT->pPrevSegment; +} + +static struct BT *_SegmentSplit(struct RA_ARENA *pArena, struct BT *pBT, + size_t uSize) +{ + struct BT *pNeighbour; + + PVR_ASSERT(pArena != NULL); + + if (pArena == NULL) { + PVR_DPF(PVR_DBG_ERROR, + "_SegmentSplit: invalid parameter - pArena"); + return NULL; + } + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(struct BT), + (void **) &pNeighbour, NULL) != PVRSRV_OK) + return NULL; + + pNeighbour->pPrevSegment = pBT; + pNeighbour->pNextSegment = pBT->pNextSegment; + if (pBT->pNextSegment == 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; + return pNeighbour; +} + +static void _FreeListInsert(struct RA_ARENA *pArena, struct BT *pBT) +{ + u32 uIndex; + uIndex = pvr_log2(pBT->uSize); + pBT->type = btt_free; + pBT->pNextFree = pArena->aHeadFree[uIndex]; + pBT->pPrevFree = NULL; + if (pArena->aHeadFree[uIndex] != NULL) + pArena->aHeadFree[uIndex]->pPrevFree = pBT; + pArena->aHeadFree[uIndex] = pBT; +} + +static void _FreeListRemove(struct RA_ARENA *pArena, struct BT *pBT) +{ + u32 uIndex; + uIndex = pvr_log2(pBT->uSize); + if (pBT->pNextFree != NULL) + pBT->pNextFree->pPrevFree = pBT->pPrevFree; + if (pBT->pPrevFree == NULL) + pArena->aHeadFree[uIndex] = pBT->pNextFree; + else + pBT->pPrevFree->pNextFree = pBT->pNextFree; +} + +static struct BT *_BuildSpanMarker(u32 base, size_t uSize) +{ + struct BT *pBT; + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(struct BT), + (void **) &pBT, NULL) != PVRSRV_OK) + return NULL; + + pBT->type = btt_span; + pBT->base = base; + pBT->uSize = uSize; + pBT->psMapping = NULL; + + return pBT; +} + +static struct BT *_BuildBT(u32 base, size_t uSize) +{ + struct BT *pBT; + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(struct BT), + (void **) &pBT, NULL) != PVRSRV_OK) + return NULL; + + pBT->type = btt_free; + pBT->base = base; + pBT->uSize = uSize; + + return pBT; +} + +static struct BT *_InsertResource(struct RA_ARENA *pArena, u32 base, + size_t uSize) +{ + struct BT *pBT; + PVR_ASSERT(pArena != NULL); + if (pArena == NULL) { + PVR_DPF(PVR_DBG_ERROR, + "_InsertResource: invalid parameter - pArena"); + return NULL; + } + + pBT = _BuildBT(base, uSize); + if (pBT != NULL) { + if (_SegmentListInsert(pArena, pBT) != PVRSRV_OK) { + PVR_DPF(PVR_DBG_ERROR, + "_InsertResource: call to _SegmentListInsert failed"); + return NULL; + } + _FreeListInsert(pArena, pBT); +#ifdef RA_STATS + pArena->sStatistics.uTotalResourceCount += uSize; + pArena->sStatistics.uFreeResourceCount += uSize; + pArena->sStatistics.uSpanCount++; +#endif + } + return pBT; +} + +static struct BT *_InsertResourceSpan(struct RA_ARENA *pArena, u32 base, + size_t uSize) +{ + enum PVRSRV_ERROR eError; + struct BT *pSpanStart; + struct BT *pSpanEnd; + struct BT *pBT; + + PVR_ASSERT(pArena != NULL); + if (pArena == NULL) { + PVR_DPF(PVR_DBG_ERROR, + "_InsertResourceSpan: invalid parameter - pArena"); + return NULL; + } + + PVR_DPF(PVR_DBG_MESSAGE, + "RA_InsertResourceSpan: arena='%s', base=0x%x, size=0x%x", + pArena->name, base, uSize); + + pSpanStart = _BuildSpanMarker(base, uSize); + if (pSpanStart == NULL) + goto fail_start; + pSpanEnd = _BuildSpanMarker(base + uSize, 0); + if (pSpanEnd == NULL) + goto fail_end; + + pBT = _BuildBT(base, uSize); + if (pBT == NULL) + goto fail_bt; + + 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; +#endif + return pBT; + +fail_SegListInsert: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct BT), pBT, NULL); +fail_bt: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct BT), pSpanEnd, NULL); +fail_end: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct BT), pSpanStart, NULL); +fail_start: + return NULL; +} + +static void _FreeBT(struct RA_ARENA *pArena, struct BT *pBT, + IMG_BOOL bFreeBackingStore) +{ + struct BT *pNeighbour; + u32 uOrigBase; + size_t uOrigSize; + + PVR_ASSERT(pArena != NULL); + PVR_ASSERT(pBT != NULL); + + if ((pArena == NULL) || (pBT == 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; + + pNeighbour = pBT->pPrevSegment; + if (pNeighbour != 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(struct BT), + pNeighbour, NULL); +#ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount--; +#endif + } + + pNeighbour = pBT->pNextSegment; + if (pNeighbour != 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(struct BT), + pNeighbour, NULL); +#ifdef RA_STATS + pArena->sStatistics.uFreeSegmentCount--; +#endif + } + + if (pArena->pBackingStoreFree != NULL && bFreeBackingStore) { + u32 uRoundedStart, uRoundedEnd; + + uRoundedStart = (uOrigBase / pArena->uQuantum) * + pArena->uQuantum; + + if (uRoundedStart < pBT->base) + uRoundedStart += pArena->uQuantum; + + uRoundedEnd = ((uOrigBase + uOrigSize + pArena->uQuantum - + 1) / pArena->uQuantum) * pArena->uQuantum; + + if (uRoundedEnd > (pBT->base + pBT->uSize)) + uRoundedEnd -= pArena->uQuantum; + + if (uRoundedStart < uRoundedEnd) + pArena->pBackingStoreFree(pArena->pImportHandle, + uRoundedStart, uRoundedEnd, + (void *) 0); + } + + if (pBT->pNextSegment != NULL && pBT->pNextSegment->type == btt_span && + pBT->pPrevSegment != NULL && pBT->pPrevSegment->type == btt_span) { + struct BT *next = pBT->pNextSegment; + struct 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(struct BT), next, + NULL); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct BT), prev, + NULL); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct BT), pBT, + NULL); + } else + _FreeListInsert(pArena, pBT); +} + +static int alloc_from_bt(struct RA_ARENA *arena, struct BT *bt, u32 start, + size_t size, u32 align, + struct BM_MAPPING **new_mapping, u32 *new_base) +{ + _FreeListRemove(arena, bt); + PVR_ASSERT(bt->type == btt_free); +#ifdef RA_STATS + arena->sStatistics.uLiveSegmentCount++; + arena->sStatistics.uFreeSegmentCount--; + arena->sStatistics.uFreeResourceCount -= bt->uSize; +#endif + if (start > bt->base) { + struct BT *next_bt; + + next_bt = _SegmentSplit(arena, bt, start - bt->base); + + if (!next_bt) { + PVR_DPF(PVR_DBG_ERROR, "_AttemptAllocAligned: " + "Front split failed"); + + _FreeListInsert(arena, bt); + return -1; + } + + _FreeListInsert(arena, bt); +#ifdef RA_STATS + arena->sStatistics.uFreeSegmentCount++; + arena->sStatistics.uFreeResourceCount += bt->uSize; +#endif + bt = next_bt; + } + + if (bt->uSize > size) { + struct BT *next_bt; + next_bt = _SegmentSplit(arena, bt, size); + + if (!next_bt) { + PVR_DPF(PVR_DBG_ERROR, "_AttemptAllocAligned: " + "Back split failed"); + + _FreeListInsert(arena, bt); + return -1; + } + + _FreeListInsert(arena, next_bt); +#ifdef RA_STATS + arena->sStatistics.uFreeSegmentCount++; + arena->sStatistics.uFreeResourceCount += next_bt->uSize; +#endif + } + + bt->type = btt_live; + + if (!HASH_Insert(arena->pSegmentHash, bt->base, (u32)bt)) { + _FreeBT(arena, bt, IMG_FALSE); + return -1; + } + + if (new_mapping) + *new_mapping = bt->psMapping; + + *new_base = bt->base; + + return 0; +} + +static IMG_BOOL _AttemptAllocAligned(struct RA_ARENA *pArena, size_t uSize, + struct BM_MAPPING **ppsMapping, u32 uFlags, u32 uAlignment, + u32 *base) +{ + u32 uIndex; + PVR_ASSERT(pArena != NULL); + if (pArena == NULL) { + PVR_DPF(PVR_DBG_ERROR, + "_AttemptAllocAligned: invalid parameter - pArena"); + return IMG_FALSE; + } + + uIndex = pvr_log2(uSize); + + while (uIndex < FREE_TABLE_LIMIT && pArena->aHeadFree[uIndex] == NULL) + uIndex++; + + for (; uIndex < FREE_TABLE_LIMIT; uIndex++) { + struct BT *pBT; + + pBT = pArena->aHeadFree[uIndex]; + if (!pBT) + continue; + + for (; pBT != NULL; pBT = pBT->pNextFree) { + u32 aligned_base; + + if (uAlignment > 1) + aligned_base = (pBT->base + uAlignment - + 1) / uAlignment * uAlignment; + else + aligned_base = pBT->base; + PVR_DPF(PVR_DBG_MESSAGE, + "RA_AttemptAllocAligned: pBT-base=0x%x " + "pBT-size=0x%x alignedbase=0x%x size=0x%x", + pBT->base, pBT->uSize, aligned_base, uSize); + + if (pBT->base + pBT->uSize < aligned_base + uSize) + continue; + + if (pBT->psMapping && pBT->psMapping->ui32Flags != + uFlags) { + PVR_DPF(PVR_DBG_MESSAGE, + "AttemptAllocAligned: mismatch in " + "flags. Import has %x, request was %x", + pBT->psMapping->ui32Flags, uFlags); + continue; + } + + if (alloc_from_bt(pArena, pBT, aligned_base, uSize, + uFlags, ppsMapping, base) < 0) + return IMG_FALSE; + + return IMG_TRUE; + } + } + + return IMG_FALSE; +} + +struct RA_ARENA *RA_Create(char *name, u32 base, size_t uSize, + struct BM_MAPPING *psMapping, size_t uQuantum, + IMG_BOOL(*imp_alloc) (void *, size_t uSize, + size_t *pActualSize, + struct BM_MAPPING **ppsMapping, + u32 _flags, u32 *pBase), + void (*imp_free) (void *, u32, struct BM_MAPPING *), + void(*backingstore_free) (void *, u32, u32, void *), + void *pImportHandle) +{ + struct RA_ARENA *pArena; + struct BT *pBT; + int i; + + PVR_DPF(PVR_DBG_MESSAGE, "RA_Create: " + "name='%s', base=0x%x, uSize=0x%x, alloc=0x%x, free=0x%x", + name, base, uSize, imp_alloc, imp_free); + + if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(*pArena), + (void **) &pArena, NULL) != PVRSRV_OK) + goto arena_fail; + + pArena->name = name; + pArena->pImportAlloc = + (imp_alloc != NULL) ? imp_alloc : _RequestAllocFail; + pArena->pImportFree = imp_free; + pArena->pBackingStoreFree = backingstore_free; + pArena->pImportHandle = pImportHandle; + for (i = 0; i < FREE_TABLE_LIMIT; i++) + pArena->aHeadFree[i] = NULL; + pArena->pHeadSegment = NULL; + pArena->pTailSegment = 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(CONFIG_PVR_DEBUG_EXTRA) + if (strcmp(pArena->name, "") != 0) { + int ret; + int (*pfnCreateProcEntry) (const char *, read_proc_t, + write_proc_t, void *); + + pArena->bInitProcEntry = + !PVRSRVGetInitServerState(PVRSRV_INIT_SERVER_SUCCESSFUL); + + pfnCreateProcEntry = pArena->bInitProcEntry ? CreateProcEntry : + CreatePerProcessProcEntry; + + ret = snprintf(pArena->szProcInfoName, + sizeof(pArena->szProcInfoName), "ra_info_%s", + pArena->name); + if (ret > 0 && ret < sizeof(pArena->szProcInfoName)) { + (void)pfnCreateProcEntry(ReplaceSpaces + (pArena->szProcInfoName), + RA_DumpInfo, NULL, pArena); + } else { + pArena->szProcInfoName[0] = 0; + PVR_DPF(PVR_DBG_ERROR, "RA_Create: " + "couldn't create ra_info proc entry for arena %s", + pArena->name); + } + + ret = snprintf(pArena->szProcSegsName, + sizeof(pArena->szProcSegsName), "ra_segs_%s", + pArena->name); + if (ret > 0 && ret < sizeof(pArena->szProcInfoName)) { + (void)pfnCreateProcEntry(ReplaceSpaces + (pArena->szProcSegsName), + RA_DumpSegs, NULL, pArena); + } else { + pArena->szProcSegsName[0] = 0; + PVR_DPF(PVR_DBG_ERROR, "RA_Create: " + "couldn't create ra_segs proc entry for arena %s", + pArena->name); + } + } +#endif + + pArena->pSegmentHash = HASH_Create(MINIMUM_HASH_SIZE); + if (pArena->pSegmentHash == NULL) + goto hash_fail; + if (uSize > 0) { + uSize = (uSize + uQuantum - 1) / uQuantum * uQuantum; + pBT = _InsertResource(pArena, base, uSize); + if (pBT == NULL) + goto insert_fail; + pBT->psMapping = psMapping; + + } + return pArena; + +insert_fail: + HASH_Delete(pArena->pSegmentHash); +hash_fail: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct RA_ARENA), pArena, + NULL); +arena_fail: + return NULL; +} + +void RA_Delete(struct RA_ARENA *pArena) +{ + u32 uIndex; + + PVR_ASSERT(pArena != NULL); + + if (pArena == 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; uIndex < FREE_TABLE_LIMIT; uIndex++) + pArena->aHeadFree[uIndex] = NULL; + + while (pArena->pHeadSegment != NULL) { + struct BT *pBT = pArena->pHeadSegment; + PVR_ASSERT(pBT->type == btt_free); + _SegmentListRemove(pArena, pBT); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct BT), pBT, + NULL); +#ifdef RA_STATS + pArena->sStatistics.uSpanCount--; +#endif + } +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_DEBUG_EXTRA) + { + void (*pfnRemoveProcEntry) (const char *); + + pfnRemoveProcEntry = + pArena-> + bInitProcEntry ? RemoveProcEntry : + RemovePerProcessProcEntry; + + if (pArena->szProcInfoName[0] != 0) + pfnRemoveProcEntry(pArena->szProcInfoName); + + if (pArena->szProcSegsName[0] != 0) + pfnRemoveProcEntry(pArena->szProcSegsName); + } +#endif + HASH_Delete(pArena->pSegmentHash); + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct RA_ARENA), pArena, + NULL); +} + +IMG_BOOL RA_TestDelete(struct RA_ARENA *pArena) +{ + PVR_ASSERT(pArena != NULL); + + if (pArena != NULL) + while (pArena->pHeadSegment != NULL) { + struct BT *pBT = pArena->pHeadSegment; + if (pBT->type != btt_free) + return IMG_FALSE; + } + + return IMG_TRUE; +} + +IMG_BOOL RA_Add(struct RA_ARENA *pArena, u32 base, size_t uSize) +{ + PVR_ASSERT(pArena != NULL); + + if (pArena == 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%x, size=0x%x", pArena->name, base, + uSize); + + uSize = (uSize + pArena->uQuantum - 1) / + pArena->uQuantum * pArena->uQuantum; + return (IMG_BOOL)(_InsertResource(pArena, base, uSize) != NULL); +} + +IMG_BOOL RA_Alloc(struct RA_ARENA *pArena, size_t uRequestSize, + struct BM_MAPPING **ppsMapping, u32 uFlags, u32 uAlignment, + u32 *base) +{ + IMG_BOOL bResult; + size_t uSize = uRequestSize; + + PVR_ASSERT(pArena != NULL); + + if (pArena == NULL) { + PVR_DPF(PVR_DBG_ERROR, + "RA_Alloc: invalid parameter - pArena"); + return IMG_FALSE; + } + + PVR_DPF(PVR_DBG_MESSAGE, "RA_Alloc: " + "arena='%s', size=0x%x(0x%x), alignment=0x%x", + pArena->name, uSize, uRequestSize, uAlignment); + + bResult = _AttemptAllocAligned(pArena, uSize, ppsMapping, uFlags, + uAlignment, base); + if (!bResult) { + struct BM_MAPPING *psImportMapping; + u32 import_base; + size_t uImportSize = uSize; + + if (uAlignment > pArena->uQuantum) + uImportSize += (uAlignment - 1); + + uImportSize = + ((uImportSize + pArena->uQuantum - 1) / + pArena->uQuantum) * pArena->uQuantum; + + bResult = + pArena->pImportAlloc(pArena->pImportHandle, uImportSize, + &uImportSize, &psImportMapping, uFlags, + &import_base); + if (bResult) { + struct BT *pBT; + pBT = _InsertResourceSpan(pArena, import_base, + uImportSize); + + if (pBT == NULL) { + pArena->pImportFree(pArena->pImportHandle, + import_base, + psImportMapping); + PVR_DPF(PVR_DBG_MESSAGE, "RA_Alloc: " + "name='%s', size=0x%x failed!", + pArena->name, uSize); + + 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, + base); + if (!bResult) + PVR_DPF(PVR_DBG_MESSAGE, "RA_Alloc: " + "name='%s' uAlignment failed!", + pArena->name); + } + } +#ifdef RA_STATS + if (bResult) + pArena->sStatistics.uCumulativeAllocs++; +#endif + + PVR_DPF(PVR_DBG_MESSAGE, + "RA_Alloc: name='%s', size=0x%x, *base=0x%x = %d", + pArena->name, uSize, *base, bResult); + + return bResult; +} + +void RA_Free(struct RA_ARENA *pArena, u32 base, IMG_BOOL bFreeBackingStore) +{ + struct BT *pBT; + + PVR_ASSERT(pArena != NULL); + + if (pArena == NULL) { + PVR_DPF(PVR_DBG_ERROR, "RA_Free: invalid parameter - pArena"); + return; + } + + PVR_DPF(PVR_DBG_MESSAGE, + "RA_Free: name='%s', base=0x%x", pArena->name, base); + + pBT = (struct BT *)HASH_Remove(pArena->pSegmentHash, base); + PVR_ASSERT(pBT != NULL); + + if (pBT) { + PVR_ASSERT(pBT->base == base); + +#ifdef RA_STATS + pArena->sStatistics.uCumulativeFrees++; +#endif + + _FreeBT(pArena, pBT, bFreeBackingStore); + } +} + +IMG_BOOL RA_GetNextLiveSegment(void *hArena, + struct RA_SEGMENT_DETAILS *psSegDetails) +{ + struct BT *pBT; + + if (psSegDetails->hSegment) { + pBT = (struct BT *)psSegDetails->hSegment; + } else { + struct RA_ARENA *pArena = (struct RA_ARENA *)hArena; + pBT = pArena->pHeadSegment; + } + + while (pBT != NULL) { + if (pBT->type == btt_live) { + psSegDetails->uiSize = pBT->uSize; + psSegDetails->sCpuPhyAddr.uiAddr = pBT->base; + psSegDetails->hSegment = (void *) pBT->pNextSegment; + + return IMG_TRUE; + } + + pBT = pBT->pNextSegment; + } + + psSegDetails->uiSize = 0; + psSegDetails->sCpuPhyAddr.uiAddr = 0; + psSegDetails->hSegment = (void *) -1; + + return IMG_FALSE; +} + +#if (defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_DEBUG_EXTRA)) || \ + defined(RA_STATS) +static char *_BTType(int eType) +{ + switch (eType) { + case btt_span: + return "span"; + case btt_free: + return "free"; + case btt_live: + return "live"; + } + return "junk"; +} +#endif + +#if defined(ENABLE_RA_DUMP) +void RA_Dump(struct RA_ARENA *pArena) +{ + struct BT *pBT; + PVR_ASSERT(pArena != NULL); + PVR_DPF(PVR_DBG_MESSAGE, "Arena '%s':", pArena->name); + PVR_DPF(PVR_DBG_MESSAGE, + " alloc=%08X free=%08X handle=%08X quantum=%d", + pArena->pImportAlloc, pArena->pImportFree, + pArena->pImportHandle, pArena->uQuantum); + PVR_DPF(PVR_DBG_MESSAGE, " segment Chain:"); + if (pArena->pHeadSegment != NULL && + pArena->pHeadSegment->pPrevSegment != NULL) + PVR_DPF(PVR_DBG_MESSAGE, + " error: head boundary tag has invalid pPrevSegment"); + if (pArena->pTailSegment != NULL && + pArena->pTailSegment->pNextSegment != NULL) + PVR_DPF(PVR_DBG_MESSAGE, + " error: tail boundary tag has invalid pNextSegment"); + + for (pBT = pArena->pHeadSegment; pBT != NULL; pBT = pBT->pNextSegment) + PVR_DPF(PVR_DBG_MESSAGE, + "\tbase=0x%x size=0x%x type=%s ref=%08X", + (u32) pBT->base, pBT->uSize, _BTType(pBT->type), + pBT->pRef); + +} +#endif + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_PVR_DEBUG_EXTRA) +static int RA_DumpSegs(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + struct BT *pBT = NULL; + int len = 0; + struct RA_ARENA *pArena = (struct RA_ARENA *)data; + + if (count < 80) { + *start = (char *)0; + return 0; + } + *eof = 0; + *start = (char *)1; + if (off == 0) + return printAppend(page, count, 0, + "Arena \"%s\"\nBase Size Type Ref\n", + pArena->name); + for (pBT = pArena->pHeadSegment; --off && pBT; + pBT = pBT->pNextSegment) + ; + if (pBT) + len = printAppend(page, count, 0, "%08x %8x %4s %08x\n", + (unsigned)pBT->base, (unsigned)pBT->uSize, + _BTType(pBT->type), (unsigned)pBT->psMapping); + else + *eof = 1; + return len; +} + +static int RA_DumpInfo(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + int len = 0; + struct RA_ARENA *pArena = (struct RA_ARENA *)data; + + if (count < 80) { + *start = (char *)0; + return 0; + } + *eof = 0; + switch (off) { + case 0: + len = printAppend(page, count, 0, "quantum\t\t\t%u\n", + pArena->uQuantum); + break; + case 1: + len = printAppend(page, count, 0, "import_handle\t\t%08X\n", + (unsigned)pArena->pImportHandle); + break; +#ifdef RA_STATS + case 2: + len = printAppend(page, count, 0, "span count\t\t%u\n", + pArena->sStatistics.uSpanCount); + break; + case 3: + len = printAppend(page, count, 0, "live segment count\t%u\n", + pArena->sStatistics.uLiveSegmentCount); + break; + case 4: + len = printAppend(page, count, 0, "free segment count\t%u\n", + pArena->sStatistics.uFreeSegmentCount); + break; + case 5: + len = printAppend(page, count, 0, + "free resource count\t%u (0x%x)\n", + pArena->sStatistics.uFreeResourceCount, + (unsigned)pArena->sStatistics. + uFreeResourceCount); + break; + case 6: + len = printAppend(page, count, 0, "total allocs\t\t%u\n", + pArena->sStatistics.uCumulativeAllocs); + break; + case 7: + len = printAppend(page, count, 0, "total frees\t\t%u\n", + pArena->sStatistics.uCumulativeFrees); + break; + case 8: + len = printAppend(page, count, 0, "import count\t\t%u\n", + pArena->sStatistics.uImportCount); + break; + case 9: + len = printAppend(page, count, 0, "export count\t\t%u\n", + pArena->sStatistics.uExportCount); + break; +#endif + + default: + *eof = 1; + } + *start = (char *)1; + return len; +} +#endif + +#ifdef RA_STATS +enum PVRSRV_ERROR RA_GetStats(struct RA_ARENA *pArena, char **ppszStr, + u32 *pui32StrLen) +{ + char *pszStr = *ppszStr; + u32 ui32StrLen = *pui32StrLen; + s32 i32Count; + struct 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=%08X freeCB=%08X handle=%08X quantum=%d\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%lu\n", + pArena->sStatistics.uSpanCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "live segment count\t%lu\n", + pArena->sStatistics.uLiveSegmentCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "free segment count\t%lu\n", + pArena->sStatistics.uFreeSegmentCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "free resource count\t%lu (0x%x)\n", + pArena->sStatistics.uFreeResourceCount, + (unsigned)pArena->sStatistics.uFreeResourceCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "total allocs\t\t%lu\n", + pArena->sStatistics.uCumulativeAllocs); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "total frees\t\t%lu\n", + pArena->sStatistics.uCumulativeFrees); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "import count\t\t%lu\n", + pArena->sStatistics.uImportCount); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, "export count\t\t%lu\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 != NULL && + pArena->pHeadSegment->pPrevSegment != NULL) { + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, + " error: head boundary tag has invalid pPrevSegment\n"); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + if (pArena->pTailSegment != NULL && + pArena->pTailSegment->pNextSegment != 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 != NULL; + pBT = pBT->pNextSegment) { + CHECK_SPACE(ui32StrLen); + i32Count = OSSNPrintf(pszStr, 100, + "\tbase=0x%x size=0x%x type=%s ref=%08X\n", + (u32) pBT->base, pBT->uSize, _BTType(pBT->type), + pBT->psMapping); + UPDATE_SPACE(pszStr, i32Count, ui32StrLen); + } + + *ppszStr = pszStr; + *pui32StrLen = ui32StrLen; + + return PVRSRV_OK; +} +#endif |