| /********************************************************************** |
| * |
| * 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/module.h> |
| #include <linux/vmalloc.h> |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) |
| #include <linux/wrapper.h> |
| #endif |
| #include <linux/slab.h> |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) |
| #include <linux/highmem.h> |
| #endif |
| #include <asm/io.h> |
| #include <asm/page.h> |
| #include <asm/shmparam.h> |
| #include <asm/pgtable.h> |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) |
| #include <linux/sched.h> |
| #include <asm/current.h> |
| #endif |
| #if defined(SUPPORT_DRI_DRM) |
| #include <drm/drmP.h> |
| #endif |
| |
| #include "services_headers.h" |
| |
| #include "pvrmmap.h" |
| #include "mutils.h" |
| #include "mmap.h" |
| #include "mm.h" |
| #include "proc.h" |
| #include "mutex.h" |
| #include "handle.h" |
| #include "perproc.h" |
| #include "env_perproc.h" |
| #include "bridged_support.h" |
| #if defined(SUPPORT_DRI_DRM) |
| #include "pvr_drm.h" |
| #endif |
| |
| #if !defined(PVR_SECURE_HANDLES) && !defined (SUPPORT_SID_INTERFACE) |
| #error "The mmap code requires PVR_SECURE_HANDLES" |
| #endif |
| |
| PVRSRV_LINUX_MUTEX g_sMMapMutex; |
| |
| static LinuxKMemCache *g_psMemmapCache = NULL; |
| static LIST_HEAD(g_sMMapAreaList); |
| static LIST_HEAD(g_sMMapOffsetStructList); |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| static IMG_UINT32 g_ui32RegisteredAreas = 0; |
| static IMG_UINT32 g_ui32TotalByteSize = 0; |
| #endif |
| |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| static struct proc_dir_entry *g_ProcMMap; |
| #endif |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| #define MMAP2_PGOFF_RESOLUTION (32-PAGE_SHIFT+12) |
| #define RESERVED_PGOFF_BITS 1 |
| #define MAX_MMAP_HANDLE ((1UL<<(MMAP2_PGOFF_RESOLUTION-RESERVED_PGOFF_BITS))-1) |
| |
| #define FIRST_PHYSICAL_PFN 0 |
| #define LAST_PHYSICAL_PFN (FIRST_PHYSICAL_PFN + MAX_MMAP_HANDLE) |
| #define FIRST_SPECIAL_PFN (LAST_PHYSICAL_PFN + 1) |
| #define LAST_SPECIAL_PFN (FIRST_SPECIAL_PFN + MAX_MMAP_HANDLE) |
| |
| #else |
| |
| #if PAGE_SHIFT != 12 |
| #error This build variant has not yet been made non-4KB page-size aware |
| #endif |
| |
| #if defined(PVR_MMAP_OFFSET_BASE) |
| #define FIRST_SPECIAL_PFN PVR_MMAP_OFFSET_BASE |
| #else |
| #define FIRST_SPECIAL_PFN 0x80000000UL |
| #endif |
| |
| #if defined(PVR_NUM_MMAP_HANDLES) |
| #define MAX_MMAP_HANDLE PVR_NUM_MMAP_HANDLES |
| #else |
| #define MAX_MMAP_HANDLE 0x7fffffffUL |
| #endif |
| |
| #endif |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| static inline IMG_BOOL |
| PFNIsPhysical(IMG_UINT32 pfn) |
| { |
| |
| return ( (pfn <= LAST_PHYSICAL_PFN)) ? IMG_TRUE : IMG_FALSE; |
| } |
| |
| static inline IMG_BOOL |
| PFNIsSpecial(IMG_UINT32 pfn) |
| { |
| |
| return ((pfn >= FIRST_SPECIAL_PFN) ) ? IMG_TRUE : IMG_FALSE; |
| } |
| #endif |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| static inline IMG_HANDLE |
| MMapOffsetToHandle(IMG_UINT32 pfn) |
| { |
| if (PFNIsPhysical(pfn)) |
| { |
| PVR_ASSERT(PFNIsPhysical(pfn)); |
| return IMG_NULL; |
| } |
| return (IMG_HANDLE)(pfn - FIRST_SPECIAL_PFN); |
| } |
| #endif |
| |
| static inline IMG_UINT32 |
| #if defined (SUPPORT_SID_INTERFACE) |
| HandleToMMapOffset(IMG_SID hHandle) |
| #else |
| HandleToMMapOffset(IMG_HANDLE hHandle) |
| #endif |
| { |
| IMG_UINT32 ulHandle = (IMG_UINT32)hHandle; |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| if (PFNIsSpecial(ulHandle)) |
| { |
| PVR_ASSERT(PFNIsSpecial(ulHandle)); |
| return 0; |
| } |
| #endif |
| return ulHandle + FIRST_SPECIAL_PFN; |
| } |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| static inline IMG_BOOL |
| LinuxMemAreaUsesPhysicalMap(LinuxMemArea *psLinuxMemArea) |
| { |
| return LinuxMemAreaPhysIsContig(psLinuxMemArea); |
| } |
| #endif |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| static inline IMG_UINT32 |
| GetCurrentThreadID(IMG_VOID) |
| { |
| |
| return (IMG_UINT32)current->pid; |
| } |
| #endif |
| |
| static PKV_OFFSET_STRUCT |
| CreateOffsetStruct(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| #if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) |
| const IMG_CHAR *pszName = LinuxMemAreaTypeToString(LinuxMemAreaRootType(psLinuxMemArea)); |
| #endif |
| |
| #if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) |
| PVR_DPF((PVR_DBG_MESSAGE, |
| "%s(%s, psLinuxMemArea: 0x%p, ui32AllocFlags: 0x%8x)", |
| __FUNCTION__, pszName, psLinuxMemArea, psLinuxMemArea->ui32AreaFlags)); |
| #endif |
| |
| PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC || LinuxMemAreaRoot(psLinuxMemArea)->eAreaType != LINUX_MEM_AREA_SUB_ALLOC); |
| |
| PVR_ASSERT(psLinuxMemArea->bMMapRegistered); |
| |
| psOffsetStruct = KMemCacheAllocWrapper(g_psMemmapCache, GFP_KERNEL); |
| if(psOffsetStruct == IMG_NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRMMapRegisterArea: Couldn't alloc another mapping record from cache")); |
| return IMG_NULL; |
| } |
| |
| psOffsetStruct->ui32MMapOffset = ui32Offset; |
| |
| psOffsetStruct->psLinuxMemArea = psLinuxMemArea; |
| |
| psOffsetStruct->ui32RealByteSize = ui32RealByteSize; |
| |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| psOffsetStruct->ui32TID = GetCurrentThreadID(); |
| #endif |
| psOffsetStruct->ui32PID = OSGetCurrentProcessIDKM(); |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| |
| psOffsetStruct->pszName = pszName; |
| #endif |
| |
| list_add_tail(&psOffsetStruct->sAreaItem, &psLinuxMemArea->sMMapOffsetStructList); |
| |
| return psOffsetStruct; |
| } |
| |
| |
| static IMG_VOID |
| DestroyOffsetStruct(PKV_OFFSET_STRUCT psOffsetStruct) |
| { |
| #ifdef DEBUG |
| IMG_CPU_PHYADDR CpuPAddr; |
| CpuPAddr = LinuxMemAreaToCpuPAddr(psOffsetStruct->psLinuxMemArea, 0); |
| #endif |
| |
| list_del(&psOffsetStruct->sAreaItem); |
| |
| if (psOffsetStruct->bOnMMapList) |
| { |
| list_del(&psOffsetStruct->sMMapItem); |
| } |
| |
| #ifdef DEBUG |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Table entry: " |
| "psLinuxMemArea=%p, CpuPAddr=0x%08X", __FUNCTION__, |
| psOffsetStruct->psLinuxMemArea, |
| CpuPAddr.uiAddr)); |
| #endif |
| |
| KMemCacheFreeWrapper(g_psMemmapCache, psOffsetStruct); |
| } |
| |
| |
| static inline IMG_VOID |
| DetermineUsersSizeAndByteOffset(LinuxMemArea *psLinuxMemArea, |
| IMG_UINT32 *pui32RealByteSize, |
| IMG_UINT32 *pui32ByteOffset) |
| { |
| IMG_UINT32 ui32PageAlignmentOffset; |
| IMG_CPU_PHYADDR CpuPAddr; |
| |
| CpuPAddr = LinuxMemAreaToCpuPAddr(psLinuxMemArea, 0); |
| ui32PageAlignmentOffset = ADDR_TO_PAGE_OFFSET(CpuPAddr.uiAddr); |
| |
| *pui32ByteOffset = ui32PageAlignmentOffset; |
| |
| *pui32RealByteSize = PAGE_ALIGN(psLinuxMemArea->ui32ByteSize + ui32PageAlignmentOffset); |
| } |
| |
| |
| PVRSRV_ERROR |
| PVRMMapOSMemHandleToMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, |
| #if defined (SUPPORT_SID_INTERFACE) |
| IMG_SID hMHandle, |
| #else |
| IMG_HANDLE hMHandle, |
| #endif |
| IMG_UINT32 *pui32MMapOffset, |
| IMG_UINT32 *pui32ByteOffset, |
| IMG_UINT32 *pui32RealByteSize, |
| IMG_UINT32 *pui32UserVAddr) |
| { |
| LinuxMemArea *psLinuxMemArea; |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| IMG_HANDLE hOSMemHandle; |
| PVRSRV_ERROR eError; |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| PVR_ASSERT(PVRSRVGetMaxHandle(psPerProc->psHandleBase) <= MAX_MMAP_HANDLE); |
| |
| eError = PVRSRVLookupOSMemHandle(psPerProc->psHandleBase, &hOSMemHandle, hMHandle); |
| if (eError != PVRSRV_OK) |
| { |
| #if defined (SUPPORT_SID_INTERFACE) |
| PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %x failed", __FUNCTION__, hMHandle)); |
| #else |
| PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %p failed", __FUNCTION__, hMHandle)); |
| #endif |
| |
| goto exit_unlock; |
| } |
| |
| psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; |
| |
| DetermineUsersSizeAndByteOffset(psLinuxMemArea, |
| pui32RealByteSize, |
| pui32ByteOffset); |
| |
| |
| list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) |
| { |
| if (psPerProc->ui32PID == psOffsetStruct->ui32PID) |
| { |
| |
| PVR_ASSERT(*pui32RealByteSize == psOffsetStruct->ui32RealByteSize); |
| |
| *pui32MMapOffset = psOffsetStruct->ui32MMapOffset; |
| *pui32UserVAddr = psOffsetStruct->ui32UserVAddr; |
| PVRSRVOffsetStructIncRef(psOffsetStruct); |
| |
| eError = PVRSRV_OK; |
| goto exit_unlock; |
| } |
| } |
| |
| |
| *pui32UserVAddr = 0; |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| if (LinuxMemAreaUsesPhysicalMap(psLinuxMemArea)) |
| { |
| *pui32MMapOffset = LinuxMemAreaToCpuPFN(psLinuxMemArea, 0); |
| PVR_ASSERT(PFNIsPhysical(*pui32MMapOffset)); |
| } |
| else |
| #endif |
| { |
| *pui32MMapOffset = HandleToMMapOffset(hMHandle); |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| PVR_ASSERT(PFNIsSpecial(*pui32MMapOffset)); |
| #endif |
| } |
| |
| psOffsetStruct = CreateOffsetStruct(psLinuxMemArea, *pui32MMapOffset, *pui32RealByteSize); |
| if (psOffsetStruct == IMG_NULL) |
| { |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto exit_unlock; |
| } |
| |
| |
| list_add_tail(&psOffsetStruct->sMMapItem, &g_sMMapOffsetStructList); |
| |
| psOffsetStruct->bOnMMapList = IMG_TRUE; |
| |
| PVRSRVOffsetStructIncRef(psOffsetStruct); |
| |
| eError = PVRSRV_OK; |
| |
| |
| |
| |
| *pui32MMapOffset = *pui32MMapOffset << (PAGE_SHIFT - 12); |
| |
| exit_unlock: |
| LinuxUnLockMutex(&g_sMMapMutex); |
| |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR |
| PVRMMapReleaseMMapData(PVRSRV_PER_PROCESS_DATA *psPerProc, |
| #if defined (SUPPORT_SID_INTERFACE) |
| IMG_SID hMHandle, |
| #else |
| IMG_HANDLE hMHandle, |
| #endif |
| IMG_BOOL *pbMUnmap, |
| IMG_UINT32 *pui32RealByteSize, |
| IMG_UINT32 *pui32UserVAddr) |
| { |
| LinuxMemArea *psLinuxMemArea; |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| IMG_HANDLE hOSMemHandle; |
| PVRSRV_ERROR eError; |
| IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| PVR_ASSERT(PVRSRVGetMaxHandle(psPerProc->psHandleBase) <= MAX_MMAP_HANDLE); |
| |
| eError = PVRSRVLookupOSMemHandle(psPerProc->psHandleBase, &hOSMemHandle, hMHandle); |
| if (eError != PVRSRV_OK) |
| { |
| #if defined (SUPPORT_SID_INTERFACE) |
| PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %x failed", __FUNCTION__, hMHandle)); |
| #else |
| PVR_DPF((PVR_DBG_ERROR, "%s: Lookup of handle %p failed", __FUNCTION__, hMHandle)); |
| #endif |
| |
| goto exit_unlock; |
| } |
| |
| psLinuxMemArea = (LinuxMemArea *)hOSMemHandle; |
| |
| |
| list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) |
| { |
| if (psOffsetStruct->ui32PID == ui32PID) |
| { |
| if (psOffsetStruct->ui32RefCount == 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Attempt to release mmap data with zero reference count for offset struct 0x%p, memory area %p", __FUNCTION__, psOffsetStruct, psLinuxMemArea)); |
| eError = PVRSRV_ERROR_STILL_MAPPED; |
| goto exit_unlock; |
| } |
| |
| PVRSRVOffsetStructDecRef(psOffsetStruct); |
| |
| *pbMUnmap = (IMG_BOOL)((psOffsetStruct->ui32RefCount == 0) && (psOffsetStruct->ui32UserVAddr != 0)); |
| |
| *pui32UserVAddr = (*pbMUnmap) ? psOffsetStruct->ui32UserVAddr : 0; |
| *pui32RealByteSize = (*pbMUnmap) ? psOffsetStruct->ui32RealByteSize : 0; |
| |
| eError = PVRSRV_OK; |
| goto exit_unlock; |
| } |
| } |
| |
| |
| #if defined (SUPPORT_SID_INTERFACE) |
| PVR_DPF((PVR_DBG_ERROR, "%s: Mapping data not found for handle %x (memory area %p)", __FUNCTION__, hMHandle, psLinuxMemArea)); |
| #else |
| PVR_DPF((PVR_DBG_ERROR, "%s: Mapping data not found for handle %p (memory area %p)", __FUNCTION__, hMHandle, psLinuxMemArea)); |
| #endif |
| |
| eError = PVRSRV_ERROR_MAPPING_NOT_FOUND; |
| |
| exit_unlock: |
| LinuxUnLockMutex(&g_sMMapMutex); |
| |
| return eError; |
| } |
| |
| static inline PKV_OFFSET_STRUCT |
| FindOffsetStructByOffset(IMG_UINT32 ui32Offset, IMG_UINT32 ui32RealByteSize) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| IMG_UINT32 ui32TID = GetCurrentThreadID(); |
| #endif |
| IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); |
| |
| list_for_each_entry(psOffsetStruct, &g_sMMapOffsetStructList, sMMapItem) |
| { |
| if (ui32Offset == psOffsetStruct->ui32MMapOffset && ui32RealByteSize == psOffsetStruct->ui32RealByteSize && psOffsetStruct->ui32PID == ui32PID) |
| { |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| |
| if (!PFNIsPhysical(ui32Offset) || psOffsetStruct->ui32TID == ui32TID) |
| #endif |
| { |
| return psOffsetStruct; |
| } |
| } |
| } |
| |
| return IMG_NULL; |
| } |
| |
| |
| static IMG_BOOL |
| DoMapToUser(LinuxMemArea *psLinuxMemArea, |
| struct vm_area_struct* ps_vma, |
| IMG_UINT32 ui32ByteOffset) |
| { |
| IMG_UINT32 ui32ByteSize; |
| |
| if (psLinuxMemArea->eAreaType == LINUX_MEM_AREA_SUB_ALLOC) |
| { |
| return DoMapToUser(LinuxMemAreaRoot(psLinuxMemArea), |
| ps_vma, |
| psLinuxMemArea->uData.sSubAlloc.ui32ByteOffset + ui32ByteOffset); |
| } |
| |
| |
| ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start; |
| PVR_ASSERT(ADDR_TO_PAGE_OFFSET(ui32ByteSize) == 0); |
| |
| #if defined (__sparc__) |
| |
| #error "SPARC not supported" |
| #endif |
| |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| if (PFNIsPhysical(ps_vma->vm_pgoff)) |
| { |
| IMG_INT result; |
| |
| PVR_ASSERT(LinuxMemAreaPhysIsContig(psLinuxMemArea)); |
| PVR_ASSERT(LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32ByteOffset) == ps_vma->vm_pgoff); |
| |
| result = IO_REMAP_PFN_RANGE(ps_vma, ps_vma->vm_start, ps_vma->vm_pgoff, ui32ByteSize, ps_vma->vm_page_prot); |
| |
| if(result == 0) |
| { |
| return IMG_TRUE; |
| } |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Failed to map contiguous physical address range (%d), trying non-contiguous path", __FUNCTION__, result)); |
| } |
| #endif |
| |
| { |
| |
| IMG_UINT32 ulVMAPos; |
| IMG_UINT32 ui32ByteEnd = ui32ByteOffset + ui32ByteSize; |
| IMG_UINT32 ui32PA; |
| #if defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| IMG_BOOL bMixedMap = IMG_FALSE; |
| #endif |
| |
| for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE) |
| { |
| IMG_UINT32 pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA); |
| |
| if (!pfn_valid(pfn)) |
| { |
| #if !defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| PVR_DPF((PVR_DBG_ERROR,"%s: Error - PFN invalid: 0x%x", __FUNCTION__, pfn)); |
| return IMG_FALSE; |
| #else |
| bMixedMap = IMG_TRUE; |
| #endif |
| } |
| } |
| |
| #if defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| if (bMixedMap) |
| { |
| ps_vma->vm_flags |= VM_MIXEDMAP; |
| } |
| #endif |
| |
| ulVMAPos = ps_vma->vm_start; |
| for(ui32PA = ui32ByteOffset; ui32PA < ui32ByteEnd; ui32PA += PAGE_SIZE) |
| { |
| IMG_UINT32 pfn; |
| IMG_INT result; |
| |
| pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ui32PA); |
| |
| #if defined(PVR_MAKE_ALL_PFNS_SPECIAL) |
| if (bMixedMap) |
| { |
| result = vm_insert_mixed(ps_vma, ulVMAPos, pfn); |
| if(result != 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Error - vm_insert_mixed failed (%d)", __FUNCTION__, result)); |
| return IMG_FALSE; |
| } |
| } |
| else |
| #endif |
| { |
| struct page *psPage; |
| |
| PVR_ASSERT(pfn_valid(pfn)); |
| |
| psPage = pfn_to_page(pfn); |
| |
| result = VM_INSERT_PAGE(ps_vma, ulVMAPos, psPage); |
| if(result != 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Error - VM_INSERT_PAGE failed (%d)", __FUNCTION__, result)); |
| return IMG_FALSE; |
| } |
| } |
| ulVMAPos += PAGE_SIZE; |
| } |
| } |
| |
| return IMG_TRUE; |
| } |
| |
| |
| static IMG_VOID |
| MMapVOpenNoLock(struct vm_area_struct* ps_vma) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; |
| |
| PVR_ASSERT(psOffsetStruct != IMG_NULL); |
| PVR_ASSERT(!psOffsetStruct->bOnMMapList); |
| |
| PVRSRVOffsetStructIncMapped(psOffsetStruct); |
| |
| if (psOffsetStruct->ui32Mapped > 1) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "%s: Offset structure 0x%p is being shared across processes (psOffsetStruct->ui32Mapped: %u)", __FUNCTION__, psOffsetStruct, psOffsetStruct->ui32Mapped)); |
| PVR_ASSERT((ps_vma->vm_flags & VM_DONTCOPY) == 0); |
| } |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| |
| PVR_DPF((PVR_DBG_MESSAGE, |
| "%s: psLinuxMemArea 0x%p, KVAddress 0x%p MMapOffset %d, ui32Mapped %d", |
| __FUNCTION__, |
| psOffsetStruct->psLinuxMemArea, |
| LinuxMemAreaToCpuVAddr(psOffsetStruct->psLinuxMemArea), |
| psOffsetStruct->ui32MMapOffset, |
| psOffsetStruct->ui32Mapped)); |
| #endif |
| } |
| |
| |
| static void |
| MMapVOpen(struct vm_area_struct* ps_vma) |
| { |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| MMapVOpenNoLock(ps_vma); |
| |
| LinuxUnLockMutex(&g_sMMapMutex); |
| } |
| |
| |
| static IMG_VOID |
| MMapVCloseNoLock(struct vm_area_struct* ps_vma) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; |
| PVR_ASSERT(psOffsetStruct != IMG_NULL) |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| PVR_DPF((PVR_DBG_MESSAGE, |
| "%s: psLinuxMemArea %p, CpuVAddr %p ui32MMapOffset %d, ui32Mapped %d", |
| __FUNCTION__, |
| psOffsetStruct->psLinuxMemArea, |
| LinuxMemAreaToCpuVAddr(psOffsetStruct->psLinuxMemArea), |
| psOffsetStruct->ui32MMapOffset, |
| psOffsetStruct->ui32Mapped)); |
| #endif |
| |
| PVR_ASSERT(!psOffsetStruct->bOnMMapList); |
| PVRSRVOffsetStructDecMapped(psOffsetStruct); |
| if (psOffsetStruct->ui32Mapped == 0) |
| { |
| if (psOffsetStruct->ui32RefCount != 0) |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: psOffsetStruct %p has non-zero reference count (ui32RefCount = %u). User mode address of start of mapping: 0x%x", __FUNCTION__, psOffsetStruct, psOffsetStruct->ui32RefCount, psOffsetStruct->ui32UserVAddr)); |
| } |
| |
| DestroyOffsetStruct(psOffsetStruct); |
| } |
| |
| ps_vma->vm_private_data = NULL; |
| } |
| |
| static void |
| MMapVClose(struct vm_area_struct* ps_vma) |
| { |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| MMapVCloseNoLock(ps_vma); |
| |
| LinuxUnLockMutex(&g_sMMapMutex); |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) |
| static int MMapVAccess(struct vm_area_struct *ps_vma, unsigned long addr, |
| void *buf, int len, int write) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| LinuxMemArea *psLinuxMemArea; |
| unsigned long ulOffset; |
| int iRetVal = -EINVAL; |
| IMG_VOID *pvKernelAddr; |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| psOffsetStruct = (PKV_OFFSET_STRUCT)ps_vma->vm_private_data; |
| psLinuxMemArea = psOffsetStruct->psLinuxMemArea; |
| ulOffset = addr - ps_vma->vm_start; |
| |
| if (ulOffset+len > psLinuxMemArea->ui32ByteSize) |
| |
| goto exit_unlock; |
| |
| pvKernelAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea); |
| |
| if (pvKernelAddr) |
| { |
| memcpy(buf, pvKernelAddr+ulOffset, len); |
| iRetVal = len; |
| } |
| else |
| { |
| IMG_UINT32 pfn, ui32OffsetInPage; |
| struct page *page; |
| |
| pfn = LinuxMemAreaToCpuPFN(psLinuxMemArea, ulOffset); |
| |
| if (!pfn_valid(pfn)) |
| goto exit_unlock; |
| |
| page = pfn_to_page(pfn); |
| ui32OffsetInPage = ADDR_TO_PAGE_OFFSET(ulOffset); |
| |
| if (ui32OffsetInPage+len > PAGE_SIZE) |
| |
| goto exit_unlock; |
| |
| pvKernelAddr = kmap(page); |
| memcpy(buf, pvKernelAddr+ui32OffsetInPage, len); |
| kunmap(page); |
| |
| iRetVal = len; |
| } |
| |
| exit_unlock: |
| LinuxUnLockMutex(&g_sMMapMutex); |
| return iRetVal; |
| } |
| #endif |
| |
| static struct vm_operations_struct MMapIOOps = |
| { |
| .open=MMapVOpen, |
| .close=MMapVClose, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) |
| .access=MMapVAccess, |
| #endif |
| }; |
| |
| |
| int |
| PVRMMap(struct file* pFile, struct vm_area_struct* ps_vma) |
| { |
| LinuxMemArea *psFlushMemArea = IMG_NULL; |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| IMG_UINT32 ui32ByteSize; |
| IMG_VOID *pvBase = IMG_NULL; |
| int iRetVal = 0; |
| |
| PVR_UNREFERENCED_PARAMETER(pFile); |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| ui32ByteSize = ps_vma->vm_end - ps_vma->vm_start; |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Received mmap(2) request with ui32MMapOffset 0x%08lx," |
| " and ui32ByteSize %d(0x%08x)", |
| __FUNCTION__, |
| ps_vma->vm_pgoff, |
| ui32ByteSize, ui32ByteSize)); |
| |
| psOffsetStruct = FindOffsetStructByOffset(ps_vma->vm_pgoff, ui32ByteSize); |
| if (psOffsetStruct == IMG_NULL) |
| { |
| #if defined(SUPPORT_DRI_DRM) |
| LinuxUnLockMutex(&g_sMMapMutex); |
| |
| #if !defined(SUPPORT_DRI_DRM_EXT) |
| |
| return drm_mmap(pFile, ps_vma); |
| #else |
| |
| return -ENOENT; |
| #endif |
| #else |
| PVR_UNREFERENCED_PARAMETER(pFile); |
| |
| PVR_DPF((PVR_DBG_ERROR, |
| "%s: Attempted to mmap unregistered area at vm_pgoff 0x%lx", |
| __FUNCTION__, ps_vma->vm_pgoff)); |
| iRetVal = -EINVAL; |
| #endif |
| goto unlock_and_return; |
| } |
| |
| list_del(&psOffsetStruct->sMMapItem); |
| psOffsetStruct->bOnMMapList = IMG_FALSE; |
| |
| |
| if (((ps_vma->vm_flags & VM_WRITE) != 0) && |
| ((ps_vma->vm_flags & VM_SHARED) == 0)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Cannot mmap non-shareable writable areas", __FUNCTION__)); |
| iRetVal = -EINVAL; |
| goto unlock_and_return; |
| } |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Mapped psLinuxMemArea 0x%p\n", |
| __FUNCTION__, psOffsetStruct->psLinuxMemArea)); |
| |
| ps_vma->vm_flags |= VM_RESERVED; |
| ps_vma->vm_flags |= VM_IO; |
| |
| |
| ps_vma->vm_flags |= VM_DONTEXPAND; |
| |
| |
| ps_vma->vm_flags |= VM_DONTCOPY; |
| |
| ps_vma->vm_private_data = (void *)psOffsetStruct; |
| |
| switch(psOffsetStruct->psLinuxMemArea->ui32AreaFlags & PVRSRV_HAP_CACHETYPE_MASK) |
| { |
| case PVRSRV_HAP_CACHED: |
| |
| break; |
| case PVRSRV_HAP_WRITECOMBINE: |
| ps_vma->vm_page_prot = PGPROT_WC(ps_vma->vm_page_prot); |
| break; |
| case PVRSRV_HAP_UNCACHED: |
| ps_vma->vm_page_prot = PGPROT_UC(ps_vma->vm_page_prot); |
| break; |
| default: |
| PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type", __FUNCTION__)); |
| iRetVal = -EINVAL; |
| goto unlock_and_return; |
| } |
| |
| |
| ps_vma->vm_ops = &MMapIOOps; |
| |
| if(!DoMapToUser(psOffsetStruct->psLinuxMemArea, ps_vma, 0)) |
| { |
| iRetVal = -EAGAIN; |
| goto unlock_and_return; |
| } |
| |
| PVR_ASSERT(psOffsetStruct->ui32UserVAddr == 0) |
| |
| psOffsetStruct->ui32UserVAddr = ps_vma->vm_start; |
| |
| |
| if(psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate) |
| { |
| IMG_UINT32 ui32ByteOffset, ui32DummyByteSize; |
| |
| DetermineUsersSizeAndByteOffset(psOffsetStruct->psLinuxMemArea, |
| &ui32DummyByteSize, |
| &ui32ByteOffset); |
| |
| pvBase = (IMG_VOID *)ps_vma->vm_start + ui32ByteOffset; |
| psFlushMemArea = psOffsetStruct->psLinuxMemArea; |
| |
| psOffsetStruct->psLinuxMemArea->bNeedsCacheInvalidate = IMG_FALSE; |
| } |
| |
| |
| MMapVOpenNoLock(ps_vma); |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Mapped area at offset 0x%08lx\n", |
| __FUNCTION__, ps_vma->vm_pgoff)); |
| |
| unlock_and_return: |
| if (iRetVal != 0 && psOffsetStruct != IMG_NULL) |
| { |
| DestroyOffsetStruct(psOffsetStruct); |
| } |
| |
| LinuxUnLockMutex(&g_sMMapMutex); |
| |
| if(psFlushMemArea) |
| { |
| OSInvalidateCPUCacheRangeKM(psFlushMemArea, pvBase, |
| psFlushMemArea->ui32ByteSize); |
| } |
| |
| return iRetVal; |
| } |
| |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| |
| static void ProcSeqStartstopMMapRegistations(struct seq_file *sfile,IMG_BOOL start) |
| { |
| if(start) |
| { |
| LinuxLockMutex(&g_sMMapMutex); |
| } |
| else |
| { |
| LinuxUnLockMutex(&g_sMMapMutex); |
| } |
| } |
| |
| |
| static void* ProcSeqOff2ElementMMapRegistrations(struct seq_file *sfile, loff_t off) |
| { |
| LinuxMemArea *psLinuxMemArea; |
| if(!off) |
| { |
| return PVR_PROC_SEQ_START_TOKEN; |
| } |
| |
| list_for_each_entry(psLinuxMemArea, &g_sMMapAreaList, sMMapItem) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct; |
| |
| list_for_each_entry(psOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) |
| { |
| off--; |
| if (off == 0) |
| { |
| PVR_ASSERT(psOffsetStruct->psLinuxMemArea == psLinuxMemArea); |
| return (void*)psOffsetStruct; |
| } |
| } |
| } |
| return (void*)0; |
| } |
| |
| static void* ProcSeqNextMMapRegistrations(struct seq_file *sfile,void* el,loff_t off) |
| { |
| return ProcSeqOff2ElementMMapRegistrations(sfile,off); |
| } |
| |
| |
| static void ProcSeqShowMMapRegistrations(struct seq_file *sfile, void *el) |
| { |
| KV_OFFSET_STRUCT *psOffsetStruct = (KV_OFFSET_STRUCT*)el; |
| LinuxMemArea *psLinuxMemArea; |
| IMG_UINT32 ui32RealByteSize; |
| IMG_UINT32 ui32ByteOffset; |
| |
| if(el == PVR_PROC_SEQ_START_TOKEN) |
| { |
| seq_printf( sfile, |
| #if !defined(DEBUG_LINUX_XML_PROC_FILES) |
| "Allocations registered for mmap: %u\n" |
| "In total these areas correspond to %u bytes\n" |
| "psLinuxMemArea " |
| "UserVAddr " |
| "KernelVAddr " |
| "CpuPAddr " |
| "MMapOffset " |
| "ByteLength " |
| "LinuxMemType " |
| "Pid Name Flags\n", |
| #else |
| "<mmap_header>\n" |
| "\t<count>%u</count>\n" |
| "\t<bytes>%u</bytes>\n" |
| "</mmap_header>\n", |
| #endif |
| g_ui32RegisteredAreas, |
| g_ui32TotalByteSize |
| ); |
| return; |
| } |
| |
| psLinuxMemArea = psOffsetStruct->psLinuxMemArea; |
| |
| DetermineUsersSizeAndByteOffset(psLinuxMemArea, |
| &ui32RealByteSize, |
| &ui32ByteOffset); |
| |
| seq_printf( sfile, |
| #if !defined(DEBUG_LINUX_XML_PROC_FILES) |
| "%-8p %08x %-8p %08x %08x %-8d %-24s %-5u %-8s %08x(%s)\n", |
| #else |
| "<mmap_record>\n" |
| "\t<pointer>%-8p</pointer>\n" |
| "\t<user_virtual>%-8x</user_virtual>\n" |
| "\t<kernel_virtual>%-8p</kernel_virtual>\n" |
| "\t<cpu_physical>%08x</cpu_physical>\n" |
| "\t<mmap_offset>%08x</mmap_offset>\n" |
| "\t<bytes>%-8d</bytes>\n" |
| "\t<linux_mem_area_type>%-24s</linux_mem_area_type>\n" |
| "\t<pid>%-5u</pid>\n" |
| "\t<name>%-8s</name>\n" |
| "\t<flags>%08x</flags>\n" |
| "\t<flags_string>%s</flags_string>\n" |
| "</mmap_record>\n", |
| #endif |
| psLinuxMemArea, |
| psOffsetStruct->ui32UserVAddr + ui32ByteOffset, |
| LinuxMemAreaToCpuVAddr(psLinuxMemArea), |
| LinuxMemAreaToCpuPAddr(psLinuxMemArea,0).uiAddr, |
| psOffsetStruct->ui32MMapOffset, |
| psLinuxMemArea->ui32ByteSize, |
| LinuxMemAreaTypeToString(psLinuxMemArea->eAreaType), |
| psOffsetStruct->ui32PID, |
| psOffsetStruct->pszName, |
| psLinuxMemArea->ui32AreaFlags, |
| HAPFlagsToString(psLinuxMemArea->ui32AreaFlags)); |
| } |
| |
| #endif |
| |
| |
| PVRSRV_ERROR |
| PVRMMapRegisterArea(LinuxMemArea *psLinuxMemArea) |
| { |
| PVRSRV_ERROR eError; |
| #if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) |
| const IMG_CHAR *pszName = LinuxMemAreaTypeToString(LinuxMemAreaRootType(psLinuxMemArea)); |
| #endif |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| #if defined(DEBUG) || defined(DEBUG_LINUX_MMAP_AREAS) |
| PVR_DPF((PVR_DBG_MESSAGE, |
| "%s(%s, psLinuxMemArea 0x%p, ui32AllocFlags 0x%8x)", |
| __FUNCTION__, pszName, psLinuxMemArea, psLinuxMemArea->ui32AreaFlags)); |
| #endif |
| |
| PVR_ASSERT(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC || LinuxMemAreaRoot(psLinuxMemArea)->eAreaType != LINUX_MEM_AREA_SUB_ALLOC); |
| |
| |
| if(psLinuxMemArea->bMMapRegistered) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: psLinuxMemArea 0x%p is already registered", |
| __FUNCTION__, psLinuxMemArea)); |
| eError = PVRSRV_ERROR_INVALID_PARAMS; |
| goto exit_unlock; |
| } |
| |
| list_add_tail(&psLinuxMemArea->sMMapItem, &g_sMMapAreaList); |
| |
| psLinuxMemArea->bMMapRegistered = IMG_TRUE; |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| g_ui32RegisteredAreas++; |
| |
| if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) |
| { |
| g_ui32TotalByteSize += psLinuxMemArea->ui32ByteSize; |
| } |
| #endif |
| |
| eError = PVRSRV_OK; |
| |
| exit_unlock: |
| LinuxUnLockMutex(&g_sMMapMutex); |
| |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR |
| PVRMMapRemoveRegisteredArea(LinuxMemArea *psLinuxMemArea) |
| { |
| PVRSRV_ERROR eError; |
| PKV_OFFSET_STRUCT psOffsetStruct, psTmpOffsetStruct; |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| PVR_ASSERT(psLinuxMemArea->bMMapRegistered); |
| |
| list_for_each_entry_safe(psOffsetStruct, psTmpOffsetStruct, &psLinuxMemArea->sMMapOffsetStructList, sAreaItem) |
| { |
| if (psOffsetStruct->ui32Mapped != 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: psOffsetStruct 0x%p for memory area 0x0x%p is still mapped; psOffsetStruct->ui32Mapped %u", __FUNCTION__, psOffsetStruct, psLinuxMemArea, psOffsetStruct->ui32Mapped)); |
| dump_stack(); |
| PVRSRVDumpRefCountCCB(); |
| eError = PVRSRV_ERROR_STILL_MAPPED; |
| goto exit_unlock; |
| } |
| else |
| { |
| |
| PVR_DPF((PVR_DBG_WARNING, "%s: psOffsetStruct 0x%p was never mapped", __FUNCTION__, psOffsetStruct)); |
| } |
| |
| PVR_ASSERT((psOffsetStruct->ui32Mapped == 0) && psOffsetStruct->bOnMMapList); |
| |
| DestroyOffsetStruct(psOffsetStruct); |
| } |
| |
| list_del(&psLinuxMemArea->sMMapItem); |
| |
| psLinuxMemArea->bMMapRegistered = IMG_FALSE; |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| g_ui32RegisteredAreas--; |
| if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) |
| { |
| g_ui32TotalByteSize -= psLinuxMemArea->ui32ByteSize; |
| } |
| #endif |
| |
| eError = PVRSRV_OK; |
| |
| exit_unlock: |
| LinuxUnLockMutex(&g_sMMapMutex); |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR |
| LinuxMMapPerProcessConnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc) |
| { |
| PVR_UNREFERENCED_PARAMETER(psEnvPerProc); |
| |
| return PVRSRV_OK; |
| } |
| |
| IMG_VOID |
| LinuxMMapPerProcessDisconnect(PVRSRV_ENV_PER_PROCESS_DATA *psEnvPerProc) |
| { |
| PKV_OFFSET_STRUCT psOffsetStruct, psTmpOffsetStruct; |
| IMG_BOOL bWarn = IMG_FALSE; |
| IMG_UINT32 ui32PID = OSGetCurrentProcessIDKM(); |
| |
| PVR_UNREFERENCED_PARAMETER(psEnvPerProc); |
| |
| LinuxLockMutex(&g_sMMapMutex); |
| |
| list_for_each_entry_safe(psOffsetStruct, psTmpOffsetStruct, &g_sMMapOffsetStructList, sMMapItem) |
| { |
| if (psOffsetStruct->ui32PID == ui32PID) |
| { |
| if (!bWarn) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "%s: process has unmapped offset structures. Removing them", __FUNCTION__)); |
| bWarn = IMG_TRUE; |
| } |
| PVR_ASSERT(psOffsetStruct->ui32Mapped == 0); |
| PVR_ASSERT(psOffsetStruct->bOnMMapList); |
| |
| DestroyOffsetStruct(psOffsetStruct); |
| } |
| } |
| |
| LinuxUnLockMutex(&g_sMMapMutex); |
| } |
| |
| |
| PVRSRV_ERROR LinuxMMapPerProcessHandleOptions(PVRSRV_HANDLE_BASE *psHandleBase) |
| { |
| PVRSRV_ERROR eError; |
| |
| eError = PVRSRVSetMaxHandle(psHandleBase, MAX_MMAP_HANDLE); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: failed to set handle limit (%d)", __FUNCTION__, eError)); |
| return eError; |
| } |
| |
| return eError; |
| } |
| |
| |
| IMG_VOID |
| PVRMMapInit(IMG_VOID) |
| { |
| LinuxInitMutex(&g_sMMapMutex); |
| |
| g_psMemmapCache = KMemCacheCreateWrapper("img-mmap", sizeof(KV_OFFSET_STRUCT), 0, 0); |
| if (!g_psMemmapCache) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__)); |
| goto error; |
| } |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| g_ProcMMap = CreateProcReadEntrySeq("mmap", NULL, |
| ProcSeqNextMMapRegistrations, |
| ProcSeqShowMMapRegistrations, |
| ProcSeqOff2ElementMMapRegistrations, |
| ProcSeqStartstopMMapRegistations |
| ); |
| #endif |
| return; |
| |
| error: |
| PVRMMapCleanup(); |
| return; |
| } |
| |
| |
| IMG_VOID |
| PVRMMapCleanup(IMG_VOID) |
| { |
| PVRSRV_ERROR eError; |
| |
| if (!list_empty(&g_sMMapAreaList)) |
| { |
| LinuxMemArea *psLinuxMemArea, *psTmpMemArea; |
| |
| PVR_DPF((PVR_DBG_ERROR, "%s: Memory areas are still registered with MMap", __FUNCTION__)); |
| |
| PVR_TRACE(("%s: Unregistering memory areas", __FUNCTION__)); |
| list_for_each_entry_safe(psLinuxMemArea, psTmpMemArea, &g_sMMapAreaList, sMMapItem) |
| { |
| eError = PVRMMapRemoveRegisteredArea(psLinuxMemArea); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: PVRMMapRemoveRegisteredArea failed (%d)", __FUNCTION__, eError)); |
| } |
| PVR_ASSERT(eError == PVRSRV_OK); |
| |
| LinuxMemAreaDeepFree(psLinuxMemArea); |
| } |
| } |
| PVR_ASSERT(list_empty((&g_sMMapAreaList))); |
| |
| #if defined(DEBUG_LINUX_MMAP_AREAS) |
| RemoveProcEntrySeq(g_ProcMMap); |
| #endif |
| |
| if(g_psMemmapCache) |
| { |
| KMemCacheDestroyWrapper(g_psMemmapCache); |
| g_psMemmapCache = NULL; |
| } |
| } |