| /********************************************************************** |
| * |
| * 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. <gpl-support@imgtec.com> |
| * 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 |
| |
| #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> |
| |
| #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 |
| |
| #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_COUNT, |
| DEBUG_MEM_ALLOC_TYPE_ION |
| }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 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 =0; |
| 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=0; |
| |
| 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 |
| |
| static LinuxKMemCache *psLinuxMemAreaCache; |
| |
| |
| #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 |
| |
| 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) |
| { |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| } |
| #endif |
| |
| |
| #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) |
| { |
| g_SeqFileMemoryRecords =CreateProcReadEntrySeq( |
| "meminfo", |
| NULL, |
| ProcSeqNextMemoryRecords, |
| ProcSeqShowMemoryRecords, |
| ProcSeqOff2ElementMemoryRecords, |
| ProcSeqStartstopDebugMutex |
| ); |
| if(!g_SeqFileMemoryRecords) |
| { |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| } |
| #endif |
| |
| psLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0); |
| if(!psLinuxMemAreaCache) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__)); |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| #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; |
| default: |
| PVR_ASSERT(0); |
| } |
| } |
| #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); |
| |
| RemoveProcEntrySeq( g_SeqFileMemArea ); |
| } |
| #endif |
| |
| |
| #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) |
| { |
| |
| |
| List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, |
| LinuxMMCleanup_MemRecords_ForEachVa); |
| |
| RemoveProcEntrySeq( g_SeqFileMemoryRecords ); |
| } |
| #endif |
| |
| if(psLinuxMemAreaCache) |
| { |
| KMemCacheDestroyWrapper(psLinuxMemAreaCache); |
| psLinuxMemAreaCache=NULL; |
| } |
| } |
| |
| |
| 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) |
| { |
| g_SysRAMWaterMark += ui32Bytes; |
| if(g_SysRAMWaterMark > g_SysRAMHighWaterMark) |
| { |
| g_SysRAMHighWaterMark = g_SysRAMWaterMark; |
| } |
| } |
| 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" |
| }; |
| return apszDebugMemoryRecordTypes[eAllocType]; |
| } |
| #endif |
| |
| |
| |
| IMG_VOID * |
| _VMallocWrapper(IMG_UINT32 ui32Bytes, |
| IMG_UINT32 ui32AllocFlags, |
| IMG_CHAR *pszFileName, |
| IMG_UINT32 ui32Line) |
| { |
| pgprot_t PGProtFlags; |
| IMG_VOID *pvRet; |
| |
| 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, |
| "VMAllocWrapper: unknown mapping flags=0x%08x", |
| ui32AllocFlags)); |
| dump_stack(); |
| 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); |
| } |
| |
| |
| LinuxMemArea * |
| NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) |
| { |
| LinuxMemArea *psLinuxMemArea; |
| IMG_VOID *pvCpuVAddr; |
| |
| psLinuxMemArea = LinuxMemAreaStructAlloc(); |
| if(!psLinuxMemArea) |
| { |
| goto failed; |
| } |
| |
| pvCpuVAddr = VMallocWrapper(ui32Bytes, ui32AreaFlags); |
| if(!pvCpuVAddr) |
| { |
| goto failed; |
| } |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) |
| |
| ReservePages(pvCpuVAddr, ui32Bytes); |
| #endif |
| |
| psLinuxMemArea->eAreaType = LINUX_MEM_AREA_VMALLOC; |
| psLinuxMemArea->uData.sVmalloc.pvVmallocAddress = pvCpuVAddr; |
| psLinuxMemArea->ui32ByteSize = ui32Bytes; |
| psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; |
| INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); |
| |
| #if defined(DEBUG_LINUX_MEM_AREAS) |
| DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); |
| #endif |
| |
| |
| if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) |
| OSInvalidateCPUCacheRangeKM(psLinuxMemArea, pvCpuVAddr, ui32Bytes); |
| |
| return psLinuxMemArea; |
| |
| failed: |
| PVR_DPF((PVR_DBG_ERROR, "%s: failed!", __FUNCTION__)); |
| if(psLinuxMemArea) |
| LinuxMemAreaStructFree(psLinuxMemArea); |
| return NULL; |
| } |
| |
| |
| IMG_VOID |
| FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea) |
| { |
| 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 |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) |
| UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress, |
| psLinuxMemArea->ui32ByteSize); |
| #endif |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p", |
| __FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress)); |
| VFreeWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); |
| |
| 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 ui32PageCount; |
| struct page **pvPageList; |
| IMG_HANDLE hBlockPageList; |
| IMG_INT32 i; |
| PVRSRV_ERROR eError; |
| |
| psLinuxMemArea = LinuxMemAreaStructAlloc(); |
| if(!psLinuxMemArea) |
| { |
| goto failed_area_alloc; |
| } |
| |
| ui32PageCount = RANGE_TO_PAGES(ui32Bytes); |
| eError = OSAllocMem(0, sizeof(*pvPageList) * ui32PageCount, (IMG_VOID **)&pvPageList, &hBlockPageList, |
| "Array of pages"); |
| if(eError != PVRSRV_OK) |
| { |
| goto failed_page_list_alloc; |
| } |
| |
| for(i=0; i<(IMG_INT32)ui32PageCount; i++) |
| { |
| pvPageList[i] = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); |
| if(!pvPageList[i]) |
| { |
| goto failed_alloc_pages; |
| } |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) |
| SetPageReserved(pvPageList[i]); |
| #else |
| mem_map_reserve(pvPageList[i]); |
| #endif |
| #endif |
| |
| } |
| |
| #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) |
| DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, |
| pvPageList, |
| 0, |
| 0, |
| NULL, |
| PAGE_ALIGN(ui32Bytes), |
| "unknown", |
| 0 |
| ); |
| #endif |
| |
| psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ALLOC_PAGES; |
| psLinuxMemArea->uData.sPageList.pvPageList = pvPageList; |
| psLinuxMemArea->uData.sPageList.hBlockPageList = hBlockPageList; |
| psLinuxMemArea->ui32ByteSize = ui32Bytes; |
| psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; |
| INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); |
| |
| |
| if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) |
| { |
| psLinuxMemArea->bNeedsCacheInvalidate = IMG_TRUE; |
| } |
| |
| #if defined(DEBUG_LINUX_MEM_AREAS) |
| DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); |
| #endif |
| |
| return psLinuxMemArea; |
| |
| failed_alloc_pages: |
| for(i--; i >= 0; i--) |
| { |
| __free_pages(pvPageList[i], 0); |
| } |
| (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); |
| psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; |
| failed_page_list_alloc: |
| LinuxMemAreaStructFree(psLinuxMemArea); |
| failed_area_alloc: |
| PVR_DPF((PVR_DBG_ERROR, "%s: failed", __FUNCTION__)); |
| |
| return NULL; |
| } |
| |
| |
| IMG_VOID |
| FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) |
| { |
| IMG_UINT32 ui32PageCount; |
| struct page **pvPageList; |
| IMG_HANDLE hBlockPageList; |
| IMG_INT32 i; |
| |
| PVR_ASSERT(psLinuxMemArea); |
| PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ALLOC_PAGES); |
| |
| #if defined(DEBUG_LINUX_MEM_AREAS) |
| DebugLinuxMemAreaRecordRemove(psLinuxMemArea); |
| #endif |
| |
| ui32PageCount = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); |
| pvPageList = psLinuxMemArea->uData.sPageList.pvPageList; |
| hBlockPageList = psLinuxMemArea->uData.sPageList.hBlockPageList; |
| |
| #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) |
| DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, pvPageList, __FILE__, __LINE__); |
| #endif |
| |
| for(i=0;i<(IMG_INT32)ui32PageCount;i++) |
| { |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) |
| ClearPageReserved(pvPageList[i]); |
| #else |
| mem_map_reserve(pvPageList[i]); |
| #endif |
| #endif |
| __free_pages(pvPageList[i], 0); |
| } |
| |
| (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); |
| psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; |
| |
| 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) |
| { |
| struct omap_ion_tiler_alloc_data sAllocData; |
| LinuxMemArea *psLinuxMemArea; |
| u32 *pu32PageAddrs; |
| int iNumPages; |
| |
| psLinuxMemArea = LinuxMemAreaStructAlloc(); |
| if(!psLinuxMemArea) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate LinuxMemArea struct", __func__)); |
| goto err_out; |
| } |
| |
| |
| BUG_ON(ui32PrivDataLength != offsetof(struct omap_ion_tiler_alloc_data, handle)); |
| memcpy(&sAllocData, pvPrivData, offsetof(struct omap_ion_tiler_alloc_data, handle)); |
| |
| if(omap_ion_tiler_alloc(gpsIONClient, &sAllocData) < 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate via ion_tiler", __func__)); |
| goto err_free; |
| } |
| |
| if(omap_tiler_pages(gpsIONClient, sAllocData.handle, &iNumPages, |
| &pu32PageAddrs) < 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute tiler pages", __func__)); |
| goto err_free; |
| } |
| |
| |
| BUG_ON(ui32Bytes != iNumPages * PAGE_SIZE); |
| |
| #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) |
| DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ION, |
| sAllocData.handle, |
| 0, |
| 0, |
| NULL, |
| PAGE_ALIGN(ui32Bytes), |
| "unknown", |
| 0 |
| ); |
| #endif |
| |
| psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ION; |
| psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = (IMG_CPU_PHYADDR *)pu32PageAddrs; |
| psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle = sAllocData.handle; |
| psLinuxMemArea->ui32ByteSize = ui32Bytes; |
| psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; |
| INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); |
| |
| |
| if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) |
| { |
| psLinuxMemArea->bNeedsCacheInvalidate = IMG_TRUE; |
| } |
| |
| #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) |
| { |
| #if defined(DEBUG_LINUX_MEM_AREAS) |
| DebugLinuxMemAreaRecordRemove(psLinuxMemArea); |
| #endif |
| |
| ion_free(gpsIONClient, psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle); |
| |
| #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) |
| DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ION, |
| psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle, |
| __FILE__, __LINE__); |
| #endif |
| |
| |
| psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = IMG_NULL; |
| psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle = 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.pvPageList[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; |
| } |
| |
| |
| 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 ""; |
| } |
| |
| |
| 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(psLinuxMemAreaCache, GFP_KERNEL); |
| printk(KERN_ERR "%s: psLinuxMemArea=%p\n", __FUNCTION__, psLinuxMemArea); |
| dump_stack(); |
| return psLinuxMemArea; |
| #else |
| return KMemCacheAllocWrapper(psLinuxMemAreaCache, GFP_KERNEL); |
| #endif |
| } |
| |
| |
| static IMG_VOID |
| LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea) |
| { |
| KMemCacheFreeWrapper(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.pvPageList[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)", |
| __FUNCTION__, psLinuxMemArea->eAreaType)); |
| dump_stack(); |
| 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)", |
| __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>%ld</bytes>\n" |
| "\t<pid>%u</pid>\n" |
| "\t<flags>%08lx</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]); |
| seq_printf( sfile, "\n"); |
| |
| seq_printf( sfile, "%-60s: %d bytes\n", |
| "The Current Water Mark for memory allocated from system RAM", |
| g_SysRAMWaterMark); |
| 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]); |
| seq_printf( sfile,"\n" ); |
| |
| seq_printf( sfile, |
| "<watermark key=\"mr14\" description=\"system_ram_current\" bytes=\"%d\"/>\n", |
| g_SysRAMWaterMark); |
| 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); |
| |
| 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 |
| |