1878 lines
54 KiB
C
1878 lines
54 KiB
C
/****************************************************************************
|
|
*
|
|
* 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;
|
|
}
|