/*************************************************************************/ /*!
|
@File
|
@Title Server side connection management
|
@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
|
@Description Handles connections coming from the client and the management
|
connection based information
|
@License Dual MIT/GPLv2
|
|
The contents of this file are subject to the MIT license as set out below.
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
of this software and associated documentation files (the "Software"), to deal
|
in the Software without restriction, including without limitation the rights
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
copies of the Software, and to permit persons to whom the Software is
|
furnished to do so, subject to the following conditions:
|
|
The above copyright notice and this permission notice shall be included in
|
all copies or substantial portions of the Software.
|
|
Alternatively, the contents of this file may be used under the terms of
|
the GNU General Public License Version 2 ("GPL") in which case the provisions
|
of GPL are applicable instead of those above.
|
|
If you wish to allow use of your version of this file only under the terms of
|
GPL, and not to allow others to use your version of this file under the terms
|
of the MIT license, indicate your decision by deleting the provisions above
|
and replace them with the notice and other provisions required by GPL as set
|
out in the file called "GPL-COPYING" included in this distribution. If you do
|
not delete the provisions above, a recipient may use your version of this file
|
under the terms of either the MIT license or GPL.
|
|
This License is also included in this distribution in the file called
|
"MIT-COPYING".
|
|
EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
|
PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
*/ /**************************************************************************/
|
|
#include "handle.h"
|
#include "pvrsrv.h"
|
#include "connection_server.h"
|
#include "osconnection_server.h"
|
#include "allocmem.h"
|
#include "pvr_debug.h"
|
#include "sync_server.h"
|
#include "process_stats.h"
|
#include "pdump_km.h"
|
#include "lists.h"
|
#include "osfunc.h"
|
#include "tlstream.h"
|
|
/* PID associated with Connection currently being purged by Cleanup thread */
|
static IMG_PID gCurrentPurgeConnectionPid = 0;
|
|
static PVRSRV_ERROR ConnectionDataDestroy(CONNECTION_DATA *psConnection)
|
{
|
PVRSRV_ERROR eError;
|
PROCESS_HANDLE_BASE *psProcessHandleBase;
|
IMG_UINT64 ui64MaxBridgeTime;
|
PVRSRV_DATA *psPVRSRVData = PVRSRVGetPVRSRVData();
|
|
if(psPVRSRVData->bUnload)
|
{
|
/* driver is unloading so do not allow the bridge lock to be released */
|
ui64MaxBridgeTime = 0;
|
}
|
else
|
{
|
ui64MaxBridgeTime = CONNECTION_DEFERRED_CLEANUP_TIMESLICE_NS;
|
}
|
|
if (psConnection == NULL)
|
{
|
PVR_DPF((PVR_DBG_ERROR, "ConnectionDestroy: Missing connection!"));
|
PVR_ASSERT(0);
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
}
|
|
/* Close the process statistics */
|
#if defined(PVRSRV_ENABLE_PROCESS_STATS) && !defined(PVRSRV_DEBUG_LINUX_MEMORY_STATS)
|
if (psConnection->hProcessStats != NULL)
|
{
|
PVRSRVStatsDeregisterProcess(psConnection->hProcessStats);
|
psConnection->hProcessStats = NULL;
|
}
|
#endif
|
|
/* Close HWPerfClient stream here even though we created it in
|
* PVRSRVConnectKM(). */
|
if (psConnection->hClientTLStream)
|
{
|
TLStreamClose(psConnection->hClientTLStream);
|
psConnection->hClientTLStream = NULL;
|
PVR_DPF((PVR_DBG_MESSAGE, "Destroyed private stream."));
|
}
|
|
/* Get process handle base to decrement the refcount */
|
psProcessHandleBase = psConnection->psProcessHandleBase;
|
|
if (psProcessHandleBase != NULL)
|
{
|
/* In case the refcount becomes 0 we can remove the process handle base */
|
if (OSAtomicDecrement(&psProcessHandleBase->iRefCount) == 0)
|
{
|
uintptr_t uiHashValue;
|
|
OSLockAcquire(psPVRSRVData->hProcessHandleBase_Lock);
|
uiHashValue = HASH_Remove(psPVRSRVData->psProcessHandleBase_Table, psConnection->pid);
|
OSLockRelease(psPVRSRVData->hProcessHandleBase_Lock);
|
|
if (!uiHashValue)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"%s: Failed to remove handle base from hash table.",
|
__func__));
|
return PVRSRV_ERROR_UNABLE_TO_REMOVE_HASH_VALUE;
|
}
|
|
eError = PVRSRVFreeHandleBase(psProcessHandleBase->psHandleBase, ui64MaxBridgeTime);
|
if (eError != PVRSRV_OK)
|
{
|
if (eError != PVRSRV_ERROR_RETRY)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"ConnectionDataDestroy: Couldn't free handle base for process (%d)",
|
eError));
|
}
|
|
return eError;
|
}
|
|
OSFreeMem(psProcessHandleBase);
|
}
|
|
psConnection->psProcessHandleBase = NULL;
|
}
|
|
/* Free handle base for this connection */
|
if (psConnection->psHandleBase != NULL)
|
{
|
eError = PVRSRVFreeHandleBase(psConnection->psHandleBase, ui64MaxBridgeTime);
|
if (eError != PVRSRV_OK)
|
{
|
if (eError != PVRSRV_ERROR_RETRY)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"ConnectionDataDestroy: Couldn't free handle base for connection (%d)",
|
eError));
|
}
|
|
return eError;
|
}
|
|
psConnection->psHandleBase = NULL;
|
}
|
|
if (psConnection->psSyncConnectionData != NULL)
|
{
|
SyncUnregisterConnection(psConnection->psSyncConnectionData);
|
psConnection->psSyncConnectionData = NULL;
|
}
|
|
if (psConnection->psPDumpConnectionData != NULL)
|
{
|
PDumpUnregisterConnection(psConnection->psPDumpConnectionData);
|
psConnection->psPDumpConnectionData = NULL;
|
}
|
|
/* Call environment specific connection data deinit function */
|
if (psConnection->hOsPrivateData != NULL)
|
{
|
eError = OSConnectionPrivateDataDeInit(psConnection->hOsPrivateData);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionDataDestroy: OSConnectionPrivateDataDeInit failed (%d)",
|
eError));
|
|
return eError;
|
}
|
|
psConnection->hOsPrivateData = NULL;
|
}
|
|
OSFreeMem(psConnection);
|
|
return PVRSRV_OK;
|
}
|
|
PVRSRV_ERROR PVRSRVConnectionConnect(void **ppvPrivData, void *pvOSData)
|
{
|
CONNECTION_DATA *psConnection;
|
PVRSRV_ERROR eError;
|
PROCESS_HANDLE_BASE *psProcessHandleBase;
|
PVRSRV_DATA *psPVRSRVData = PVRSRVGetPVRSRVData();
|
|
/* Allocate connection data area */
|
psConnection = OSAllocZMem(sizeof(*psConnection));
|
if (psConnection == NULL)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionConnect: Couldn't allocate connection data"));
|
return PVRSRV_ERROR_OUT_OF_MEMORY;
|
}
|
|
/* Call environment specific connection data init function */
|
eError = OSConnectionPrivateDataInit(&psConnection->hOsPrivateData, pvOSData);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionConnect: OSConnectionPrivateDataInit failed (%d)",
|
eError));
|
goto failure;
|
}
|
|
psConnection->pid = OSGetCurrentClientProcessIDKM();
|
|
/* Register this connection with the sync core */
|
eError = SyncRegisterConnection(&psConnection->psSyncConnectionData);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionConnect: Couldn't register the sync data"));
|
goto failure;
|
}
|
|
/*
|
* Register this connection with the pdump core. Pass in the sync connection data
|
* as it will be needed later when we only get passed in the PDump connection data.
|
*/
|
eError = PDumpRegisterConnection(psConnection->psSyncConnectionData,
|
&psConnection->psPDumpConnectionData);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionConnect: Couldn't register the PDump data"));
|
goto failure;
|
}
|
|
/* Allocate handle base for this connection */
|
eError = PVRSRVAllocHandleBase(&psConnection->psHandleBase,
|
PVRSRV_HANDLE_BASE_TYPE_CONNECTION);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionConnect: Couldn't allocate handle base for connection (%d)",
|
eError));
|
goto failure;
|
}
|
|
/* Try to get process handle base if it already exists */
|
OSLockAcquire(psPVRSRVData->hProcessHandleBase_Lock);
|
psProcessHandleBase = (PROCESS_HANDLE_BASE*) HASH_Retrieve(PVRSRVGetPVRSRVData()->psProcessHandleBase_Table,
|
psConnection->pid);
|
|
/* In case there is none we are going to allocate one */
|
if (psProcessHandleBase == NULL)
|
{
|
psProcessHandleBase = OSAllocZMem(sizeof(PROCESS_HANDLE_BASE));
|
if (psProcessHandleBase == NULL)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"%s: Failed to allocate handle base, oom.",
|
__func__));
|
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
|
goto failureLock;
|
}
|
|
/* Allocate handle base for this process */
|
eError = PVRSRVAllocHandleBase(&psProcessHandleBase->psHandleBase,
|
PVRSRV_HANDLE_BASE_TYPE_PROCESS);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"%s: Couldn't allocate handle base for process (%d)",
|
__func__,
|
eError));
|
OSFreeMem(psProcessHandleBase);
|
goto failureLock;
|
}
|
|
/* Insert the handle base into the global hash table */
|
if (!HASH_Insert(PVRSRVGetPVRSRVData()->psProcessHandleBase_Table,
|
psConnection->pid,
|
(uintptr_t) psProcessHandleBase))
|
{
|
|
eError = PVRSRV_ERROR_UNABLE_TO_INSERT_HASH_VALUE;
|
|
PVRSRVFreeHandleBase(psProcessHandleBase->psHandleBase, 0);
|
|
OSFreeMem(psProcessHandleBase);
|
goto failureLock;
|
}
|
}
|
OSLockRelease(psPVRSRVData->hProcessHandleBase_Lock);
|
|
psConnection->psProcessHandleBase = psProcessHandleBase;
|
|
OSAtomicIncrement(&psProcessHandleBase->iRefCount);
|
|
/* Allocate process statistics */
|
#if defined(PVRSRV_ENABLE_PROCESS_STATS) && !defined(PVRSRV_DEBUG_LINUX_MEMORY_STATS)
|
eError = PVRSRVStatsRegisterProcess(&psConnection->hProcessStats);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"PVRSRVConnectionConnect: Couldn't register process statistics (%d)",
|
eError));
|
goto failure;
|
}
|
#endif
|
|
*ppvPrivData = psConnection;
|
|
return eError;
|
|
failureLock:
|
OSLockRelease(psPVRSRVData->hProcessHandleBase_Lock);
|
failure:
|
ConnectionDataDestroy(psConnection);
|
|
return eError;
|
}
|
|
static PVRSRV_ERROR _CleanupThreadPurgeConnectionData(void *pvConnectionData)
|
{
|
PVRSRV_ERROR eErrorConnection, eErrorKernel;
|
CONNECTION_DATA *psConnectionData = pvConnectionData;
|
|
OSAcquireBridgeLock();
|
|
gCurrentPurgeConnectionPid = psConnectionData->pid;
|
|
eErrorConnection = ConnectionDataDestroy(psConnectionData);
|
if (eErrorConnection != PVRSRV_OK)
|
{
|
if (eErrorConnection == PVRSRV_ERROR_RETRY)
|
{
|
PVR_DPF((PVR_DBG_MESSAGE,
|
"_CleanupThreadPurgeConnectionData: Failed to purge connection data %p "
|
"(deferring destruction)",
|
psConnectionData));
|
}
|
}
|
else
|
{
|
PVR_DPF((PVR_DBG_MESSAGE,
|
"_CleanupThreadPurgeConnectionData: Connection data %p deferred destruction finished",
|
psConnectionData));
|
}
|
|
/* Check if possible resize the global handle base */
|
eErrorKernel = PVRSRVPurgeHandles(KERNEL_HANDLE_BASE);
|
if (eErrorKernel != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"_CleanupThreadPurgeConnectionData: Purge of global handle pool failed (%d)",
|
eErrorKernel));
|
}
|
|
gCurrentPurgeConnectionPid = 0;
|
|
OSReleaseBridgeLock();
|
|
return eErrorConnection;
|
}
|
|
void PVRSRVConnectionDisconnect(void *pvDataPtr)
|
{
|
CONNECTION_DATA *psConnectionData = pvDataPtr;
|
|
/* Notify the PDump core if the pdump control client is disconnecting */
|
if (psConnectionData->ui32ClientFlags & SRV_FLAGS_PDUMPCTRL)
|
{
|
PDumpDisconnectionNotify();
|
}
|
#if defined(PVRSRV_FORCE_UNLOAD_IF_BAD_STATE)
|
if (PVRSRVGetPVRSRVData()->eServicesState == PVRSRV_SERVICES_STATE_OK)
|
#endif
|
{
|
/* Defer the release of the connection data */
|
psConnectionData->sCleanupThreadFn.pfnFree = _CleanupThreadPurgeConnectionData;
|
psConnectionData->sCleanupThreadFn.pvData = psConnectionData;
|
psConnectionData->sCleanupThreadFn.ui32RetryCount = CLEANUP_THREAD_RETRY_COUNT_DEFAULT;
|
psConnectionData->sCleanupThreadFn.bDependsOnHW = IMG_FALSE;
|
PVRSRVCleanupThreadAddWork(&psConnectionData->sCleanupThreadFn);
|
}
|
}
|
|
IMG_PID PVRSRVGetPurgeConnectionPid(void)
|
{
|
return gCurrentPurgeConnectionPid;
|
}
|