diff options
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_db.c')
-rw-r--r-- | drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_db.c | 1877 |
1 files changed, 1877 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_db.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_db.c new file mode 100644 index 000000000000..49c261a633f0 --- /dev/null +++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_db.c @@ -0,0 +1,1877 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2018 Vivante Corporation +* +* 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. +* +* 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. 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. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2018 Vivante Corporation +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* 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. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_DATABASE + +/******************************************************************************* +***** Private fuctions ********************************************************/ + +#define _GetSlot(database, x) \ + (gctUINT32)(gcmPTR_TO_UINT64(x) % gcmCOUNTOF(database->list)) + +/******************************************************************************* +** gckKERNEL_FindDatabase +** +** Find a database identified by a process ID and move it to the head of the +** hash list. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID that identifies the database. +** +** gctBOOL LastProcessID +** gcvTRUE if searching for the last known process ID. gcvFALSE if +** we need to search for the process ID specified by the ProcessID +** argument. +** +** OUTPUT: +** +** gcsDATABASE_PTR * Database +** Pointer to a variable receiving the database structure pointer on +** success. +*/ +gceSTATUS +gckKERNEL_FindDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctBOOL LastProcessID, + OUT gcsDATABASE_PTR * Database + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database, previous; + gctSIZE_T slot; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d LastProcessID=%d", + Kernel, ProcessID, LastProcessID); + + /* Compute the hash for the database. */ + slot = ProcessID % gcmCOUNTOF(Kernel->db->db); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Check whether we are getting the last known database. */ + if (LastProcessID) + { + /* Use last database. */ + database = Kernel->db->lastDatabase; + + if (database == gcvNULL) + { + /* Database not found. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + } + else + { + /* Walk the hash list. */ + for (previous = gcvNULL, database = Kernel->db->db[slot]; + database != gcvNULL; + database = database->next) + { + if (database->processID == ProcessID) + { + /* Found it! */ + break; + } + + previous = database; + } + + if (database == gcvNULL) + { + /* Database not found. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + if (previous != gcvNULL) + { + /* Move database to the head of the hash list. */ + previous->next = database->next; + database->next = Kernel->db->db[slot]; + Kernel->db->db[slot] = database; + } + } + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Return the database. */ + *Database = database; + + /* Success. */ + gcmkFOOTER_ARG("*Database=0x%x", *Database); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_DeinitDatabase +** +** De-init a database structure. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to the database structure to deinit. +** +** OUTPUT: +** +** Nothing. +*/ +static gceSTATUS +gckKERNEL_DeinitDatabase( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database + ) +{ + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x", Kernel, Database); + + if (Database) + { + Database->deleted = gcvFALSE; + + /* Destory handle db. */ + if (Database->refs) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, Database->refs)); + Database->refs = gcvNULL; + } + + if (Database->handleDatabase) + { + gcmkVERIFY_OK(gckKERNEL_DestroyIntegerDatabase(Kernel, Database->handleDatabase)); + Database->handleDatabase = gcvNULL; + } + + if (Database->handleDatabaseMutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Database->handleDatabaseMutex)); + Database->handleDatabaseMutex = gcvNULL; + } + +#if gcdPROCESS_ADDRESS_SPACE + if (Database->mmu) + { + gcmkONERROR(gckEVENT_DestroyMmu(Kernel->eventObj, Database->mmu, gcvKERNEL_PIXEL)); + Database->mmu = gcvNULL; + } +#endif + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** gckKERNEL_NewRecord +** +** Create a new database record structure and insert it to the head of the +** database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to a database structure. +** +** OUTPUT: +** +** gcsDATABASE_RECORD_PTR * Record +** Pointer to a variable receiving the database record structure +** pointer on success. +*/ +static gceSTATUS +gckKERNEL_NewRecord( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database, + IN gctUINT32 Slot, + OUT gcsDATABASE_RECORD_PTR * Record + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_RECORD_PTR record = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x", Kernel, Database); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (Kernel->db->freeRecord != gcvNULL) + { + /* Allocate the record from the free list. */ + record = Kernel->db->freeRecord; + Kernel->db->freeRecord = record->next; + } + else + { + gctPOINTER pointer = gcvNULL; + + /* Allocate the record from the heap. */ + gcmkONERROR(gckOS_Allocate(Kernel->os, + gcmSIZEOF(gcsDATABASE_RECORD), + &pointer)); + + record = pointer; + } + + /* Insert the record in the database. */ + record->next = Database->list[Slot]; + Database->list[Slot] = record; + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Return the record. */ + *Record = record; + + /* Success. */ + gcmkFOOTER_ARG("*Record=0x%x", *Record); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + if (record != gcvNULL) + { + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, record)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_DeleteRecord +** +** Remove a database record from the database and delete its structure. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to a database structure. +** +** gceDATABASE_TYPE Type +** Type of the record to remove. +** +** gctPOINTER Data +** Data of the record to remove. +** +** OUTPUT: +** +** gctSIZE_T_PTR Bytes +** Pointer to a variable that receives the size of the record deleted. +** Can be gcvNULL if the size is not required. +*/ +static gceSTATUS +gckKERNEL_DeleteRecord( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Data, + OUT gctSIZE_T_PTR Bytes OPTIONAL + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_RECORD_PTR record, previous; + gctUINT32 slot = _GetSlot(Database, Data); + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x", + Kernel, Database, Type, Data); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Scan the database for this record. */ + for (record = Database->list[slot], previous = gcvNULL; + record != gcvNULL; + record = record->next + ) + { + if ((record->type == Type) + && (record->data == Data) + ) + { + /* Found it! */ + break; + } + + previous = record; + } + + if (record == gcvNULL) + { + /* Ouch! This record is not found? */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + if (Bytes != gcvNULL) + { + /* Return size of record. */ + *Bytes = record->bytes; + } + + /* Remove record from database. */ + if (previous == gcvNULL) + { + Database->list[slot] = record->next; + } + else + { + previous->next = record->next; + } + + /* Insert record in free list. */ + record->next = Kernel->db->freeRecord; + Kernel->db->freeRecord = record; + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_FindRecord +** +** Find a database record from the database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to a database structure. +** +** gceDATABASE_TYPE Type +** Type of the record to remove. +** +** gctPOINTER Data +** Data of the record to remove. +** +** OUTPUT: +** +** gctSIZE_T_PTR Bytes +** Pointer to a variable that receives the size of the record deleted. +** Can be gcvNULL if the size is not required. +*/ +static gceSTATUS +gckKERNEL_FindRecord( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Data, + OUT gcsDATABASE_RECORD_PTR Record + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_RECORD_PTR record; + gctUINT32 slot = _GetSlot(Database, Data); + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x", + Kernel, Database, Type, Data); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Scan the database for this record. */ + for (record = Database->list[slot]; + record != gcvNULL; + record = record->next + ) + { + if ((record->type == Type) + && (record->data == Data) + ) + { + /* Found it! */ + break; + } + } + + if (record == gcvNULL) + { + /* Ouch! This record is not found? */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + if (Record != gcvNULL) + { + /* Return information of record. */ + gcmkONERROR( + gckOS_MemCopy(Record, record, sizeof(gcsDATABASE_RECORD))); + } + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_ARG("Record=0x%x", Record); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +***** Public API **************************************************************/ + +/******************************************************************************* +** gckKERNEL_CreateProcessDB +** +** Create a new process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_CreateProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gcsDATABASE_PTR database = gcvNULL; + gctPOINTER pointer = gcvNULL; + gctBOOL acquired = gcvFALSE; + gctSIZE_T slot; + gctUINT32 i; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Compute the hash for the database. */ + slot = ProcessID % gcmCOUNTOF(Kernel->db->db); + + /* Acquire the database mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Walk the hash list. */ + for (database = Kernel->db->db[slot]; + database != gcvNULL; + database = database->next) + { + if (database->processID == ProcessID) + { + gctINT32 oldVal = 0; + + if (database->deleted) + { + gcmkFATAL("%s(%d): DB of Process=0x%x cannot be reentered since it was in deletion\n", + __FUNCTION__, __LINE__, ProcessID); + gcmkONERROR(gcvSTATUS_INVALID_REQUEST); + } + + gcmkVERIFY_OK(gckOS_AtomIncrement(Kernel->os, database->refs, &oldVal)); + goto OnExit; + } + } + + if (Kernel->db->freeDatabase) + { + /* Allocate a database from the free list. */ + database = Kernel->db->freeDatabase; + Kernel->db->freeDatabase = database->next; + } + else + { + /* Allocate a new database from the heap. */ + gcmkONERROR(gckOS_Allocate(Kernel->os, + gcmSIZEOF(gcsDATABASE), + &pointer)); + + gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsDATABASE)); + + database = pointer; + + gcmkONERROR(gckOS_CreateMutex(Kernel->os, &database->counterMutex)); + } + + /* Initialize the database. */ + /* Save the hash slot. */ + database->slot = slot; + database->processID = ProcessID; + database->vidMem.bytes = 0; + database->vidMem.maxBytes = 0; + database->vidMem.totalBytes = 0; + database->nonPaged.bytes = 0; + database->nonPaged.maxBytes = 0; + database->nonPaged.totalBytes = 0; + database->contiguous.bytes = 0; + database->contiguous.maxBytes = 0; + database->contiguous.totalBytes = 0; + database->mapMemory.bytes = 0; + database->mapMemory.maxBytes = 0; + database->mapMemory.totalBytes = 0; + database->mapUserMemory.bytes = 0; + database->mapUserMemory.maxBytes = 0; + database->mapUserMemory.totalBytes = 0; + + for (i = 0; i < gcmCOUNTOF(database->list); i++) + { + database->list[i] = gcvNULL; + } + + for (i = 0; i < gcvSURF_NUM_TYPES; i++) + { + database->vidMemType[i].bytes = 0; + database->vidMemType[i].maxBytes = 0; + database->vidMemType[i].totalBytes = 0; + } + + for (i = 0; i < gcvPOOL_NUMBER_OF_POOLS; i++) + { + database->vidMemPool[i].bytes = 0; + database->vidMemPool[i].maxBytes = 0; + database->vidMemPool[i].totalBytes = 0; + } + + gcmkASSERT(database->refs == gcvNULL); + gcmkONERROR(gckOS_AtomConstruct(Kernel->os, &database->refs)); + gcmkONERROR(gckOS_AtomSet(Kernel->os, database->refs, 1)); + + gcmkASSERT(database->handleDatabase == gcvNULL); + gcmkONERROR(gckKERNEL_CreateIntegerDatabase(Kernel, &database->handleDatabase)); + + gcmkASSERT(database->handleDatabaseMutex == gcvNULL); + gcmkONERROR(gckOS_CreateMutex(Kernel->os, &database->handleDatabaseMutex)); + +#if gcdPROCESS_ADDRESS_SPACE + gcmkASSERT(database->mmu == gcvNULL); + gcmkONERROR(gckMMU_Construct(Kernel, gcdMMU_SIZE, &database->mmu)); +#endif + +#if gcdSECURE_USER + { + gctINT idx; + gcskSECURE_CACHE * cache = &database->cache; + + /* Setup the linked list of cache nodes. */ + for (idx = 1; idx <= gcdSECURE_CACHE_SLOTS; ++idx) + { + cache->cache[idx].logical = gcvNULL; + +#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE + cache->cache[idx].prev = &cache->cache[idx - 1]; + cache->cache[idx].next = &cache->cache[idx + 1]; +# endif +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + cache->cache[idx].nextHash = gcvNULL; + cache->cache[idx].prevHash = gcvNULL; +# endif + } + +#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE + /* Setup the head and tail of the cache. */ + cache->cache[0].next = &cache->cache[1]; + cache->cache[0].prev = &cache->cache[gcdSECURE_CACHE_SLOTS]; + cache->cache[0].logical = gcvNULL; + + /* Fix up the head and tail pointers. */ + cache->cache[0].next->prev = &cache->cache[0]; + cache->cache[0].prev->next = &cache->cache[0]; +# endif + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + /* Zero out the hash table. */ + for (idx = 0; idx < gcmCOUNTOF(cache->hash); ++idx) + { + cache->hash[idx].logical = gcvNULL; + cache->hash[idx].nextHash = gcvNULL; + } +# endif + + /* Initialize cache index. */ + cache->cacheIndex = gcvNULL; + cache->cacheFree = 1; + cache->cacheStamp = 0; + } +#endif + + /* Insert the database into the hash. */ + database->next = Kernel->db->db[slot]; + Kernel->db->db[slot] = database; + + /* Reset idle timer. */ + Kernel->db->lastIdle = 0; + +OnError: + if (gcmIS_ERROR(status)) + { + gcmkVERIFY_OK(gckKERNEL_DeinitDatabase(Kernel, database)); + + if (pointer) + { + gcmkOS_SAFE_FREE(Kernel->os, pointer); + } + } + +OnExit: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_AddProcessDB +** +** Add a record to a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gceDATABASE_TYPE TYPE +** Type of the record to add. +** +** gctPOINTER Pointer +** Data of the record to add. +** +** gctPHYS_ADDR Physical +** Physical address of the record to add. +** +** gctSIZE_T Size +** Size of the record to add. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_AddProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Size + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcsDATABASE_RECORD_PTR record = gcvNULL; + gcsDATABASE_COUNTERS * count; + gctUINT32 vidMemType; + gcePOOL vidMemPool; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x " + "Physical=0x%x Size=%lu", + Kernel, ProcessID, Type, Pointer, Physical, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Decode type. */ + vidMemType = (Type & gcdDB_VIDEO_MEMORY_TYPE_MASK) >> gcdDB_VIDEO_MEMORY_TYPE_SHIFT; + vidMemPool = (Type & gcdDB_VIDEO_MEMORY_POOL_MASK) >> gcdDB_VIDEO_MEMORY_POOL_SHIFT; + + Type &= gcdDATABASE_TYPE_MASK; + + /* Special case the idle record. */ + if (Type == gcvDB_IDLE) + { + gctUINT64 time; + + /* Get the current profile time. */ + gcmkONERROR(gckOS_GetProfileTick(&time)); + + if ((ProcessID == 0) && (Kernel->db->lastIdle != 0)) + { + /* Out of idle, adjust time it was idle. */ + Kernel->db->idleTime += time - Kernel->db->lastIdle; + Kernel->db->lastIdle = 0; + } + else if (ProcessID == 1) + { + /* Save current idle time. */ + Kernel->db->lastIdle = time; + } + +#if gcdDYNAMIC_SPEED + { + /* Test for first call. */ + if (Kernel->db->lastSlowdown == 0) + { + /* Save milliseconds. */ + Kernel->db->lastSlowdown = time; + Kernel->db->lastSlowdownIdle = Kernel->db->idleTime; + } + else + { + /* Compute ellapsed time in milliseconds. */ + gctUINT delta = gckOS_ProfileToMS(time - Kernel->db->lastSlowdown); + + /* Test for end of period. */ + if (delta >= gcdDYNAMIC_SPEED) + { + /* Compute number of idle milliseconds. */ + gctUINT idle = gckOS_ProfileToMS( + Kernel->db->idleTime - Kernel->db->lastSlowdownIdle); + + /* Broadcast to slow down the GPU. */ + gcmkONERROR(gckOS_BroadcastCalibrateSpeed(Kernel->os, + Kernel->hardware, + idle, + delta)); + + /* Save current time. */ + Kernel->db->lastSlowdown = time; + Kernel->db->lastSlowdownIdle = Kernel->db->idleTime; + } + } + } +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Create a new record in the database. */ + gcmkONERROR(gckKERNEL_NewRecord(Kernel, database, _GetSlot(database, Pointer), &record)); + + /* Initialize the record. */ + record->kernel = Kernel; + record->type = Type; + record->data = Pointer; + record->physical = Physical; + record->bytes = Size; + + /* Get pointer to counters. */ + switch (Type) + { + case gcvDB_VIDEO_MEMORY: + count = &database->vidMem; + break; + + case gcvDB_NON_PAGED: + count = &database->nonPaged; + break; + + case gcvDB_CONTIGUOUS: + count = &database->contiguous; + break; + + case gcvDB_MAP_MEMORY: + count = &database->mapMemory; + break; + + case gcvDB_MAP_USER_MEMORY: + count = &database->mapUserMemory; + break; + + default: + count = gcvNULL; + break; + } + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, database->counterMutex, gcvINFINITE)); + + if (count != gcvNULL) + { + /* Adjust counters. */ + count->totalBytes += Size; + count->bytes += Size; + count->allocCount++; + + if (count->bytes > count->maxBytes) + { + count->maxBytes = count->bytes; + } + } + + if (Type == gcvDB_VIDEO_MEMORY) + { + count = &database->vidMemType[vidMemType]; + + /* Adjust counters. */ + count->totalBytes += Size; + count->bytes += Size; + count->allocCount++; + + if (count->bytes > count->maxBytes) + { + count->maxBytes = count->bytes; + } + + count = &database->vidMemPool[vidMemPool]; + + /* Adjust counters. */ + count->totalBytes += Size; + count->bytes += Size; + count->allocCount++; + + if (count->bytes > count->maxBytes) + { + count->maxBytes = count->bytes; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, database->counterMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_RemoveProcessDB +** +** Remove a record from a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gceDATABASE_TYPE TYPE +** Type of the record to remove. +** +** gctPOINTER Pointer +** Data of the record to remove. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_RemoveProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gctSIZE_T bytes = 0; + gctUINT32 vidMemType; + gcePOOL vidMemPool; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x", + Kernel, ProcessID, Type, Pointer); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + /* Decode type. */ + vidMemType = (Type & gcdDB_VIDEO_MEMORY_TYPE_MASK) >> gcdDB_VIDEO_MEMORY_TYPE_SHIFT; + vidMemPool = (Type & gcdDB_VIDEO_MEMORY_POOL_MASK) >> gcdDB_VIDEO_MEMORY_POOL_SHIFT; + + Type &= gcdDATABASE_TYPE_MASK; + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Delete the record. */ + gcmkONERROR( + gckKERNEL_DeleteRecord(Kernel, database, Type, Pointer, &bytes)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, database->counterMutex, gcvINFINITE)); + + /* Update counters. */ + switch (Type) + { + case gcvDB_VIDEO_MEMORY: + database->vidMem.bytes -= bytes; + database->vidMem.freeCount++; + database->vidMemType[vidMemType].bytes -= bytes; + database->vidMemType[vidMemType].freeCount++; + database->vidMemPool[vidMemPool].bytes -= bytes; + database->vidMemPool[vidMemPool].freeCount++; + break; + + case gcvDB_NON_PAGED: + database->nonPaged.bytes -= bytes; + database->nonPaged.freeCount++; + break; + + case gcvDB_CONTIGUOUS: + database->contiguous.bytes -= bytes; + database->contiguous.freeCount++; + break; + + case gcvDB_MAP_MEMORY: + database->mapMemory.bytes -= bytes; + database->mapMemory.freeCount++; + break; + + case gcvDB_MAP_USER_MEMORY: + database->mapUserMemory.bytes -= bytes; + database->mapUserMemory.freeCount++; + break; + + default: + break; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, database->counterMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_FindProcessDB +** +** Find a record from a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gceDATABASE_TYPE TYPE +** Type of the record to remove. +** +** gctPOINTER Pointer +** Data of the record to remove. +** +** OUTPUT: +** +** gcsDATABASE_RECORD_PTR Record +** Copy of record. +*/ +gceSTATUS +gckKERNEL_FindProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 ThreadID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer, + OUT gcsDATABASE_RECORD_PTR Record + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x", + Kernel, ProcessID, ThreadID, Type, Pointer); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Find the record. */ + gcmkONERROR( + gckKERNEL_FindRecord(Kernel, database, Type, Pointer, Record)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_DestroyProcessDB +** +** Destroy a process database. If the database contains any records, the data +** inside those records will be deleted as well. This aids in the cleanup if +** a process has died unexpectedly or has memory leaks. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_DestroyProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gckKERNEL kernel = Kernel; + gcsDATABASE_PTR previous = gcvNULL; + gcsDATABASE_PTR database = gcvNULL; + gcsDATABASE_PTR db = gcvNULL; + gctBOOL acquired = gcvFALSE; + gctSIZE_T slot; + gctUINT32 i; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Compute the hash for the database. */ + slot = ProcessID % gcmCOUNTOF(Kernel->db->db); + + /* Acquire the database mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Walk the hash list. */ + for (database = Kernel->db->db[slot]; + database != gcvNULL; + database = database->next) + { + if (database->processID == ProcessID) + { + break; + } + } + + if (database) + { + gctINT32 oldVal = 0; + gcmkONERROR(gckOS_AtomDecrement(Kernel->os, database->refs, &oldVal)); + if (oldVal != 1) + { + goto OnExit; + } + + /* Mark it for delete so disallow reenter until really delete it */ + gcmkASSERT(!database->deleted); + database->deleted = gcvTRUE; + } + else + { + gcmkFATAL("%s(%d): DB destroy of Process=0x%x cannot match with creation\n", + __FUNCTION__, __LINE__, ProcessID); + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + /* Cannot remove the database from the hash list + ** since later records deinit need to access from the hash + */ + + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + acquired = gcvFALSE; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): VidMem: total=%lu max=%lu", + ProcessID, database->vidMem.totalBytes, + database->vidMem.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): NonPaged: total=%lu max=%lu", + ProcessID, database->nonPaged.totalBytes, + database->nonPaged.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Contiguous: total=%lu max=%lu", + ProcessID, database->contiguous.totalBytes, + database->contiguous.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Idle time=%llu", + ProcessID, Kernel->db->idleTime); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Map: total=%lu max=%lu", + ProcessID, database->mapMemory.totalBytes, + database->mapMemory.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Map: total=%lu max=%lu", + ProcessID, database->mapUserMemory.totalBytes, + database->mapUserMemory.maxBytes); + + if (database->list != gcvNULL) + { + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "Process %d has entries in its database:", + ProcessID); + } + + for (i = 0; i < gcmCOUNTOF(database->list); i++) + { + gcsDATABASE_RECORD_PTR record, next; + + /* Walk all records. */ + for (record = database->list[i]; record != gcvNULL; record = next) + { + gctBOOL asynchronous = gcvTRUE; + gckVIDMEM_NODE nodeObject; + gctPHYS_ADDR physical; + gctUINT32 handle; + + /* Next next record. */ + next = record->next; + + /* Dispatch on record type. */ + switch (record->type) + { + case gcvDB_VIDEO_MEMORY: + gcmkERR_BREAK(gckVIDMEM_HANDLE_Lookup(record->kernel, + ProcessID, + gcmPTR2INT32(record->data), + &nodeObject)); + + /* Free the video memory. */ + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(record->kernel, + ProcessID, + gcmPTR2INT32(record->data))); + + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(record->kernel, + nodeObject)); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: VIDEO_MEMORY 0x%x (status=%d)", + record->data, status); + break; + + case gcvDB_NON_PAGED: + physical = gcmNAME_TO_PTR(record->physical); + /* Unmap user logical memory first. */ + status = gckOS_UnmapUserLogical(Kernel->os, + physical, + record->bytes, + record->data); + + /* Free the non paged memory. */ + status = gckEVENT_FreeNonPagedMemory(record->kernel->eventObj, + record->bytes, + physical, + record->data, + gcvKERNEL_PIXEL); + gcmRELEASE_NAME(record->physical); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: NON_PAGED 0x%x, bytes=%lu (status=%d)", + record->data, record->bytes, status); + break; + + case gcvDB_COMMAND_BUFFER: + /* Free the command buffer. */ + status = gckEVENT_DestroyVirtualCommandBuffer(record->kernel->eventObj, + record->bytes, + gcmNAME_TO_PTR(record->physical), + record->data, + gcvKERNEL_PIXEL); + gcmRELEASE_NAME(record->physical); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: COMMAND_BUFFER 0x%x, bytes=%lu (status=%d)", + record->data, record->bytes, status); + break; + + case gcvDB_CONTIGUOUS: + physical = gcmNAME_TO_PTR(record->physical); + /* Unmap user logical memory first. */ + status = gckOS_UnmapUserLogical(Kernel->os, + physical, + record->bytes, + record->data); + + /* Free the contiguous memory. */ + status = gckEVENT_FreeContiguousMemory(record->kernel->eventObj, + record->bytes, + physical, + record->data, + gcvKERNEL_PIXEL); + gcmRELEASE_NAME(record->physical); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: CONTIGUOUS 0x%x bytes=%lu (status=%d)", + record->data, record->bytes, status); + break; + + case gcvDB_SIGNAL: +#if USE_NEW_LINUX_SIGNAL + status = gcvSTATUS_NOT_SUPPORTED; +#else + /* Free the user signal. */ + status = gckOS_DestroyUserSignal(Kernel->os, + gcmPTR2INT32(record->data)); +#endif /* USE_NEW_LINUX_SIGNAL */ + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: SIGNAL %d (status=%d)", + (gctINT)(gctUINTPTR_T)record->data, status); + break; + + case gcvDB_VIDEO_MEMORY_LOCKED: + handle = gcmPTR2INT32(record->data); + + gcmkERR_BREAK(gckVIDMEM_HANDLE_Lookup(record->kernel, + ProcessID, + handle, + &nodeObject)); + + /* Unlock what we still locked */ + status = gckVIDMEM_Unlock(record->kernel, + nodeObject, + nodeObject->type, + &asynchronous); + +#if gcdENABLE_VG + if (record->kernel->core == gcvCORE_VG) + { + if (gcmIS_SUCCESS(status) && (gcvTRUE == asynchronous)) + { + status = gckVIDMEM_Unlock(record->kernel, + nodeObject, + nodeObject->type, + gcvNULL); + } + + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(record->kernel, + ProcessID, + handle)); + + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(record->kernel, + nodeObject)); + } + else +#endif + { + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(record->kernel, + ProcessID, + handle)); + + if (gcmIS_SUCCESS(status) && (gcvTRUE == asynchronous)) + { + status = gckEVENT_Unlock(record->kernel->eventObj, + gcvKERNEL_PIXEL, + nodeObject, + nodeObject->type); + } + else + { + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(record->kernel, + nodeObject)); + } + } + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: VIDEO_MEMORY_LOCKED 0x%x (status=%d)", + record->data, status); + break; + + case gcvDB_CONTEXT: + status = gckCOMMAND_Detach(record->kernel->command, gcmNAME_TO_PTR(record->data)); + gcmRELEASE_NAME(record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: CONTEXT 0x%x (status=%d)", + record->data, status); + break; + + case gcvDB_MAP_MEMORY: + /* Unmap memory. */ + status = gckKERNEL_UnmapMemory(record->kernel, + record->physical, + record->bytes, + record->data, + ProcessID); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: MAP MEMORY %d (status=%d)", + gcmPTR2INT32(record->data), status); + break; + + case gcvDB_MAP_USER_MEMORY: + status = gckOS_UnmapUserMemory(Kernel->os, + Kernel->core, + record->physical, + record->bytes, + gcmNAME_TO_PTR(record->data), + 0); + gcmRELEASE_NAME(record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: MAP USER MEMORY %d (status=%d)", + gcmPTR2INT32(record->data), status); + break; + + case gcvDB_SHBUF: + /* Free shared buffer. */ + status = gckKERNEL_DestroyShBuffer(record->kernel, + (gctSHBUF) record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: SHBUF %u (status=%d)", + (gctUINT32)(gctUINTPTR_T) record->data, status); + break; + + default: + gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DATABASE, + "DB: Correcupted record=0x%08x type=%d", + record, record->type); + break; + } + + /* Delete the record. */ + gcmkONERROR(gckKERNEL_DeleteRecord(Kernel, + database, + record->type, + record->data, + gcvNULL)); + } + } + + /* Acquire the database mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Walk the hash list. */ + for (db = Kernel->db->db[slot]; + db != gcvNULL; + db = db->next) + { + if (db->processID == ProcessID) + { + break; + } + previous = db; + } + + if (db != database || !db->deleted) + { + gcmkFATAL("%s(%d): DB of Process=0x%x corrupted after found in deletion\n", + __FUNCTION__, __LINE__, ProcessID); + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + /* Remove the database from the hash list. */ + if (previous) + { + previous->next = database->next; + } + else + { + Kernel->db->db[slot] = database->next; + } + + /* Deinit current database. */ + gcmkVERIFY_OK(gckKERNEL_DeinitDatabase(Kernel, database)); + + if (Kernel->db->lastDatabase) + { + /* Insert last database to the free list. */ + Kernel->db->lastDatabase->next = Kernel->db->freeDatabase; + Kernel->db->freeDatabase = Kernel->db->lastDatabase; + } + + /* Update last database to current one. */ + Kernel->db->lastDatabase = database; + +OnError: +OnExit: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_QueryProcessDB +** +** Query a process database for the current usage of a particular record type. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gctBOOL LastProcessID +** gcvTRUE if searching for the last known process ID. gcvFALSE if +** we need to search for the process ID specified by the ProcessID +** argument. +** +** gceDATABASE_TYPE Type +** Type of the record to query. +** +** OUTPUT: +** +** gcuDATABASE_INFO * Info +** Pointer to a variable that receives the requested information. +*/ +gceSTATUS +gckKERNEL_QueryProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctBOOL LastProcessID, + IN gceDATABASE_TYPE Type, + OUT gcuDATABASE_INFO * Info + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcePOOL vidMemPool; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Info=0x%x", + Kernel, ProcessID, Type, Info); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Info != gcvNULL); + + /* Deocde pool. */ + vidMemPool = (Type & gcdDB_VIDEO_MEMORY_POOL_MASK) >> gcdDB_VIDEO_MEMORY_POOL_SHIFT; + + Type &= gcdDATABASE_TYPE_MASK; + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, LastProcessID, &database)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, database->counterMutex, gcvINFINITE)); + + /* Get pointer to counters. */ + switch (Type) + { + case gcvDB_VIDEO_MEMORY: + if (vidMemPool != gcvPOOL_UNKNOWN) + { + gckOS_MemCopy(&Info->counters, + &database->vidMemPool[vidMemPool], + gcmSIZEOF(database->vidMemPool[vidMemPool])); + } + else + { + gckOS_MemCopy(&Info->counters, + &database->vidMem, + gcmSIZEOF(database->vidMem)); + } + break; + + case gcvDB_NON_PAGED: + gckOS_MemCopy(&Info->counters, + &database->nonPaged, + gcmSIZEOF(database->vidMem)); + break; + + case gcvDB_CONTIGUOUS: + gckOS_MemCopy(&Info->counters, + &database->contiguous, + gcmSIZEOF(database->vidMem)); + break; + + case gcvDB_IDLE: + Info->time = Kernel->db->idleTime; + Kernel->db->idleTime = 0; + break; + + case gcvDB_MAP_MEMORY: + gckOS_MemCopy(&Info->counters, + &database->mapMemory, + gcmSIZEOF(database->mapMemory)); + break; + + case gcvDB_MAP_USER_MEMORY: + gckOS_MemCopy(&Info->counters, + &database->mapUserMemory, + gcmSIZEOF(database->mapUserMemory)); + break; + + default: + break; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, database->counterMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_FindHandleDatbase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gctPOINTER * HandleDatabase, + OUT gctPOINTER * HandleDatabaseMutex + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", + Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + *HandleDatabase = database->handleDatabase; + *HandleDatabaseMutex = database->handleDatabaseMutex; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +gckKERNEL_GetProcessMMU( + IN gckKERNEL Kernel, + OUT gckMMU * Mmu + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gctUINT32 processID; + + gcmkONERROR(gckOS_GetProcessID(&processID)); + + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, processID, gcvFALSE, &database)); + + *Mmu = database->mmu; + + return gcvSTATUS_OK; + +OnError: + return status; +} +#endif + +#if gcdSECURE_USER +/******************************************************************************* +** gckKERNEL_GetProcessDBCache +** +** Get teh secure cache from a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** OUTPUT: +** +** gcskSECURE_CACHE_PTR * Cache +** Pointer to a variable that receives the secure cache pointer. +*/ +gceSTATUS +gckKERNEL_GetProcessDBCache( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gcskSECURE_CACHE_PTR * Cache + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Cache != gcvNULL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Return the pointer to the cache. */ + *Cache = &database->cache; + + /* Success. */ + gcmkFOOTER_ARG("*Cache=0x%x", *Cache); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +gceSTATUS +gckKERNEL_DumpProcessDB( + IN gckKERNEL Kernel + ) +{ + gcsDATABASE_PTR database; + gctINT i, pid; + gctUINT8 name[24]; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Acquire the database mutex. */ + gcmkVERIFY_OK( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + + gcmkPRINT("**************************\n"); + gcmkPRINT("*** PROCESS DB DUMP ***\n"); + gcmkPRINT("**************************\n"); + + gcmkPRINT_N(8, "%-8s%s\n", "PID", "NAME"); + /* Walk the databases. */ + for (i = 0; i < gcmCOUNTOF(Kernel->db->db); ++i) + { + for (database = Kernel->db->db[i]; + database != gcvNULL; + database = database->next) + { + pid = database->processID; + + gcmkVERIFY_OK(gckOS_ZeroMemory(name, gcmSIZEOF(name))); + + gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name)); + + gcmkPRINT_N(8, "%-8d%s\n", pid, name); + } + } + + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +void +_DumpCounter( + IN gcsDATABASE_COUNTERS * Counter, + IN gctCONST_STRING Name + ) +{ + gcmkPRINT("%s:", Name); + gcmkPRINT(" Currently allocated : %10lld", Counter->bytes); + gcmkPRINT(" Maximum allocated : %10lld", Counter->maxBytes); + gcmkPRINT(" Total allocated : %10lld", Counter->totalBytes); +} + +gceSTATUS +gckKERNEL_DumpVidMemUsage( + IN gckKERNEL Kernel, + IN gctINT32 ProcessID + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcsDATABASE_COUNTERS * counter; + gctUINT32 i = 0; + + static gctCONST_STRING surfaceTypes[] = { + "UNKNOWN", + "INDEX", + "VERTEX", + "TEXTURE", + "RENDER_TARGET", + "DEPTH", + "BITMAP", + "TILE_STATUS", + "IMAGE", + "MASK", + "SCISSOR", + "HIERARCHICAL_DEPTH", + "ICACHE", + "TXDESC", + "FENCE", + "TFBHEADER", + }; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", + Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Find the database. */ + gcmkONERROR( + gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + gcmkPRINT("VidMem Usage (Process %d):", ProcessID); + + /* Get pointer to counters. */ + counter = &database->vidMem; + + _DumpCounter(counter, "Total Video Memory"); + + for (i = 0; i < gcvSURF_NUM_TYPES; i++) + { + counter = &database->vidMemType[i]; + + _DumpCounter(counter, surfaceTypes[i]); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} |