blob: 5053a455436df8bcd23fdac164c7968497cd1b0c [file] [log] [blame] [edit]
/**********************************************************************
*
* Copyright (C) Imagination Technologies Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful but, except
* as otherwise stated in writing, without any warranty; without even the
* implied warranty of merchantability or fitness for a particular purpose.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Contact Information:
* Imagination Technologies Ltd. <[email protected]>
* Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK
*
******************************************************************************/
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38))
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#endif
#if !defined(PVR_LINUX_MEM_AREA_POOL_MAX_PAGES)
#define PVR_LINUX_MEM_AREA_POOL_MAX_PAGES 0
#endif
#include <linux/kernel.h>
#include <asm/atomic.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
#include <linux/wrapper.h>
#endif
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/sched.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0))
#include <linux/shrinker.h>
#endif
#include "img_defs.h"
#include "services.h"
#include "servicesint.h"
#include "syscommon.h"
#include "mutils.h"
#include "mm.h"
#include "pvrmmap.h"
#include "mmap.h"
#include "osfunc.h"
#include "pvr_debug.h"
#include "proc.h"
#include "mutex.h"
#include "lock.h"
#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
#include "lists.h"
#endif
static atomic_t g_sPagePoolEntryCount = ATOMIC_INIT(0);
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
typedef enum {
DEBUG_MEM_ALLOC_TYPE_KMALLOC,
DEBUG_MEM_ALLOC_TYPE_VMALLOC,
DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES,
DEBUG_MEM_ALLOC_TYPE_IOREMAP,
DEBUG_MEM_ALLOC_TYPE_IO,
DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE,
DEBUG_MEM_ALLOC_TYPE_ION,
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
DEBUG_MEM_ALLOC_TYPE_VMAP,
#endif
DEBUG_MEM_ALLOC_TYPE_COUNT
} DEBUG_MEM_ALLOC_TYPE;
typedef struct _DEBUG_MEM_ALLOC_REC
{
DEBUG_MEM_ALLOC_TYPE eAllocType;
IMG_VOID *pvKey;
IMG_VOID *pvCpuVAddr;
IMG_UINT32 ulCpuPAddr;
IMG_VOID *pvPrivateData;
IMG_UINT32 ui32Bytes;
pid_t pid;
IMG_CHAR *pszFileName;
IMG_UINT32 ui32Line;
struct _DEBUG_MEM_ALLOC_REC *psNext;
struct _DEBUG_MEM_ALLOC_REC **ppsThis;
} DEBUG_MEM_ALLOC_REC;
static IMPLEMENT_LIST_ANY_VA_2(DEBUG_MEM_ALLOC_REC, IMG_BOOL, IMG_FALSE)
static IMPLEMENT_LIST_ANY_VA(DEBUG_MEM_ALLOC_REC)
static IMPLEMENT_LIST_FOR_EACH(DEBUG_MEM_ALLOC_REC)
static IMPLEMENT_LIST_INSERT(DEBUG_MEM_ALLOC_REC)
static IMPLEMENT_LIST_REMOVE(DEBUG_MEM_ALLOC_REC)
static DEBUG_MEM_ALLOC_REC *g_MemoryRecords;
static IMG_UINT32 g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT];
static IMG_UINT32 g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT];
static IMG_UINT32 g_SysRAMWaterMark;
static IMG_UINT32 g_SysRAMHighWaterMark;
static inline IMG_UINT32
SysRAMTrueWaterMark(void)
{
return g_SysRAMWaterMark + PAGES_TO_BYTES(atomic_read(&g_sPagePoolEntryCount));
}
static IMG_UINT32 g_IOMemWaterMark;
static IMG_UINT32 g_IOMemHighWaterMark;
static IMG_VOID DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE eAllocType,
IMG_VOID *pvKey,
IMG_VOID *pvCpuVAddr,
IMG_UINT32 ulCpuPAddr,
IMG_VOID *pvPrivateData,
IMG_UINT32 ui32Bytes,
IMG_CHAR *pszFileName,
IMG_UINT32 ui32Line);
static IMG_VOID DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_VOID *pvKey, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line);
static IMG_CHAR *DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType);
static struct proc_dir_entry *g_SeqFileMemoryRecords;
static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off);
static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el);
static void* ProcSeqOff2ElementMemoryRecords(struct seq_file * sfile, loff_t off);
#endif
#if defined(DEBUG_LINUX_MEM_AREAS)
typedef struct _DEBUG_LINUX_MEM_AREA_REC
{
LinuxMemArea *psLinuxMemArea;
IMG_UINT32 ui32Flags;
pid_t pid;
struct _DEBUG_LINUX_MEM_AREA_REC *psNext;
struct _DEBUG_LINUX_MEM_AREA_REC **ppsThis;
}DEBUG_LINUX_MEM_AREA_REC;
static IMPLEMENT_LIST_ANY_VA(DEBUG_LINUX_MEM_AREA_REC)
static IMPLEMENT_LIST_FOR_EACH(DEBUG_LINUX_MEM_AREA_REC)
static IMPLEMENT_LIST_INSERT(DEBUG_LINUX_MEM_AREA_REC)
static IMPLEMENT_LIST_REMOVE(DEBUG_LINUX_MEM_AREA_REC)
static DEBUG_LINUX_MEM_AREA_REC *g_LinuxMemAreaRecords;
static IMG_UINT32 g_LinuxMemAreaCount;
static IMG_UINT32 g_LinuxMemAreaWaterMark;
static IMG_UINT32 g_LinuxMemAreaHighWaterMark;
static struct proc_dir_entry *g_SeqFileMemArea;
static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off);
static void ProcSeqShowMemArea(struct seq_file *sfile,void* el);
static void* ProcSeqOff2ElementMemArea(struct seq_file *sfile, loff_t off);
#endif
#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
static PVRSRV_LINUX_MUTEX g_sDebugMutex;
#endif
#if (defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS))
static void ProcSeqStartstopDebugMutex(struct seq_file *sfile,IMG_BOOL start);
#endif
typedef struct
{
struct list_head sPagePoolItem;
struct page *psPage;
} LinuxPagePoolEntry;
static LinuxKMemCache *g_PsLinuxMemAreaCache;
static LinuxKMemCache *g_PsLinuxPagePoolCache;
static LIST_HEAD(g_sPagePoolList);
static int g_iPagePoolMaxEntries;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
static IMG_VOID ReservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length);
static IMG_VOID UnreservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length);
#endif
static LinuxMemArea *LinuxMemAreaStructAlloc(IMG_VOID);
static IMG_VOID LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea);
#if defined(DEBUG_LINUX_MEM_AREAS)
static IMG_VOID DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags);
static DEBUG_LINUX_MEM_AREA_REC *DebugLinuxMemAreaRecordFind(LinuxMemArea *psLinuxMemArea);
static IMG_VOID DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea);
#endif
static inline IMG_BOOL
AreaIsUncached(IMG_UINT32 ui32AreaFlags)
{
return (ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) != 0;
}
static inline IMG_BOOL
CanFreeToPool(LinuxMemArea *psLinuxMemArea)
{
return AreaIsUncached(psLinuxMemArea->ui32AreaFlags) && !psLinuxMemArea->bNeedsCacheInvalidate;
}
IMG_VOID *
_KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
IMG_VOID *pvRet;
pvRet = kmalloc(ui32ByteSize, uFlags);
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
if (pvRet)
{
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_KMALLOC,
pvRet,
pvRet,
0,
NULL,
ui32ByteSize,
pszFileName,
ui32Line
);
}
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
return pvRet;
}
IMG_VOID
_KFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMALLOC, pvCpuVAddr, pszFileName, ui32Line);
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
kfree(pvCpuVAddr);
}
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
static IMG_VOID
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE eAllocType,
IMG_VOID *pvKey,
IMG_VOID *pvCpuVAddr,
IMG_UINT32 ulCpuPAddr,
IMG_VOID *pvPrivateData,
IMG_UINT32 ui32Bytes,
IMG_CHAR *pszFileName,
IMG_UINT32 ui32Line)
{
DEBUG_MEM_ALLOC_REC *psRecord;
LinuxLockMutex(&g_sDebugMutex);
psRecord = kmalloc(sizeof(DEBUG_MEM_ALLOC_REC), GFP_KERNEL);
psRecord->eAllocType = eAllocType;
psRecord->pvKey = pvKey;
psRecord->pvCpuVAddr = pvCpuVAddr;
psRecord->ulCpuPAddr = ulCpuPAddr;
psRecord->pvPrivateData = pvPrivateData;
psRecord->pid = OSGetCurrentProcessIDKM();
psRecord->ui32Bytes = ui32Bytes;
psRecord->pszFileName = pszFileName;
psRecord->ui32Line = ui32Line;
List_DEBUG_MEM_ALLOC_REC_Insert(&g_MemoryRecords, psRecord);
g_WaterMarkData[eAllocType] += ui32Bytes;
if (g_WaterMarkData[eAllocType] > g_HighWaterMarkData[eAllocType])
{
g_HighWaterMarkData[eAllocType] = g_WaterMarkData[eAllocType];
}
if (eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_VMALLOC
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE)
{
IMG_UINT32 ui32SysRAMTrueWaterMark;
g_SysRAMWaterMark += ui32Bytes;
ui32SysRAMTrueWaterMark = SysRAMTrueWaterMark();
if (ui32SysRAMTrueWaterMark > g_SysRAMHighWaterMark)
{
g_SysRAMHighWaterMark = ui32SysRAMTrueWaterMark;
}
}
else if (eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_IO)
{
g_IOMemWaterMark += ui32Bytes;
if (g_IOMemWaterMark > g_IOMemHighWaterMark)
{
g_IOMemHighWaterMark = g_IOMemWaterMark;
}
}
LinuxUnLockMutex(&g_sDebugMutex);
}
static IMG_BOOL DebugMemAllocRecordRemove_AnyVaCb(DEBUG_MEM_ALLOC_REC *psCurrentRecord, va_list va)
{
DEBUG_MEM_ALLOC_TYPE eAllocType;
IMG_VOID *pvKey;
eAllocType = va_arg(va, DEBUG_MEM_ALLOC_TYPE);
pvKey = va_arg(va, IMG_VOID*);
if (psCurrentRecord->eAllocType == eAllocType
&& psCurrentRecord->pvKey == pvKey)
{
eAllocType = psCurrentRecord->eAllocType;
g_WaterMarkData[eAllocType] -= psCurrentRecord->ui32Bytes;
if (eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_VMALLOC
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE)
{
g_SysRAMWaterMark -= psCurrentRecord->ui32Bytes;
}
else if (eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP
|| eAllocType == DEBUG_MEM_ALLOC_TYPE_IO)
{
g_IOMemWaterMark -= psCurrentRecord->ui32Bytes;
}
List_DEBUG_MEM_ALLOC_REC_Remove(psCurrentRecord);
kfree(psCurrentRecord);
return IMG_TRUE;
}
else
{
return IMG_FALSE;
}
}
static IMG_VOID
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_VOID *pvKey, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
LinuxLockMutex(&g_sDebugMutex);
if (!List_DEBUG_MEM_ALLOC_REC_IMG_BOOL_Any_va(g_MemoryRecords,
DebugMemAllocRecordRemove_AnyVaCb,
eAllocType,
pvKey))
{
PVR_DPF((PVR_DBG_ERROR, "%s: couldn't find an entry for type=%s with pvKey=%p (called from %s, line %d\n",
__FUNCTION__, DebugMemAllocRecordTypeToString(eAllocType), pvKey,
pszFileName, ui32Line));
}
LinuxUnLockMutex(&g_sDebugMutex);
}
static IMG_CHAR *
DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType)
{
IMG_CHAR *apszDebugMemoryRecordTypes[] = {
"KMALLOC",
"VMALLOC",
"ALLOC_PAGES",
"IOREMAP",
"IO",
"KMEM_CACHE_ALLOC",
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
"VMAP"
#endif
};
return apszDebugMemoryRecordTypes[eAllocType];
}
#endif
static IMG_BOOL
AllocFlagsToPGProt(pgprot_t *pPGProtFlags, IMG_UINT32 ui32AllocFlags)
{
pgprot_t PGProtFlags;
switch (ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK)
{
case PVRSRV_HAP_CACHED:
PGProtFlags = PAGE_KERNEL;
break;
case PVRSRV_HAP_WRITECOMBINE:
PGProtFlags = PGPROT_WC(PAGE_KERNEL);
break;
case PVRSRV_HAP_UNCACHED:
PGProtFlags = PGPROT_UC(PAGE_KERNEL);
break;
default:
PVR_DPF((PVR_DBG_ERROR,
"%s: Unknown mapping flags=0x%08x",
__FUNCTION__, ui32AllocFlags));
dump_stack();
return IMG_FALSE;
}
*pPGProtFlags = PGProtFlags;
return IMG_TRUE;
}
IMG_VOID *
_VMallocWrapper(IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32AllocFlags,
IMG_CHAR *pszFileName,
IMG_UINT32 ui32Line)
{
pgprot_t PGProtFlags;
IMG_VOID *pvRet;
if (!AllocFlagsToPGProt(&PGProtFlags, ui32AllocFlags))
{
return NULL;
}
pvRet = __vmalloc(ui32Bytes, GFP_KERNEL | __GFP_HIGHMEM, PGProtFlags);
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
if (pvRet)
{
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMALLOC,
pvRet,
pvRet,
0,
NULL,
PAGE_ALIGN(ui32Bytes),
pszFileName,
ui32Line
);
}
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
return pvRet;
}
IMG_VOID
_VFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_VMALLOC, pvCpuVAddr, pszFileName, ui32Line);
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
vfree(pvCpuVAddr);
}
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
static IMG_VOID *
_VMapWrapper(struct page **ppsPageList, IMG_UINT32 ui32NumPages, IMG_UINT32 ui32AllocFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
pgprot_t PGProtFlags;
IMG_VOID *pvRet;
if (!AllocFlagsToPGProt(&PGProtFlags, ui32AllocFlags))
{
return NULL;
}
pvRet = vmap(ppsPageList, ui32NumPages, GFP_KERNEL | __GFP_HIGHMEM, PGProtFlags);
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
if (pvRet)
{
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMAP,
pvRet,
pvRet,
0,
NULL,
PAGES_TO_BYTES(ui32NumPages),
pszFileName,
ui32Line
);
}
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
return pvRet;
}
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
#define VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags) _VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags, __FILE__, __LINE__)
#else
#define VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags) _VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags, NULL, 0)
#endif
static IMG_VOID
_VUnmapWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_VMAP, pvCpuVAddr, pszFileName, ui32Line);
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
vunmap(pvCpuVAddr);
}
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
#define VUnmapWrapper(pvCpuVAddr) _VUnmapWrapper(pvCpuVAddr, __FILE__, __LINE__)
#else
#define VUnmapWrapper(pvCpuVAddr) _VUnmapWrapper(pvCpuVAddr, NULL, 0)
#endif
#endif
IMG_VOID
_KMemCacheFreeWrapper(LinuxKMemCache *psCache, IMG_VOID *pvObject, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, pvObject, pszFileName, ui32Line);
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
kmem_cache_free(psCache, pvObject);
}
const IMG_CHAR *
KMemCacheNameWrapper(LinuxKMemCache *psCache)
{
PVR_UNREFERENCED_PARAMETER(psCache);
return "";
}
static LinuxPagePoolEntry *
LinuxPagePoolEntryAlloc(IMG_VOID)
{
return KMemCacheAllocWrapper(g_PsLinuxPagePoolCache, GFP_KERNEL);
}
static IMG_VOID
LinuxPagePoolEntryFree(LinuxPagePoolEntry *psPagePoolEntry)
{
KMemCacheFreeWrapper(g_PsLinuxPagePoolCache, psPagePoolEntry);
}
static struct page *
AllocPageFromLinux(void)
{
struct page *psPage;
psPage = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0);
if (!psPage)
{
return NULL;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
SetPageReserved(psPage);
#else
mem_map_reserve(psPage);
#endif
#endif
return psPage;
}
static IMG_VOID
FreePageToLinux(struct page *psPage)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
ClearPageReserved(psPage);
#else
mem_map_reserve(psPage);
#endif
#endif
__free_pages(psPage, 0);
}
#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0)
static DEFINE_MUTEX(g_sPagePoolMutex);
static inline void
PagePoolLock(void)
{
mutex_lock(&g_sPagePoolMutex);
}
static inline void
PagePoolUnlock(void)
{
mutex_unlock(&g_sPagePoolMutex);
}
static inline int
PagePoolTrylock(void)
{
return mutex_trylock(&g_sPagePoolMutex);
}
#else
static inline void
PagePoolLock(void)
{
}
static inline void
PagePoolUnlock(void)
{
}
static inline int
PagePoolTrylock(void)
{
return 1;
}
#endif
static inline void
AddEntryToPool(LinuxPagePoolEntry *psPagePoolEntry)
{
list_add_tail(&psPagePoolEntry->sPagePoolItem, &g_sPagePoolList);
atomic_inc(&g_sPagePoolEntryCount);
}
static inline void
RemoveEntryFromPool(LinuxPagePoolEntry *psPagePoolEntry)
{
list_del(&psPagePoolEntry->sPagePoolItem);
atomic_dec(&g_sPagePoolEntryCount);
}
static inline LinuxPagePoolEntry *
RemoveFirstEntryFromPool(void)
{
LinuxPagePoolEntry *psPagePoolEntry;
if (list_empty(&g_sPagePoolList))
{
PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0);
return NULL;
}
PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) > 0);
psPagePoolEntry = list_first_entry(&g_sPagePoolList, LinuxPagePoolEntry, sPagePoolItem);
RemoveEntryFromPool(psPagePoolEntry);
return psPagePoolEntry;
}
static struct page *
AllocPage(IMG_UINT32 ui32AreaFlags, IMG_BOOL *pbFromPagePool)
{
struct page *psPage = NULL;
if (AreaIsUncached(ui32AreaFlags) && atomic_read(&g_sPagePoolEntryCount) != 0)
{
LinuxPagePoolEntry *psPagePoolEntry;
PagePoolLock();
psPagePoolEntry = RemoveFirstEntryFromPool();
PagePoolUnlock();
if (psPagePoolEntry)
{
psPage = psPagePoolEntry->psPage;
LinuxPagePoolEntryFree(psPagePoolEntry);
*pbFromPagePool = IMG_TRUE;
}
}
if (!psPage)
{
psPage = AllocPageFromLinux();
if (psPage)
{
*pbFromPagePool = IMG_FALSE;
}
}
return psPage;
}
static IMG_VOID
FreePage(IMG_BOOL bToPagePool, struct page *psPage)
{
if (bToPagePool && atomic_read(&g_sPagePoolEntryCount) < g_iPagePoolMaxEntries)
{
LinuxPagePoolEntry *psPagePoolEntry = LinuxPagePoolEntryAlloc();
if (psPagePoolEntry)
{
psPagePoolEntry->psPage = psPage;
PagePoolLock();
AddEntryToPool(psPagePoolEntry);
PagePoolUnlock();
return;
}
}
FreePageToLinux(psPage);
}
static IMG_VOID
FreePagePool(IMG_VOID)
{
LinuxPagePoolEntry *psPagePoolEntry, *psTempPoolEntry;
PagePoolLock();
#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0)
PVR_TRACE(("%s: Freeing %d pages from pool", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount)));
#else
PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0);
PVR_ASSERT(list_empty(&g_sPagePoolList));
#endif
list_for_each_entry_safe(psPagePoolEntry, psTempPoolEntry, &g_sPagePoolList, sPagePoolItem)
{
RemoveEntryFromPool(psPagePoolEntry);
FreePageToLinux(psPagePoolEntry->psPage);
LinuxPagePoolEntryFree(psPagePoolEntry);
}
PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0);
PagePoolUnlock();
}
#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK)
#if defined(PVRSRV_NEED_PVR_ASSERT)
static struct shrinker g_sShrinker;
#endif
static int
ShrinkPagePool(struct shrinker *psShrinker, struct shrink_control *psShrinkControl)
{
unsigned long uNumToScan = psShrinkControl->nr_to_scan;
PVR_ASSERT(psShrinker == &g_sShrinker);
(void)psShrinker;
if (uNumToScan != 0)
{
LinuxPagePoolEntry *psPagePoolEntry, *psTempPoolEntry;
PVR_TRACE(("%s: Number to scan: %ld", __FUNCTION__, uNumToScan));
PVR_TRACE(("%s: Pages in pool before scan: %d", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount)));
if (!PagePoolTrylock())
{
PVR_TRACE(("%s: Couldn't get page pool lock", __FUNCTION__));
return -1;
}
list_for_each_entry_safe(psPagePoolEntry, psTempPoolEntry, &g_sPagePoolList, sPagePoolItem)
{
RemoveEntryFromPool(psPagePoolEntry);
FreePageToLinux(psPagePoolEntry->psPage);
LinuxPagePoolEntryFree(psPagePoolEntry);
if (--uNumToScan == 0)
{
break;
}
}
if (list_empty(&g_sPagePoolList))
{
PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0);
}
PagePoolUnlock();
PVR_TRACE(("%s: Pages in pool after scan: %d", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount)));
}
return atomic_read(&g_sPagePoolEntryCount);
}
#endif
static IMG_BOOL
AllocPages(IMG_UINT32 ui32AreaFlags, struct page ***pppsPageList, IMG_HANDLE *phBlockPageList, IMG_UINT32 ui32NumPages, IMG_BOOL *pbFromPagePool)
{
struct page **ppsPageList;
IMG_HANDLE hBlockPageList;
IMG_INT32 i;
PVRSRV_ERROR eError;
IMG_BOOL bFromPagePool = IMG_FALSE;
eError = OSAllocMem(0, sizeof(*ppsPageList) * ui32NumPages, (IMG_VOID **)&ppsPageList, &hBlockPageList,
"Array of pages");
if (eError != PVRSRV_OK)
{
goto failed_page_list_alloc;
}
*pbFromPagePool = IMG_TRUE;
for(i = 0; i < (IMG_INT32)ui32NumPages; i++)
{
ppsPageList[i] = AllocPage(ui32AreaFlags, &bFromPagePool);
if (!ppsPageList[i])
{
goto failed_alloc_pages;
}
*pbFromPagePool &= bFromPagePool;
}
*pppsPageList = ppsPageList;
*phBlockPageList = hBlockPageList;
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES,
ppsPageList,
0,
0,
NULL,
PAGES_TO_BYTES(ui32NumPages),
"unknown",
0
);
#endif
return IMG_TRUE;
failed_alloc_pages:
for(i--; i >= 0; i--)
{
FreePage(*pbFromPagePool, ppsPageList[i]);
}
(IMG_VOID) OSFreeMem(0, sizeof(*ppsPageList) * ui32NumPages, ppsPageList, hBlockPageList);
failed_page_list_alloc:
return IMG_FALSE;
}
static IMG_VOID
FreePages(IMG_BOOL bToPagePool, struct page **ppsPageList, IMG_HANDLE hBlockPageList, IMG_UINT32 ui32NumPages)
{
IMG_INT32 i;
for(i = 0; i < (IMG_INT32)ui32NumPages; i++)
{
FreePage(bToPagePool, ppsPageList[i]);
}
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, ppsPageList, __FILE__, __LINE__);
#endif
(IMG_VOID) OSFreeMem(0, sizeof(*ppsPageList) * ui32NumPages, ppsPageList, hBlockPageList);
}
LinuxMemArea *
NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags)
{
LinuxMemArea *psLinuxMemArea = NULL;
IMG_VOID *pvCpuVAddr;
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
IMG_UINT32 ui32NumPages = 0;
struct page **ppsPageList = NULL;
IMG_HANDLE hBlockPageList;
#endif
IMG_BOOL bFromPagePool = IMG_FALSE;
psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
goto failed;
}
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
ui32NumPages = RANGE_TO_PAGES(ui32Bytes);
if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool))
{
goto failed;
}
pvCpuVAddr = VMapWrapper(ppsPageList, ui32NumPages, ui32AreaFlags);
#else
pvCpuVAddr = VMallocWrapper(ui32Bytes, ui32AreaFlags);
if (!pvCpuVAddr)
{
goto failed;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
ReservePages(pvCpuVAddr, ui32Bytes);
#endif
#endif
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_VMALLOC;
psLinuxMemArea->uData.sVmalloc.pvVmallocAddress = pvCpuVAddr;
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
psLinuxMemArea->uData.sVmalloc.ppsPageList = ppsPageList;
psLinuxMemArea->uData.sVmalloc.hBlockPageList = hBlockPageList;
#endif
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = ui32AreaFlags;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags);
#endif
if (AreaIsUncached(ui32AreaFlags) && !bFromPagePool)
{
OSInvalidateCPUCacheRangeKM(psLinuxMemArea, pvCpuVAddr, ui32Bytes);
}
return psLinuxMemArea;
failed:
PVR_DPF((PVR_DBG_ERROR, "%s: failed!", __FUNCTION__));
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
if (ppsPageList)
{
FreePages(bFromPagePool, ppsPageList, hBlockPageList, ui32NumPages);
}
#endif
if (psLinuxMemArea)
{
LinuxMemAreaStructFree(psLinuxMemArea);
}
return NULL;
}
IMG_VOID
FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
IMG_UINT32 ui32NumPages;
struct page **ppsPageList;
IMG_HANDLE hBlockPageList;
#endif
PVR_ASSERT(psLinuxMemArea);
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_VMALLOC);
PVR_ASSERT(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p",
__FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress));
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
VUnmapWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress);
ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize);
ppsPageList = psLinuxMemArea->uData.sVmalloc.ppsPageList;
hBlockPageList = psLinuxMemArea->uData.sVmalloc.hBlockPageList;
FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages);
#else
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress,
psLinuxMemArea->ui32ByteSize);
#endif
VFreeWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress);
#endif
LinuxMemAreaStructFree(psLinuxMemArea);
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
static IMG_VOID
ReservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length)
{
IMG_VOID *pvPage;
IMG_VOID *pvEnd = pvAddress + ui32Length;
for(pvPage = pvAddress; pvPage < pvEnd; pvPage += PAGE_SIZE)
{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
SetPageReserved(vmalloc_to_page(pvPage));
#else
mem_map_reserve(vmalloc_to_page(pvPage));
#endif
}
}
static IMG_VOID
UnreservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length)
{
IMG_VOID *pvPage;
IMG_VOID *pvEnd = pvAddress + ui32Length;
for(pvPage = pvAddress; pvPage < pvEnd; pvPage += PAGE_SIZE)
{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
ClearPageReserved(vmalloc_to_page(pvPage));
#else
mem_map_unreserve(vmalloc_to_page(pvPage));
#endif
}
}
#endif
IMG_VOID *
_IORemapWrapper(IMG_CPU_PHYADDR BasePAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32MappingFlags,
IMG_CHAR *pszFileName,
IMG_UINT32 ui32Line)
{
IMG_VOID *pvIORemapCookie;
switch (ui32MappingFlags & PVRSRV_HAP_CACHETYPE_MASK)
{
case PVRSRV_HAP_CACHED:
pvIORemapCookie = (IMG_VOID *)IOREMAP(BasePAddr.uiAddr, ui32Bytes);
break;
case PVRSRV_HAP_WRITECOMBINE:
pvIORemapCookie = (IMG_VOID *)IOREMAP_WC(BasePAddr.uiAddr, ui32Bytes);
break;
case PVRSRV_HAP_UNCACHED:
pvIORemapCookie = (IMG_VOID *)IOREMAP_UC(BasePAddr.uiAddr, ui32Bytes);
break;
default:
PVR_DPF((PVR_DBG_ERROR, "IORemapWrapper: unknown mapping flags"));
return NULL;
}
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
if (pvIORemapCookie)
{
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_IOREMAP,
pvIORemapCookie,
pvIORemapCookie,
BasePAddr.uiAddr,
NULL,
ui32Bytes,
pszFileName,
ui32Line
);
}
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
return pvIORemapCookie;
}
IMG_VOID
_IOUnmapWrapper(IMG_VOID *pvIORemapCookie, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line)
{
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IOREMAP, pvIORemapCookie, pszFileName, ui32Line);
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
iounmap(pvIORemapCookie);
}
LinuxMemArea *
NewIORemapLinuxMemArea(IMG_CPU_PHYADDR BasePAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32AreaFlags)
{
LinuxMemArea *psLinuxMemArea;
IMG_VOID *pvIORemapCookie;
psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
return NULL;
}
pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32AreaFlags);
if (!pvIORemapCookie)
{
LinuxMemAreaStructFree(psLinuxMemArea);
return NULL;
}
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_IOREMAP;
psLinuxMemArea->uData.sIORemap.pvIORemapCookie = pvIORemapCookie;
psLinuxMemArea->uData.sIORemap.CPUPhysAddr = BasePAddr;
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = ui32AreaFlags;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags);
#endif
return psLinuxMemArea;
}
IMG_VOID
FreeIORemapLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_IOREMAP);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
IOUnmapWrapper(psLinuxMemArea->uData.sIORemap.pvIORemapCookie);
LinuxMemAreaStructFree(psLinuxMemArea);
}
#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
static IMG_BOOL
TreatExternalPagesAsContiguous(IMG_SYS_PHYADDR *psSysPhysAddr, IMG_UINT32 ui32Bytes, IMG_BOOL bPhysContig)
{
IMG_UINT32 ui32;
IMG_UINT32 ui32AddrChk;
IMG_UINT32 ui32NumPages = RANGE_TO_PAGES(ui32Bytes);
for (ui32 = 0, ui32AddrChk = psSysPhysAddr[0].uiAddr;
ui32 < ui32NumPages;
ui32++, ui32AddrChk = (bPhysContig) ? (ui32AddrChk + PAGE_SIZE) : psSysPhysAddr[ui32].uiAddr)
{
if (!pfn_valid(PHYS_TO_PFN(ui32AddrChk)))
{
break;
}
}
if (ui32 == ui32NumPages)
{
return IMG_FALSE;
}
if (!bPhysContig)
{
for (ui32 = 0, ui32AddrChk = psSysPhysAddr[0].uiAddr;
ui32 < ui32NumPages;
ui32++, ui32AddrChk += PAGE_SIZE)
{
if (psSysPhysAddr[ui32].uiAddr != ui32AddrChk)
{
return IMG_FALSE;
}
}
}
return IMG_TRUE;
}
#endif
LinuxMemArea *NewExternalKVLinuxMemArea(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *pvCPUVAddr, IMG_UINT32 ui32Bytes, IMG_BOOL bPhysContig, IMG_UINT32 ui32AreaFlags)
{
LinuxMemArea *psLinuxMemArea;
psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
return NULL;
}
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_EXTERNAL_KV;
psLinuxMemArea->uData.sExternalKV.pvExternalKV = pvCPUVAddr;
psLinuxMemArea->uData.sExternalKV.bPhysContig =
#if !defined(PVR_MAKE_ALL_PFNS_SPECIAL)
(bPhysContig || TreatExternalPagesAsContiguous(pBasePAddr, ui32Bytes, bPhysContig))
? IMG_TRUE : IMG_FALSE;
#else
bPhysContig;
#endif
if (psLinuxMemArea->uData.sExternalKV.bPhysContig)
{
psLinuxMemArea->uData.sExternalKV.uPhysAddr.SysPhysAddr = *pBasePAddr;
}
else
{
psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr = pBasePAddr;
}
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = ui32AreaFlags;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags);
#endif
return psLinuxMemArea;
}
IMG_VOID
FreeExternalKVLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_EXTERNAL_KV);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
LinuxMemAreaStructFree(psLinuxMemArea);
}
LinuxMemArea *
NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr,
IMG_UINT32 ui32Bytes,
IMG_UINT32 ui32AreaFlags)
{
LinuxMemArea *psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
return NULL;
}
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_IO;
psLinuxMemArea->uData.sIO.CPUPhysAddr.uiAddr = BasePAddr.uiAddr;
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = ui32AreaFlags;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_IO,
(IMG_VOID *)BasePAddr.uiAddr,
0,
BasePAddr.uiAddr,
NULL,
ui32Bytes,
"unknown",
0
);
#endif
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags);
#endif
return psLinuxMemArea;
}
IMG_VOID
FreeIOLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_IO);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO,
(IMG_VOID *)psLinuxMemArea->uData.sIO.CPUPhysAddr.uiAddr, __FILE__, __LINE__);
#endif
LinuxMemAreaStructFree(psLinuxMemArea);
}
LinuxMemArea *
NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags)
{
LinuxMemArea *psLinuxMemArea;
IMG_UINT32 ui32NumPages;
struct page **ppsPageList;
IMG_HANDLE hBlockPageList;
IMG_BOOL bFromPagePool;
psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
goto failed_area_alloc;
}
ui32NumPages = RANGE_TO_PAGES(ui32Bytes);
if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool))
{
goto failed_alloc_pages;
}
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ALLOC_PAGES;
psLinuxMemArea->uData.sPageList.ppsPageList = ppsPageList;
psLinuxMemArea->uData.sPageList.hBlockPageList = hBlockPageList;
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = ui32AreaFlags;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
psLinuxMemArea->bNeedsCacheInvalidate = AreaIsUncached(ui32AreaFlags) && !bFromPagePool;
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags);
#endif
return psLinuxMemArea;
failed_alloc_pages:
LinuxMemAreaStructFree(psLinuxMemArea);
failed_area_alloc:
PVR_DPF((PVR_DBG_ERROR, "%s: failed", __FUNCTION__));
return NULL;
}
IMG_VOID
FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
IMG_UINT32 ui32NumPages;
struct page **ppsPageList;
IMG_HANDLE hBlockPageList;
PVR_ASSERT(psLinuxMemArea);
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ALLOC_PAGES);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize);
ppsPageList = psLinuxMemArea->uData.sPageList.ppsPageList;
hBlockPageList = psLinuxMemArea->uData.sPageList.hBlockPageList;
FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages);
LinuxMemAreaStructFree(psLinuxMemArea);
}
#if defined(CONFIG_ION_OMAP)
#include "env_perproc.h"
#include <linux/ion.h>
#include <linux/omap_ion.h>
extern struct ion_client *gpsIONClient;
LinuxMemArea *
NewIONLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags,
IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength)
{
const IMG_UINT32 ui32AllocDataLen =
offsetof(struct omap_ion_tiler_alloc_data, handle);
struct omap_ion_tiler_alloc_data asAllocData[2] = {};
u32 *pu32PageAddrs[2] = { NULL, NULL };
IMG_UINT32 i, ui32NumHandlesPerFd;
IMG_BYTE *pbPrivData = pvPrivData;
IMG_CPU_PHYADDR *pCPUPhysAddrs;
int iNumPages[2] = { 0, 0 };
LinuxMemArea *psLinuxMemArea;
psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate LinuxMemArea struct", __func__));
goto err_out;
}
BUG_ON(ui32PrivDataLength != ui32AllocDataLen &&
ui32PrivDataLength != ui32AllocDataLen * 2);
ui32NumHandlesPerFd = ui32PrivDataLength / ui32AllocDataLen;
for(i = 0; i < ui32NumHandlesPerFd; i++)
{
memcpy(&asAllocData[i], &pbPrivData[i * ui32AllocDataLen], ui32AllocDataLen);
if (omap_ion_tiler_alloc(gpsIONClient, &asAllocData[i]) < 0)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate via ion_tiler", __func__));
goto err_free;
}
if (omap_tiler_pages(gpsIONClient, asAllocData[i].handle, &iNumPages[i],
&pu32PageAddrs[i]) < 0)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute tiler pages", __func__));
goto err_free;
}
}
BUG_ON(ui32Bytes != (iNumPages[0] + iNumPages[1]) * PAGE_SIZE);
BUG_ON(sizeof(IMG_CPU_PHYADDR) != sizeof(int));
pCPUPhysAddrs = vmalloc(sizeof(IMG_CPU_PHYADDR) * (iNumPages[0] + iNumPages[1]));
if (!pCPUPhysAddrs)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate page list", __func__));
goto err_free;
}
for(i = 0; i < iNumPages[0]; i++)
pCPUPhysAddrs[i].uiAddr = pu32PageAddrs[0][i];
for(i = 0; i < iNumPages[1]; i++)
pCPUPhysAddrs[iNumPages[0] + i].uiAddr = pu32PageAddrs[1][i];
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ION,
asAllocData[0].handle,
0,
0,
NULL,
PAGE_ALIGN(ui32Bytes),
"unknown",
0
);
#endif
for(i = 0; i < 2; i++)
psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = asAllocData[i].handle;
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ION;
psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = pCPUPhysAddrs;
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = ui32AreaFlags;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
psLinuxMemArea->bNeedsCacheInvalidate = AreaIsUncached(ui32AreaFlags);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags);
#endif
err_out:
return psLinuxMemArea;
err_free:
LinuxMemAreaStructFree(psLinuxMemArea);
psLinuxMemArea = IMG_NULL;
goto err_out;
}
IMG_VOID
FreeIONLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
IMG_UINT32 i;
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ION,
psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[0],
__FILE__, __LINE__);
#endif
for(i = 0; i < 2; i++)
{
if (!psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i])
break;
ion_free(gpsIONClient, psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]);
psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = IMG_NULL;
}
vfree(psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs);
psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = IMG_NULL;
LinuxMemAreaStructFree(psLinuxMemArea);
}
#endif
struct page*
LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea,
IMG_UINT32 ui32ByteOffset)
{
IMG_UINT32 ui32PageIndex;
IMG_CHAR *pui8Addr;
switch (psLinuxMemArea->eAreaType)
{
case LINUX_MEM_AREA_ALLOC_PAGES:
ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset);
return psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageIndex];
case LINUX_MEM_AREA_VMALLOC:
pui8Addr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress;
pui8Addr += ui32ByteOffset;
return vmalloc_to_page(pui8Addr);
case LINUX_MEM_AREA_SUB_ALLOC:
return LinuxMemAreaOffsetToPage(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea,
psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset
+ ui32ByteOffset);
default:
PVR_DPF((PVR_DBG_ERROR,
"%s: Unsupported request for struct page from LinuxMemArea with type=%s",
__FUNCTION__, LinuxMemAreaTypeToString(psLinuxMemArea->eAreaType)));
return NULL;
}
}
LinuxKMemCache *
KMemCacheCreateWrapper(IMG_CHAR *pszName,
size_t Size,
size_t Align,
IMG_UINT32 ui32Flags)
{
#if defined(DEBUG_LINUX_SLAB_ALLOCATIONS)
ui32Flags |= SLAB_POISON|SLAB_RED_ZONE;
#endif
return kmem_cache_create(pszName, Size, Align, ui32Flags, NULL
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22))
, NULL
#endif
);
}
IMG_VOID
KMemCacheDestroyWrapper(LinuxKMemCache *psCache)
{
kmem_cache_destroy(psCache);
}
IMG_VOID *
_KMemCacheAllocWrapper(LinuxKMemCache *psCache,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14))
gfp_t Flags,
#else
IMG_INT Flags,
#endif
IMG_CHAR *pszFileName,
IMG_UINT32 ui32Line)
{
IMG_VOID *pvRet;
pvRet = kmem_cache_zalloc(psCache, Flags);
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE,
pvRet,
pvRet,
0,
psCache,
kmem_cache_size(psCache),
pszFileName,
ui32Line
);
#else
PVR_UNREFERENCED_PARAMETER(pszFileName);
PVR_UNREFERENCED_PARAMETER(ui32Line);
#endif
return pvRet;
}
LinuxMemArea *
NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea,
IMG_UINT32 ui32ByteOffset,
IMG_UINT32 ui32Bytes)
{
LinuxMemArea *psLinuxMemArea;
PVR_ASSERT((ui32ByteOffset+ui32Bytes) <= psParentLinuxMemArea->ui32ByteSize);
psLinuxMemArea = LinuxMemAreaStructAlloc();
if (!psLinuxMemArea)
{
return NULL;
}
psLinuxMemArea->eAreaType = LINUX_MEM_AREA_SUB_ALLOC;
psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea = psParentLinuxMemArea;
psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset = ui32ByteOffset;
psLinuxMemArea->ui32ByteSize = ui32Bytes;
psLinuxMemArea->ui32AreaFlags = psParentLinuxMemArea->ui32AreaFlags;
psLinuxMemArea->bNeedsCacheInvalidate = psParentLinuxMemArea->bNeedsCacheInvalidate;
INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList);
#if defined(DEBUG_LINUX_MEM_AREAS)
{
DEBUG_LINUX_MEM_AREA_REC *psParentRecord;
psParentRecord = DebugLinuxMemAreaRecordFind(psParentLinuxMemArea);
DebugLinuxMemAreaRecordAdd(psLinuxMemArea, psParentRecord->ui32Flags);
}
#endif
return psLinuxMemArea;
}
static IMG_VOID
FreeSubLinuxMemArea(LinuxMemArea *psLinuxMemArea)
{
PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC);
#if defined(DEBUG_LINUX_MEM_AREAS)
DebugLinuxMemAreaRecordRemove(psLinuxMemArea);
#endif
LinuxMemAreaStructFree(psLinuxMemArea);
}
static LinuxMemArea *
LinuxMemAreaStructAlloc(IMG_VOID)
{
#if 0
LinuxMemArea *psLinuxMemArea;
psLinuxMemArea = kmem_cache_alloc(g_PsLinuxMemAreaCache, GFP_KERNEL);
printk(KERN_ERR "%s: psLinuxMemArea=%p\n", __FUNCTION__, psLinuxMemArea);
dump_stack();
return psLinuxMemArea;
#else
return KMemCacheAllocWrapper(g_PsLinuxMemAreaCache, GFP_KERNEL);
#endif
}
static IMG_VOID
LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea)
{
KMemCacheFreeWrapper(g_PsLinuxMemAreaCache, psLinuxMemArea);
}
IMG_VOID
LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea)
{
switch (psLinuxMemArea->eAreaType)
{
case LINUX_MEM_AREA_VMALLOC:
FreeVMallocLinuxMemArea(psLinuxMemArea);
break;
case LINUX_MEM_AREA_ALLOC_PAGES:
FreeAllocPagesLinuxMemArea(psLinuxMemArea);
break;
case LINUX_MEM_AREA_IOREMAP:
FreeIORemapLinuxMemArea(psLinuxMemArea);
break;
case LINUX_MEM_AREA_EXTERNAL_KV:
FreeExternalKVLinuxMemArea(psLinuxMemArea);
break;
case LINUX_MEM_AREA_IO:
FreeIOLinuxMemArea(psLinuxMemArea);
break;
case LINUX_MEM_AREA_SUB_ALLOC:
FreeSubLinuxMemArea(psLinuxMemArea);
break;
case LINUX_MEM_AREA_ION:
FreeIONLinuxMemArea(psLinuxMemArea);
break;
default:
PVR_DPF((PVR_DBG_ERROR, "%s: Unknown are type (%d)\n",
__FUNCTION__, psLinuxMemArea->eAreaType));
break;
}
}
#if defined(DEBUG_LINUX_MEM_AREAS)
static IMG_VOID
DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags)
{
DEBUG_LINUX_MEM_AREA_REC *psNewRecord;
const IMG_CHAR *pi8FlagsString;
LinuxLockMutex(&g_sDebugMutex);
if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC)
{
g_LinuxMemAreaWaterMark += psLinuxMemArea->ui32ByteSize;
if (g_LinuxMemAreaWaterMark > g_LinuxMemAreaHighWaterMark)
{
g_LinuxMemAreaHighWaterMark = g_LinuxMemAreaWaterMark;
}
}
g_LinuxMemAreaCount++;
psNewRecord = kmalloc(sizeof(DEBUG_LINUX_MEM_AREA_REC), GFP_KERNEL);
if (psNewRecord)
{
psNewRecord->psLinuxMemArea = psLinuxMemArea;
psNewRecord->ui32Flags = ui32Flags;
psNewRecord->pid = OSGetCurrentProcessIDKM();
List_DEBUG_LINUX_MEM_AREA_REC_Insert(&g_LinuxMemAreaRecords, psNewRecord);
}
else
{
PVR_DPF((PVR_DBG_ERROR,
"%s: failed to allocate linux memory area record.",
__FUNCTION__));
}
pi8FlagsString = HAPFlagsToString(ui32Flags);
if (strstr(pi8FlagsString, "UNKNOWN"))
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Unexpected flags (0x%08x) associated with psLinuxMemArea @ %p",
__FUNCTION__,
ui32Flags,
psLinuxMemArea));
}
LinuxUnLockMutex(&g_sDebugMutex);
}
static IMG_VOID* MatchLinuxMemArea_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord,
va_list va)
{
LinuxMemArea *psLinuxMemArea;
psLinuxMemArea = va_arg(va, LinuxMemArea*);
if (psCurrentRecord->psLinuxMemArea == psLinuxMemArea)
{
return psCurrentRecord;
}
else
{
return IMG_NULL;
}
}
static DEBUG_LINUX_MEM_AREA_REC *
DebugLinuxMemAreaRecordFind(LinuxMemArea *psLinuxMemArea)
{
DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord;
LinuxLockMutex(&g_sDebugMutex);
psCurrentRecord = List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords,
MatchLinuxMemArea_AnyVaCb,
psLinuxMemArea);
LinuxUnLockMutex(&g_sDebugMutex);
return psCurrentRecord;
}
static IMG_VOID
DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea)
{
DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord;
LinuxLockMutex(&g_sDebugMutex);
if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC)
{
g_LinuxMemAreaWaterMark -= psLinuxMemArea->ui32ByteSize;
}
g_LinuxMemAreaCount--;
psCurrentRecord = List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords,
MatchLinuxMemArea_AnyVaCb,
psLinuxMemArea);
if (psCurrentRecord)
{
List_DEBUG_LINUX_MEM_AREA_REC_Remove(psCurrentRecord);
kfree(psCurrentRecord);
}
else
{
PVR_DPF((PVR_DBG_ERROR, "%s: couldn't find an entry for psLinuxMemArea=%p\n",
__FUNCTION__, psLinuxMemArea));
}
LinuxUnLockMutex(&g_sDebugMutex);
}
#endif
IMG_VOID *
LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea)
{
switch (psLinuxMemArea->eAreaType)
{
case LINUX_MEM_AREA_VMALLOC:
return psLinuxMemArea->uData.sVmalloc.pvVmallocAddress;
case LINUX_MEM_AREA_IOREMAP:
return psLinuxMemArea->uData.sIORemap.pvIORemapCookie;
case LINUX_MEM_AREA_EXTERNAL_KV:
return psLinuxMemArea->uData.sExternalKV.pvExternalKV;
case LINUX_MEM_AREA_SUB_ALLOC:
{
IMG_CHAR *pAddr =
LinuxMemAreaToCpuVAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea);
if (!pAddr)
{
return NULL;
}
return pAddr + psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset;
}
default:
return NULL;
}
}
IMG_CPU_PHYADDR
LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset)
{
IMG_CPU_PHYADDR CpuPAddr;
CpuPAddr.uiAddr = 0;
switch (psLinuxMemArea->eAreaType)
{
case LINUX_MEM_AREA_IOREMAP:
{
CpuPAddr = psLinuxMemArea->uData.sIORemap.CPUPhysAddr;
CpuPAddr.uiAddr += ui32ByteOffset;
break;
}
case LINUX_MEM_AREA_EXTERNAL_KV:
{
if (psLinuxMemArea->uData.sExternalKV.bPhysContig)
{
CpuPAddr = SysSysPAddrToCpuPAddr(psLinuxMemArea->uData.sExternalKV.uPhysAddr.SysPhysAddr);
CpuPAddr.uiAddr += ui32ByteOffset;
}
else
{
IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset);
IMG_SYS_PHYADDR SysPAddr = psLinuxMemArea->uData.sExternalKV.uPhysAddr.pSysPhysAddr[ui32PageIndex];
CpuPAddr = SysSysPAddrToCpuPAddr(SysPAddr);
CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset);
}
break;
}
case LINUX_MEM_AREA_IO:
{
CpuPAddr = psLinuxMemArea->uData.sIO.CPUPhysAddr;
CpuPAddr.uiAddr += ui32ByteOffset;
break;
}
case LINUX_MEM_AREA_VMALLOC:
{
IMG_CHAR *pCpuVAddr;
pCpuVAddr =
(IMG_CHAR *)psLinuxMemArea->uData.sVmalloc.pvVmallocAddress;
pCpuVAddr += ui32ByteOffset;
CpuPAddr.uiAddr = VMallocToPhys(pCpuVAddr);
break;
}
case LINUX_MEM_AREA_ION:
{
IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset);
CpuPAddr = psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs[ui32PageIndex];
CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset);
break;
}
case LINUX_MEM_AREA_ALLOC_PAGES:
{
struct page *page;
IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset);
page = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageIndex];
CpuPAddr.uiAddr = page_to_phys(page);
CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset);
break;
}
case LINUX_MEM_AREA_SUB_ALLOC:
{
CpuPAddr =
OSMemHandleToCpuPAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea,
psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset
+ ui32ByteOffset);
break;
}
default:
{
PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)\n",
__FUNCTION__, psLinuxMemArea->eAreaType));
PVR_ASSERT(CpuPAddr.uiAddr);
break;
}
}
return CpuPAddr;
}
IMG_BOOL
LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea)
{
switch (psLinuxMemArea->eAreaType)
{
case LINUX_MEM_AREA_IOREMAP:
case LINUX_MEM_AREA_IO:
return IMG_TRUE;
case LINUX_MEM_AREA_EXTERNAL_KV:
return psLinuxMemArea->uData.sExternalKV.bPhysContig;
case LINUX_MEM_AREA_ION:
case LINUX_MEM_AREA_VMALLOC:
case LINUX_MEM_AREA_ALLOC_PAGES:
return IMG_FALSE;
case LINUX_MEM_AREA_SUB_ALLOC:
return LinuxMemAreaPhysIsContig(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea);
default:
PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)\n",
__FUNCTION__, psLinuxMemArea->eAreaType));
break;
}
return IMG_FALSE;
}
const IMG_CHAR *
LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType)
{
switch (eMemAreaType)
{
case LINUX_MEM_AREA_IOREMAP:
return "LINUX_MEM_AREA_IOREMAP";
case LINUX_MEM_AREA_EXTERNAL_KV:
return "LINUX_MEM_AREA_EXTERNAL_KV";
case LINUX_MEM_AREA_IO:
return "LINUX_MEM_AREA_IO";
case LINUX_MEM_AREA_VMALLOC:
return "LINUX_MEM_AREA_VMALLOC";
case LINUX_MEM_AREA_SUB_ALLOC:
return "LINUX_MEM_AREA_SUB_ALLOC";
case LINUX_MEM_AREA_ALLOC_PAGES:
return "LINUX_MEM_AREA_ALLOC_PAGES";
case LINUX_MEM_AREA_ION:
return "LINUX_MEM_AREA_ION";
default:
PVR_ASSERT(0);
}
return "";
}
#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
static void ProcSeqStartstopDebugMutex(struct seq_file *sfile, IMG_BOOL start)
{
if (start)
{
LinuxLockMutex(&g_sDebugMutex);
}
else
{
LinuxUnLockMutex(&g_sDebugMutex);
}
}
#endif
#if defined(DEBUG_LINUX_MEM_AREAS)
static IMG_VOID* DecOffMemAreaRec_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psNode, va_list va)
{
off_t *pOff = va_arg(va, off_t*);
if (--(*pOff))
{
return IMG_NULL;
}
else
{
return psNode;
}
}
static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off)
{
DEBUG_LINUX_MEM_AREA_REC *psRecord;
psRecord = (DEBUG_LINUX_MEM_AREA_REC*)
List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords,
DecOffMemAreaRec_AnyVaCb,
&off);
return (void*)psRecord;
}
static void* ProcSeqOff2ElementMemArea(struct seq_file * sfile, loff_t off)
{
DEBUG_LINUX_MEM_AREA_REC *psRecord;
if (!off)
{
return PVR_PROC_SEQ_START_TOKEN;
}
psRecord = (DEBUG_LINUX_MEM_AREA_REC*)
List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords,
DecOffMemAreaRec_AnyVaCb,
&off);
return (void*)psRecord;
}
static void ProcSeqShowMemArea(struct seq_file *sfile,void* el)
{
DEBUG_LINUX_MEM_AREA_REC *psRecord = (DEBUG_LINUX_MEM_AREA_REC*)el;
if (el == PVR_PROC_SEQ_START_TOKEN)
{
#if !defined(DEBUG_LINUX_XML_PROC_FILES)
seq_printf(sfile,
"Number of Linux Memory Areas: %u\n"
"At the current water mark these areas correspond to %u bytes (excluding SUB areas)\n"
"At the highest water mark these areas corresponded to %u bytes (excluding SUB areas)\n"
"\nDetails for all Linux Memory Areas:\n"
"%s %-24s %s %s %-8s %-5s %s\n",
g_LinuxMemAreaCount,
g_LinuxMemAreaWaterMark,
g_LinuxMemAreaHighWaterMark,
"psLinuxMemArea",
"LinuxMemType",
"CpuVAddr",
"CpuPAddr",
"Bytes",
"Pid",
"Flags"
);
#else
seq_printf(sfile,
"<mem_areas_header>\n"
"\t<count>%u</count>\n"
"\t<watermark key=\"mar0\" description=\"current\" bytes=\"%u\"/>\n"
"\t<watermark key=\"mar1\" description=\"high\" bytes=\"%u\"/>\n"
"</mem_areas_header>\n",
g_LinuxMemAreaCount,
g_LinuxMemAreaWaterMark,
g_LinuxMemAreaHighWaterMark
);
#endif
return;
}
seq_printf(sfile,
#if !defined(DEBUG_LINUX_XML_PROC_FILES)
"%8p %-24s %8p %08x %-8d %-5u %08x=(%s)\n",
#else
"<linux_mem_area>\n"
"\t<pointer>%8p</pointer>\n"
"\t<type>%s</type>\n"
"\t<cpu_virtual>%8p</cpu_virtual>\n"
"\t<cpu_physical>%08x</cpu_physical>\n"
"\t<bytes>%d</bytes>\n"
"\t<pid>%u</pid>\n"
"\t<flags>%08x</flags>\n"
"\t<flags_string>%s</flags_string>\n"
"</linux_mem_area>\n",
#endif
psRecord->psLinuxMemArea,
LinuxMemAreaTypeToString(psRecord->psLinuxMemArea->eAreaType),
LinuxMemAreaToCpuVAddr(psRecord->psLinuxMemArea),
LinuxMemAreaToCpuPAddr(psRecord->psLinuxMemArea,0).uiAddr,
psRecord->psLinuxMemArea->ui32ByteSize,
psRecord->pid,
psRecord->ui32Flags,
HAPFlagsToString(psRecord->ui32Flags)
);
}
#endif
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
static IMG_VOID* DecOffMemAllocRec_AnyVaCb(DEBUG_MEM_ALLOC_REC *psNode, va_list va)
{
off_t *pOff = va_arg(va, off_t*);
if (--(*pOff))
{
return IMG_NULL;
}
else
{
return psNode;
}
}
static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off)
{
DEBUG_MEM_ALLOC_REC *psRecord;
psRecord = (DEBUG_MEM_ALLOC_REC*)
List_DEBUG_MEM_ALLOC_REC_Any_va(g_MemoryRecords,
DecOffMemAllocRec_AnyVaCb,
&off);
#if defined(DEBUG_LINUX_XML_PROC_FILES)
if (!psRecord)
{
seq_printf(sfile, "</meminfo>\n");
}
#endif
return (void*)psRecord;
}
static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off)
{
DEBUG_MEM_ALLOC_REC *psRecord;
if (!off)
{
return PVR_PROC_SEQ_START_TOKEN;
}
psRecord = (DEBUG_MEM_ALLOC_REC*)
List_DEBUG_MEM_ALLOC_REC_Any_va(g_MemoryRecords,
DecOffMemAllocRec_AnyVaCb,
&off);
#if defined(DEBUG_LINUX_XML_PROC_FILES)
if (!psRecord)
{
seq_printf(sfile, "</meminfo>\n");
}
#endif
return (void*)psRecord;
}
static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el)
{
DEBUG_MEM_ALLOC_REC *psRecord = (DEBUG_MEM_ALLOC_REC*)el;
if (el == PVR_PROC_SEQ_START_TOKEN)
{
#if !defined(DEBUG_LINUX_XML_PROC_FILES)
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes allocated via kmalloc",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes allocated via kmalloc",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes allocated via vmalloc",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes allocated via vmalloc",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes allocated via alloc_pages",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes allocated via alloc_pages",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes allocated via ioremap",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes allocated via ioremap",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes reserved for \"IO\" memory areas",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes allocated for \"IO\" memory areas",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes allocated via kmem_cache_alloc",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes allocated via kmem_cache_alloc",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]);
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
seq_printf(sfile, "%-60s: %d bytes\n",
"Current Water Mark of bytes mapped via vmap",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]);
seq_printf(sfile, "%-60s: %d bytes\n",
"Highest Water Mark of bytes mapped via vmap",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]);
#endif
#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0)
seq_printf(sfile, "%-60s: %d pages\n",
"Number of pages in page pool",
atomic_read(&g_sPagePoolEntryCount));
#endif
seq_printf( sfile, "\n");
seq_printf(sfile, "%-60s: %d bytes\n",
"The Current Water Mark for memory allocated from system RAM",
SysRAMTrueWaterMark());
seq_printf(sfile, "%-60s: %d bytes\n",
"The Highest Water Mark for memory allocated from system RAM",
g_SysRAMHighWaterMark);
seq_printf(sfile, "%-60s: %d bytes\n",
"The Current Water Mark for memory allocated from IO memory",
g_IOMemWaterMark);
seq_printf(sfile, "%-60s: %d bytes\n",
"The Highest Water Mark for memory allocated from IO memory",
g_IOMemHighWaterMark);
seq_printf( sfile, "\n");
seq_printf(sfile, "Details for all known allocations:\n"
"%-16s %-8s %-8s %-10s %-5s %-10s %s\n",
"Type",
"CpuVAddr",
"CpuPAddr",
"Bytes",
"PID",
"PrivateData",
"Filename:Line");
#else
seq_printf(sfile, "<meminfo>\n<meminfo_header>\n");
seq_printf(sfile,
"<watermark key=\"mr0\" description=\"kmalloc_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]);
seq_printf(sfile,
"<watermark key=\"mr1\" description=\"kmalloc_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]);
seq_printf(sfile,
"<watermark key=\"mr2\" description=\"vmalloc_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]);
seq_printf(sfile,
"<watermark key=\"mr3\" description=\"vmalloc_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]);
seq_printf(sfile,
"<watermark key=\"mr4\" description=\"alloc_pages_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]);
seq_printf(sfile,
"<watermark key=\"mr5\" description=\"alloc_pages_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]);
seq_printf(sfile,
"<watermark key=\"mr6\" description=\"ioremap_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]);
seq_printf(sfile,
"<watermark key=\"mr7\" description=\"ioremap_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]);
seq_printf(sfile,
"<watermark key=\"mr8\" description=\"io_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]);
seq_printf(sfile,
"<watermark key=\"mr9\" description=\"io_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]);
seq_printf(sfile,
"<watermark key=\"mr10\" description=\"kmem_cache_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]);
seq_printf(sfile,
"<watermark key=\"mr11\" description=\"kmem_cache_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]);
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
seq_printf(sfile,
"<watermark key=\"mr12\" description=\"vmap_current\" bytes=\"%d\"/>\n",
g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]);
seq_printf(sfile,
"<watermark key=\"mr13\" description=\"vmap_high\" bytes=\"%d\"/>\n",
g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]);
#endif
seq_printf(sfile,
"<watermark key=\"mr14\" description=\"system_ram_current\" bytes=\"%d\"/>\n",
SysRAMTrueWaterMark());
seq_printf(sfile,
"<watermark key=\"mr15\" description=\"system_ram_high\" bytes=\"%d\"/>\n",
g_SysRAMHighWaterMark);
seq_printf(sfile,
"<watermark key=\"mr16\" description=\"system_io_current\" bytes=\"%d\"/>\n",
g_IOMemWaterMark);
seq_printf(sfile,
"<watermark key=\"mr17\" description=\"system_io_high\" bytes=\"%d\"/>\n",
g_IOMemHighWaterMark);
#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0)
seq_printf(sfile,
"<watermark key=\"mr18\" description=\"page_pool_current\" bytes=\"%d\"/>\n",
PAGES_TO_BYTES(atomic_read(&g_sPagePoolEntryCount)));
#endif
seq_printf(sfile, "</meminfo_header>\n");
#endif
return;
}
if (psRecord->eAllocType != DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE)
{
seq_printf(sfile,
#if !defined(DEBUG_LINUX_XML_PROC_FILES)
"%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n",
#else
"<allocation>\n"
"\t<type>%s</type>\n"
"\t<cpu_virtual>%-8p</cpu_virtual>\n"
"\t<cpu_physical>%08x</cpu_physical>\n"
"\t<bytes>%d</bytes>\n"
"\t<pid>%d</pid>\n"
"\t<private>%s</private>\n"
"\t<filename>%s</filename>\n"
"\t<line>%d</line>\n"
"</allocation>\n",
#endif
DebugMemAllocRecordTypeToString(psRecord->eAllocType),
psRecord->pvCpuVAddr,
psRecord->ulCpuPAddr,
psRecord->ui32Bytes,
psRecord->pid,
"NULL",
psRecord->pszFileName,
psRecord->ui32Line);
}
else
{
seq_printf(sfile,
#if !defined(DEBUG_LINUX_XML_PROC_FILES)
"%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n",
#else
"<allocation>\n"
"\t<type>%s</type>\n"
"\t<cpu_virtual>%-8p</cpu_virtual>\n"
"\t<cpu_physical>%08x</cpu_physical>\n"
"\t<bytes>%d</bytes>\n"
"\t<pid>%d</pid>\n"
"\t<private>%s</private>\n"
"\t<filename>%s</filename>\n"
"\t<line>%d</line>\n"
"</allocation>\n",
#endif
DebugMemAllocRecordTypeToString(psRecord->eAllocType),
psRecord->pvCpuVAddr,
psRecord->ulCpuPAddr,
psRecord->ui32Bytes,
psRecord->pid,
KMemCacheNameWrapper(psRecord->pvPrivateData),
psRecord->pszFileName,
psRecord->ui32Line);
}
}
#endif
#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MMAP_AREAS)
const IMG_CHAR *
HAPFlagsToString(IMG_UINT32 ui32Flags)
{
static IMG_CHAR szFlags[50];
IMG_INT32 i32Pos = 0;
IMG_UINT32 ui32CacheTypeIndex, ui32MapTypeIndex;
IMG_CHAR *apszCacheTypes[] = {
"UNCACHED",
"CACHED",
"WRITECOMBINE",
"UNKNOWN"
};
IMG_CHAR *apszMapType[] = {
"KERNEL_ONLY",
"SINGLE_PROCESS",
"MULTI_PROCESS",
"FROM_EXISTING_PROCESS",
"NO_CPU_VIRTUAL",
"UNKNOWN"
};
if (ui32Flags & PVRSRV_HAP_UNCACHED) {
ui32CacheTypeIndex = 0;
} else if (ui32Flags & PVRSRV_HAP_CACHED) {
ui32CacheTypeIndex = 1;
} else if (ui32Flags & PVRSRV_HAP_WRITECOMBINE) {
ui32CacheTypeIndex = 2;
} else {
ui32CacheTypeIndex = 3;
PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type (%u)",
__FUNCTION__, (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK)));
}
if (ui32Flags & PVRSRV_HAP_KERNEL_ONLY) {
ui32MapTypeIndex = 0;
} else if (ui32Flags & PVRSRV_HAP_SINGLE_PROCESS) {
ui32MapTypeIndex = 1;
} else if (ui32Flags & PVRSRV_HAP_MULTI_PROCESS) {
ui32MapTypeIndex = 2;
} else if (ui32Flags & PVRSRV_HAP_FROM_EXISTING_PROCESS) {
ui32MapTypeIndex = 3;
} else if (ui32Flags & PVRSRV_HAP_NO_CPU_VIRTUAL) {
ui32MapTypeIndex = 4;
} else {
ui32MapTypeIndex = 5;
PVR_DPF((PVR_DBG_ERROR, "%s: unknown map type (%u)",
__FUNCTION__, (ui32Flags & PVRSRV_HAP_MAPTYPE_MASK)));
}
i32Pos = sprintf(szFlags, "%s|", apszCacheTypes[ui32CacheTypeIndex]);
if (i32Pos <= 0)
{
PVR_DPF((PVR_DBG_ERROR, "%s: sprintf for cache type %u failed (%d)",
__FUNCTION__, ui32CacheTypeIndex, i32Pos));
szFlags[0] = 0;
}
else
{
sprintf(szFlags + i32Pos, "%s", apszMapType[ui32MapTypeIndex]);
}
return szFlags;
}
#endif
#if defined(DEBUG_LINUX_MEM_AREAS)
static IMG_VOID LinuxMMCleanup_MemAreas_ForEachCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord)
{
LinuxMemArea *psLinuxMemArea;
psLinuxMemArea = psCurrentRecord->psLinuxMemArea;
PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up Linux memory area (%p), type=%s, size=%d bytes",
__FUNCTION__,
psCurrentRecord->psLinuxMemArea,
LinuxMemAreaTypeToString(psCurrentRecord->psLinuxMemArea->eAreaType),
psCurrentRecord->psLinuxMemArea->ui32ByteSize));
LinuxMemAreaDeepFree(psLinuxMemArea);
}
#endif
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
static IMG_VOID LinuxMMCleanup_MemRecords_ForEachVa(DEBUG_MEM_ALLOC_REC *psCurrentRecord)
{
PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up memory: "
"type=%s "
"CpuVAddr=%p "
"CpuPAddr=0x%08x, "
"allocated @ file=%s,line=%d",
__FUNCTION__,
DebugMemAllocRecordTypeToString(psCurrentRecord->eAllocType),
psCurrentRecord->pvCpuVAddr,
psCurrentRecord->ulCpuPAddr,
psCurrentRecord->pszFileName,
psCurrentRecord->ui32Line));
switch (psCurrentRecord->eAllocType)
{
case DEBUG_MEM_ALLOC_TYPE_KMALLOC:
KFreeWrapper(psCurrentRecord->pvCpuVAddr);
break;
case DEBUG_MEM_ALLOC_TYPE_IOREMAP:
IOUnmapWrapper(psCurrentRecord->pvCpuVAddr);
break;
case DEBUG_MEM_ALLOC_TYPE_IO:
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO, psCurrentRecord->pvKey, __FILE__, __LINE__);
break;
case DEBUG_MEM_ALLOC_TYPE_VMALLOC:
VFreeWrapper(psCurrentRecord->pvCpuVAddr);
break;
case DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES:
DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, psCurrentRecord->pvKey, __FILE__, __LINE__);
break;
case DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE:
KMemCacheFreeWrapper(psCurrentRecord->pvPrivateData, psCurrentRecord->pvCpuVAddr);
break;
#if defined(PVR_LINUX_MEM_AREA_USE_VMAP)
case DEBUG_MEM_ALLOC_TYPE_VMAP:
VUnmapWrapper(psCurrentRecord->pvCpuVAddr);
break;
#endif
default:
PVR_ASSERT(0);
}
}
#endif
#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK)
static struct shrinker g_sShrinker =
{
.shrink = ShrinkPagePool,
.seeks = DEFAULT_SEEKS
};
static IMG_BOOL g_bShrinkerRegistered;
#endif
IMG_VOID
LinuxMMCleanup(IMG_VOID)
{
#if defined(DEBUG_LINUX_MEM_AREAS)
{
if (g_LinuxMemAreaCount)
{
PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: There are %d LinuxMemArea allocation unfreed (%d bytes)",
__FUNCTION__, g_LinuxMemAreaCount, g_LinuxMemAreaWaterMark));
}
List_DEBUG_LINUX_MEM_AREA_REC_ForEach(g_LinuxMemAreaRecords, LinuxMMCleanup_MemAreas_ForEachCb);
if (g_SeqFileMemArea)
{
RemoveProcEntrySeq(g_SeqFileMemArea);
}
}
#endif
#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK)
if (g_bShrinkerRegistered)
{
unregister_shrinker(&g_sShrinker);
}
#endif
FreePagePool();
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
{
List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, LinuxMMCleanup_MemRecords_ForEachVa);
if (g_SeqFileMemoryRecords)
{
RemoveProcEntrySeq(g_SeqFileMemoryRecords);
}
}
#endif
if (g_PsLinuxMemAreaCache)
{
KMemCacheDestroyWrapper(g_PsLinuxMemAreaCache);
}
if (g_PsLinuxPagePoolCache)
{
KMemCacheDestroyWrapper(g_PsLinuxPagePoolCache);
}
}
PVRSRV_ERROR
LinuxMMInit(IMG_VOID)
{
#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
LinuxInitMutex(&g_sDebugMutex);
#endif
#if defined(DEBUG_LINUX_MEM_AREAS)
{
g_SeqFileMemArea = CreateProcReadEntrySeq(
"mem_areas",
NULL,
ProcSeqNextMemArea,
ProcSeqShowMemArea,
ProcSeqOff2ElementMemArea,
ProcSeqStartstopDebugMutex
);
if (!g_SeqFileMemArea)
{
goto failed;
}
}
#endif
#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS)
{
g_SeqFileMemoryRecords = CreateProcReadEntrySeq(
"meminfo",
NULL,
ProcSeqNextMemoryRecords,
ProcSeqShowMemoryRecords,
ProcSeqOff2ElementMemoryRecords,
ProcSeqStartstopDebugMutex
);
if (!g_SeqFileMemoryRecords)
{
goto failed;
}
}
#endif
g_PsLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0);
if (!g_PsLinuxMemAreaCache)
{
PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate mem area kmem_cache", __FUNCTION__));
goto failed;
}
#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0)
g_iPagePoolMaxEntries = PVR_LINUX_MEM_AREA_POOL_MAX_PAGES;
if (g_iPagePoolMaxEntries <= 0 || g_iPagePoolMaxEntries > INT_MAX/2)
{
g_iPagePoolMaxEntries = INT_MAX/2;
PVR_TRACE(("%s: No limit set for page pool size", __FUNCTION__));
}
else
{
PVR_TRACE(("%s: Maximum page pool size: %d", __FUNCTION__, g_iPagePoolMaxEntries));
}
g_PsLinuxPagePoolCache = KMemCacheCreateWrapper("img-mm-pool", sizeof(LinuxPagePoolEntry), 0, 0);
if (!g_PsLinuxPagePoolCache)
{
PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate page pool kmem_cache", __FUNCTION__));
goto failed;
}
#endif
#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK)
register_shrinker(&g_sShrinker);
g_bShrinkerRegistered = IMG_TRUE;
#endif
return PVRSRV_OK;
failed:
LinuxMMCleanup();
return PVRSRV_ERROR_OUT_OF_MEMORY;
}