| /**************************************************************************** |
| * |
| * 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; |
| } |