diff -r 000000000000 -r a41df078684a kernel/eka/memmodel/epoc/flexible/mshbuf.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/flexible/mshbuf.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1908 @@ +// Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32/memmodel/epoc/flexible/mshbuf.cpp +// Shareable Data Buffers + +#include +#include "mmu/mm.h" +#include "mmboot.h" +#include + +_LIT(KLitDMemModelAlignedShPool,"DMMAlignedShPool"); // Must be no more than 16 characters! + +struct TWait + { + void Link(TWait*& aList) + { + iSem.SetOwner(NULL); + iNext = aList; + aList = this; + }; + void Wait() + { + NKern::FSWait(&iSem); + } + NFastSemaphore iSem; + TWait* iNext; + + static void SignalAll(TWait* aList) + { + while (aList) + { + TWait* next = aList->iNext; + NKern::FSSignal(&aList->iSem); + aList = next; + } + } + }; + + +class DShBufMapping : public DBase + { +public: + SDblQueLink iObjLink; + DMemoryMapping* iMapping; + TInt iOsAsid; + TWait* iTransitions; // Mapping and Unmapping operations + TBool iTransitioning; + }; + + +DMemModelShPool::DMemModelShPool() : DShPool() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelShPool::DMemModelShPool")); + } + +DMemModelShPool::~DMemModelShPool() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelShPool::~DMemModelShPool")); + } + +void DMemModelShPool::DestroyClientResources(DProcess* aProcess) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelShPool::DestroyClientResources")); + + TInt r = DestroyAllMappingsAndReservedHandles(aProcess); + __NK_ASSERT_DEBUG((r == KErrNone) || (r == KErrDied)); + (void)r; // Silence warnings + } + +DMemModelAlignedShBuf::DMemModelAlignedShBuf(DShPool* aPool) : DShBuf(aPool) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::DMemModelAlignedShBuf()")); + } + +TInt DMemModelAlignedShBuf::Construct() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::Construct()")); + + TInt r = KErrNone; + + r = DShBuf::Construct(); + + if (r == KErrNone) + r = Create(); + + return r; + } + +TInt DMemModelAlignedShBuf::Close(TAny* aPtr) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::Close(0x%08x)", aPtr)); + + if (aPtr) + { + DProcess* pP = reinterpret_cast(aPtr); + UnMap(pP); + iPool->CloseClient(pP); + } + + return DShBuf::Close(aPtr); + } + +TInt DMemModelAlignedShBuf::AddToProcess(DProcess* aProcess, TUint aAttr) + { + __KTRACE_OPT(KMMU,Kern::Printf("Adding DMemModelShBuf %O to process %O",this,aProcess)); + TInt r; + TLinAddr base; + TUint flags; + + r = iPool->OpenClient(aProcess, flags); + + if (r == KErrNone) + { + if ((flags & EShPoolAutoMapBuf) && ((aAttr & EShPoolNoMapBuf) == 0)) + { + // note we use the client's pool flags and not the buffer attributes + r = Map(flags, aProcess, base); + + if (aProcess == K::TheKernelProcess) + iRelAddress = static_cast(base); + } + } + + return r; + } + +TInt DMemModelAlignedShBuf::Create() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::Create()")); + TInt r = KErrNone; + + // calculate memory type... + TMemoryObjectType memoryType = EMemoryObjectUnpaged; + + TMemoryAttributes attr = EMemoryAttributeStandard; + + // calculate memory flags... + TMemoryCreateFlags flags = static_cast((EMemoryCreateDefault|EMemoryCreateUseCustomWipeByte|(0xAA<iBufGap), flags, attr); + + if(r!=KErrNone) + return r; + + if (iPool->iPoolFlags & EShPoolContiguous) + { + TPhysAddr paddr; + r = MM::MemoryAllocContiguous(iMemoryObject, 0, MM::RoundToPageCount(iPool->iBufSize), 0, paddr); + } + else + { + r = MM::MemoryAlloc(iMemoryObject, 0, MM::RoundToPageCount(iPool->iBufSize)); + } + + return r; + } + +DMemModelAlignedShBuf::~DMemModelAlignedShBuf() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::~DMemModelAlignedShBuf()")); + + __NK_ASSERT_DEBUG(iMappings.IsEmpty()); + + MM::MemoryDestroy(iMemoryObject); + } + +TInt DMemModelAlignedShBuf::Map(TUint aMapAttr, DProcess* aProcess, TLinAddr& aBase) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::Map()")); + TInt r = KErrNone; + + DShBufMapping* m = NULL; + DMemoryMapping* mapping = NULL; + DMemModelProcess* pP = reinterpret_cast(aProcess); + + TBool write = (TBool)EFalse; + + // User = ETrue, ReadOnlyWrite = ETrue, Execute = EFalse + if (aMapAttr & EShPoolWriteable) + write = (TBool)ETrue; + + TMappingPermissions perm = MM::MappingPermissions(pP!=K::TheKernelProcess, write, (TBool)EFalse); + TWait wait; + + for(;;) + { + iPool->LockPool(); + r = FindMapping(m, pP); + + if (r != KErrNone) + break; + + if (m->iTransitioning) + { + wait.Link(m->iTransitions); + iPool->UnlockPool(); + wait.Wait(); + } + else + { + iPool->UnlockPool(); + return KErrAlreadyExists; + } + } + + DMemModelAlignedShPoolClient* client = reinterpret_cast(iPool->iClientMap->Find(reinterpret_cast(aProcess))); + + __NK_ASSERT_DEBUG(client); + + DMemModelAlignedShPool* pool = reinterpret_cast(iPool); + + __NK_ASSERT_DEBUG(m == NULL); + r = pool->GetFreeMapping(m, client); + + if (r == KErrNone) + { + iMappings.AddHead(&m->iObjLink); + m->iTransitioning = ETrue; + + mapping = m->iMapping; + iPool->UnlockPool(); // have to release fast lock for MappingMap + + r = MM::MappingMap(mapping, perm, iMemoryObject, 0, MM::RoundToPageCount(pool->iBufSize)); + + iPool->LockPool(); + + TWait* list = m->iTransitions; + m->iTransitions = NULL; + + if (r != KErrNone) + pool->ReleaseMapping(m, client); + else + aBase = MM::MappingBase(mapping); + + m->iTransitioning = EFalse; + iPool->UnlockPool(); + + TWait::SignalAll(list); + } + else + iPool->UnlockPool(); + + return r; + } + +TInt DMemModelAlignedShBuf::FindMapping(DShBufMapping*& aMapping, DMemModelProcess* aProcess) + { + // Must be in critical section so we don't leak os asid references. + __ASSERT_CRITICAL; + __NK_ASSERT_DEBUG(iPool->iLock.HeldByCurrentThread()); + + TInt r = KErrNotFound; + aMapping = NULL; + + // Open a reference on aProcess's os asid so that it can't be freed and + // reused while searching. + TInt osAsid = aProcess->TryOpenOsAsid(); + if (osAsid < 0) + {// aProcess has died and freed its os asid. + return KErrDied; + } + + SDblQueLink* pLink = iMappings.First(); + SDblQueLink* end = reinterpret_cast(&iMappings); + DShBufMapping* m = NULL; + + while (pLink != end) + { + m = _LOFF(pLink, DShBufMapping, iObjLink); + + if (m->iOsAsid == osAsid) + { + aMapping = m; + r = KErrNone; + break; + } + pLink = pLink->iNext; + } + + // Close the reference on the os asid as if we have a mapping then its lifetime will + // determine whether the process still owns an os asid. + aProcess->CloseOsAsid(); + return r; + } + +TInt DMemModelAlignedShBuf::UnMap(DProcess* aProcess) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::UnMap()")); + + TInt r = KErrNone; + + DMemModelProcess* pP = reinterpret_cast(aProcess); + + DShBufMapping* m = NULL; + TWait wait; + + for(;;) + { + iPool->LockPool(); + r = FindMapping(m, pP); + + if (r != KErrNone) + { + iPool->UnlockPool(); + return KErrNotFound; + } + + if (m->iTransitioning) + { + wait.Link(m->iTransitions); + iPool->UnlockPool(); + wait.Wait(); + } + else + { + break; + } + } + + m->iTransitioning = ETrue; + iPool->UnlockPool(); + + MM::MappingUnmap(m->iMapping); + + iPool->LockPool(); + DMemModelAlignedShPoolClient* client = reinterpret_cast(iPool->iClientMap->Find(reinterpret_cast(aProcess))); + + __NK_ASSERT_DEBUG(client); + + TWait* list = m->iTransitions; + m->iTransitions = NULL; + m->iObjLink.Deque(); + m->iTransitioning = EFalse; + + DMemModelAlignedShPool* pool = reinterpret_cast(iPool); + pool->ReleaseMapping(m, client); + + if (aProcess == K::TheKernelProcess) + iRelAddress = NULL; + + iPool->UnlockPool(); + + wait.SignalAll(list); + return KErrNone; + } + +TUint8* DMemModelAlignedShBuf::Base(DProcess* aProcess) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShBuf::Base()")); + DMemModelProcess* pP = reinterpret_cast(aProcess); + + DShBufMapping* mapping = NULL; + iPool->LockPool(); + TInt r = FindMapping(mapping, pP); + TUint8* base = NULL; + + if (r == KErrNone) + base = reinterpret_cast(MM::MappingBase(mapping->iMapping)); + iPool->UnlockPool(); + + return base; + } + +TUint8* DMemModelAlignedShBuf::Base() + { + return reinterpret_cast(iRelAddress); + } + +TInt DMemModelAlignedShBuf::Pin(TPhysicalPinObject* aPinObject, TBool aReadOnly, TPhysAddr& aAddress, TPhysAddr* aPages, TUint32& aMapAttr, TUint& aColour) + { + CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"DMemModelAlignedShBuf::Pin"); + + TInt r = MM::PinPhysicalMemory(iMemoryObject, (DPhysicalPinMapping*)aPinObject, 0, + MM::RoundToPageCount(Size()), + aReadOnly, aAddress, aPages, aMapAttr, aColour); + + return r; + } + +TInt DMemModelAlignedShPool::GetFreeMapping(DShBufMapping*& aMapping, DMemModelAlignedShPoolClient* aClient) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::GetFreeMapping()")); + __NK_ASSERT_DEBUG(iLock.HeldByCurrentThread()); + + TInt r = KErrNotFound; + aMapping = NULL; + + if (aClient) + { + if (!aClient->iMappingFreeList.IsEmpty()) + { + aMapping = _LOFF(aClient->iMappingFreeList.GetFirst(), DShBufMapping, iObjLink); + r = KErrNone; + } + else + { + r = KErrNoMemory; + } + } + + __KTRACE_OPT(KMMU2, Kern::Printf("DMemModelAlignedShPool::GetFreeMapping(0x%08x, 0x%08x) returns %d", aMapping, aClient, r)); + return r; + } + +TInt DMemModelAlignedShPool::ReleaseMapping(DShBufMapping*& aMapping, DMemModelAlignedShPoolClient* aClient) + { + __KTRACE_OPT(KMMU2, Kern::Printf("DMemModelAlignedShPool::ReleaseMapping(0x%08x,0x%08x)",aMapping,aClient)); + __NK_ASSERT_DEBUG(iLock.HeldByCurrentThread()); + + TInt r = KErrNone; + + if (aClient) + { + aClient->iMappingFreeList.AddHead(&aMapping->iObjLink); + aMapping = NULL; + } + else + { + // pool has probably been closed delete mapping + r = KErrNotFound; + __KTRACE_OPT(KMMU2, Kern::Printf("DMemModelAlignedShPool::ReleaseMapping delete 0x%08x",aMapping)); + UnlockPool(); // have to release fast lock for MappingDestroy + MM::MappingDestroy(aMapping->iMapping); + delete aMapping; + aMapping = NULL; + LockPool(); + } + + return r; + } + +TInt DMemModelAlignedShPool::SetBufferWindow(DProcess* aProcess, TInt aWindowSize) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::SetBufferWindow()")); + + // Create and construct mappings but do not map + // also allocate reserved handles + TInt r = KErrNone; + TUint noOfBuffers = aWindowSize; + + if (aWindowSize > static_cast(iMaxBuffers)) + return KErrArgument; + + Kern::MutexWait(*iProcessLock); + + LockPool(); + DMemModelAlignedShPoolClient* client = reinterpret_cast(iClientMap->Find(reinterpret_cast(aProcess))); + UnlockPool(); + + if (client) + { + if (client->iWindowSize != 0) + { + Kern::MutexSignal(*iProcessLock); + return KErrAlreadyExists; + } + + if (aWindowSize < 0) + { + noOfBuffers = iTotalBuffers; + } + + DMemModelProcess* pP = reinterpret_cast(aProcess); + r = CreateMappings(client, noOfBuffers, pP); + + if (r == KErrNone) + { + client->iWindowSize = aWindowSize; + } + else + { + DestroyMappings(client, noOfBuffers); + } + } + else + { + r = KErrNotFound; + } + + Kern::MutexSignal(*iProcessLock); + + return r; + } + +TInt DMemModelAlignedShPool::MappingNew(DShBufMapping*& aMapping, DMemModelProcess* aProcess) + { + // Must be in critical section so we don't leak os asid references. + __ASSERT_CRITICAL; + + TMappingCreateFlags flags=EMappingCreateDefault; + + FlagSet(flags, EMappingCreateReserveAllResources); + + // Open a reference to aProcess's os so it isn't freed and reused while + // we're creating this mapping. + TInt osAsid = aProcess->TryOpenOsAsid(); + if (osAsid < 0) + {// The process has freed its os asid so can't create a new mapping. + return KErrDied; + } + + DMemoryMapping* mapping = NULL; + DShBufMapping* m = NULL; + TInt r = MM::MappingNew(mapping, MM::RoundToPageCount(iBufGap), osAsid, flags); + + if (r == KErrNone) + { + m = new DShBufMapping; + + if (m) + { + m->iMapping = mapping; + m->iOsAsid = osAsid; + } + else + { + MM::MappingDestroy(mapping); + r = KErrNoMemory; + } + } + + // Close the reference on the os asid as while aMapping is valid then the + // os asid must be also. + aProcess->CloseOsAsid(); + + aMapping = m; + __KTRACE_OPT(KMMU2, Kern::Printf("DMemModelAlignedShPool::MappingNew returns 0x%08x,%d",aMapping,r)); + return r; + } + +TInt DMemModelAlignedShPool::AddToProcess(DProcess* aProcess, TUint aAttr) + { + __KTRACE_OPT(KMMU,Kern::Printf("Adding DMemModelAlignedShPool %O to process %O",this,aProcess)); + TInt r = KErrNone; + + Kern::MutexWait(*iProcessLock); + + LockPool(); + DShPoolClient* client = reinterpret_cast(iClientMap->Find(reinterpret_cast(aProcess))); + UnlockPool(); + + if (!client) + { + client = new DMemModelAlignedShPoolClient; + if (client) + { + client->iFlags = aAttr; + r = iClientMap->Add(reinterpret_cast(aProcess), client); + + if (r == KErrNone) + { + if (aProcess != K::TheKernelProcess) + { + r = aProcess->iHandles.Reserve(iTotalBuffers); + + if (r != KErrNone) + { + iClientMap->Remove(reinterpret_cast(aProcess)); + } + } + } + if (r != KErrNone) + { + delete client; + r = KErrNoMemory; + } + } + else + { + r = KErrNoMemory; + } + } + else + { + LockPool(); + client->iAccessCount++; + UnlockPool(); + } + + Kern::MutexSignal(*iProcessLock); + + return r; + } + +DMemModelAlignedShPool::DMemModelAlignedShPool() : DMemModelShPool() + + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::DMemModelAlignedShPool")); + } + +void DMemModelAlignedShPool::Free(DShBuf* aBuf) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::Free (aBuf = 0x%08x)", aBuf)); + + LockPool(); +#ifdef _DEBUG + // Remove from allocated list + aBuf->iObjLink.Deque(); +#endif + + DMemModelAlignedShBuf* buf = reinterpret_cast(aBuf); + + if (MM::MemoryIsNotMapped(buf->iMemoryObject)) + { + UnlockPool(); // have to release fast mutex + MM::MemoryWipe(buf->iMemoryObject); + LockPool(); + + // we want to put the initial buffers at the head of the free list + // and the grown buffers at the tail as this makes shrinking more efficient + if (aBuf >= iInitialBuffersArray && aBuf < (iInitialBuffersArray + iInitialBuffers)) + { + iFreeList.AddHead(&aBuf->iObjLink); + } + else + { + iFreeList.Add(&aBuf->iObjLink); + } + ++iFreeBuffers; +#ifdef _DEBUG + --iAllocatedBuffers; +#endif + } + else + { + iPendingList.Add(&aBuf->iObjLink); + } + + iPoolFlags &= ~EShPoolSuppressShrink; // Allow shrinking again, if it was blocked + UnlockPool(); + + // queue ManagementDfc which completes notifications as appropriate + if (HaveWorkToDo()) + KickManagementDfc(); + + DShPool::Close(NULL); // decrement pool reference count + } + +TInt DMemModelAlignedShPool::UpdateFreeList() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::UpdateFreeList")); + + LockPool(); + SDblQueLink* pLink = iPendingList.First(); + UnlockPool(); + + SDblQueLink* anchor = &iPendingList.iA; + + while (pLink != anchor) + { + DMemModelAlignedShBuf* buf = _LOFF(pLink, DMemModelAlignedShBuf, iObjLink); + LockPool(); + pLink = pLink->iNext; + UnlockPool(); + + if (MM::MemoryIsNotMapped(buf->iMemoryObject)) + { + LockPool(); + buf->iObjLink.Deque(); + UnlockPool(); + + MM::MemoryWipe(buf->iMemoryObject); + + LockPool(); + if (buf >= iInitialBuffersArray && buf < (iInitialBuffersArray + iInitialBuffers)) + { + iFreeList.AddHead(&buf->iObjLink); + } + else + { + iFreeList.Add(&buf->iObjLink); + } + ++iFreeBuffers; +#ifdef _DEBUG + --iAllocatedBuffers; +#endif + UnlockPool(); + } + } + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelAlignedShPool::~DMemModelAlignedShPool")); + } + +TInt DMemModelAlignedShPool::DoCreate(TShPoolCreateInfo& aInfo) + { + + TUint64 maxSize64 = static_cast(aInfo.iInfo.iMaxBufs) * static_cast(iBufGap); + + if (maxSize64 > static_cast(KMaxTInt) || maxSize64 <= static_cast(0)) + return KErrArgument; + + iMaxPages = MM::RoundToPageCount(static_cast(maxSize64)); + + return KErrNone; + } + +TInt DMemModelAlignedShPool::DestroyAllMappingsAndReservedHandles(DProcess* aProcess) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::DestroyAllMappingsAndReservedHandles(0x%08x)", aProcess)); + + TInt r = KErrNone; + Kern::MutexWait(*iProcessLock); + DMemModelAlignedShPoolClient* client = reinterpret_cast(iClientMap->Remove(reinterpret_cast(aProcess))); + + __NK_ASSERT_DEBUG(client); + __NK_ASSERT_DEBUG(client->iAccessCount == 0); + + DestroyMappings(client, KMaxTInt); + delete client; + + if (aProcess != K::TheKernelProcess) + { + // Remove reserved handles + r = aProcess->iHandles.Reserve(-iTotalBuffers); + } + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelAlignedShPool::DestroyMappings(0x%08x)", aClient)); + + TInt r = KErrNone; + TInt i = 0; + + DShBufMapping* m = NULL; + SDblQueLink* pLink = NULL; + + while (i < aNoOfMappings && !aClient->iMappingFreeList.IsEmpty()) + { + LockPool(); + pLink = aClient->iMappingFreeList.GetFirst(); + UnlockPool(); + + if (pLink == NULL) + break; + + m = _LOFF(pLink, DShBufMapping, iObjLink); + __KTRACE_OPT(KMMU2, Kern::Printf("DMemModelAlignedShPool::DestroyMappings delete 0x%08x",m)); + MM::MappingClose(m->iMapping); + delete m; + ++i; + } + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelAlignedShPool::CreateMappings")); + + __ASSERT_MUTEX(iProcessLock); + + TInt r = KErrNone; + + for (TInt i = 0; i < aNoOfMappings; ++i) + { + DShBufMapping* mapping; + r = MappingNew(mapping, aProcess); + if (r == KErrNone) + { + LockPool(); + aClient->iMappingFreeList.AddHead(&mapping->iObjLink); + UnlockPool(); + } + else + { + r = KErrNoMemory; + break; + } + } + + return r; + } + +TInt DMemModelAlignedShPool::UpdateMappingsAndReservedHandles(TInt aNoOfBuffers) + { + __KTRACE_OPT(KMMU2, Kern::Printf(">DMemModelAlignedShPool::UpdateMappingsAndReservedHandles(0x%08x)", aNoOfBuffers)); + + SMap::TIterator iter(*iClientMap); + SMap::TEntry* entry; + SMap::TEntry* lastEntry = NULL; + DMemModelProcess* pP; + DMemModelAlignedShPoolClient* client; + TInt result = KErrNone; + + Kern::MutexWait(*iProcessLock); + + // First handle the case of increasing allocation + if (aNoOfBuffers > 0) + while ((entry = iter.Next()) != lastEntry) + { + // Try to update handle reservation; skip if process is null or has gone away + client = (DMemModelAlignedShPoolClient*)(entry->iObj); + pP = (DMemModelProcess*)(entry->iKey); + if (!pP) + continue; + TInt r = pP->iHandles.Reserve(aNoOfBuffers); + if (r) + __KTRACE_OPT(KMMU2, Kern::Printf("?DMemModelAlignedShPool::UpdateMappingsAndReservedHandles(0x%08x) Reserve failed %d", aNoOfBuffers, r)); + if (r == KErrDied) + continue; + + if (r == KErrNone && client->iWindowSize <= 0) + { + // A positive window size means the number of mappings is fixed, so we don't need to reserve more. + // But here zero or negative means a variable number, so we need to create extra mappings now. + r = CreateMappings(client, aNoOfBuffers, pP); + if (r != KErrNone) + { + __KTRACE_OPT(KMMU2, Kern::Printf("?DMemModelAlignedShPool::UpdateMappingsAndReservedHandles(0x%08x) CreateMappings failed %d", aNoOfBuffers, r)); + pP->iHandles.Reserve(-aNoOfBuffers); // Creation failed, so release the handles reserved above + } + } + + if (r != KErrNone) + { + // Some problem; cleanup as best we can by falling into the loop below to undo what we've done + result = r; + iter.Reset(); + lastEntry = entry; + aNoOfBuffers = -aNoOfBuffers; + break; + } + } + + // Now handle the case of decreasing allocation; also used for recovery from errors, in which case + // this loop iterates only over the elements that were *successfully* processed by the loop above + if (aNoOfBuffers < 0) + while ((entry = iter.Next()) != lastEntry) + { + // Try to update handle reservation; skip if process is null or has gone away + client = (DMemModelAlignedShPoolClient*)(entry->iObj); + pP = (DMemModelProcess*)(entry->iKey); + if (!pP) + continue; + TInt r = pP->iHandles.Reserve(aNoOfBuffers); + if (r == KErrDied) + continue; + + if (r == KErrNone && client->iWindowSize <= 0) + r = DestroyMappings(client, -aNoOfBuffers); + // De-allocation by Reserve(-n) and/or DestroyMappings() should never fail + if (r != KErrNone) + Kern::PanicCurrentThread(KLitDMemModelAlignedShPool, r); + } + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU2, Kern::Printf("DMemModelAlignedShPool::DeleteInitialBuffers")); + + if (iInitialBuffersArray != NULL) + { + for (TUint i = 0; i < iInitialBuffers; i++) + { + iInitialBuffersArray[i].iObjLink.Deque(); // remove from free list + iInitialBuffersArray[i].Dec(); + iInitialBuffersArray[i].~DMemModelAlignedShBuf(); + } + } + + Kern::Free(iInitialBuffersArray); + iInitialBuffersArray = NULL; + + return KErrNone; + } + +TInt DMemModelAlignedShPool::Close(TAny* aPtr) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::Close(0x%08x)", aPtr)); + + if (aPtr) + { + DProcess* pP = reinterpret_cast(aPtr); + + CloseClient(pP); + } + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelAlignedShPool::CreateInitialBuffers")); + + iInitialBuffersArray = reinterpret_cast(Kern::Alloc(iInitialBuffers * sizeof(DMemModelAlignedShBuf))); + + if (iInitialBuffersArray == NULL) + return KErrNoMemory; + + for (TUint i = 0; i < iInitialBuffers; i++) + { + // always use kernel linear address in DShBuf + DMemModelAlignedShBuf *buf = new (&iInitialBuffersArray[i]) DMemModelAlignedShBuf(this); + TInt r = buf->Construct(); + + if (r == KErrNone) + { + iFreeList.Add(&buf->iObjLink); + } + else + { + iInitialBuffers = i; + return KErrNoMemory; + } + } + + iFreeBuffers = iInitialBuffers; + iTotalBuffers = iInitialBuffers; + return KErrNone; + } + + +TInt DMemModelAlignedShPool::GrowPool() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelAlignedShPool::GrowPool()")); + TInt r = KErrNone; + SDblQue temp; + + Kern::MutexWait(*iProcessLock); + + TUint32 headroom = iMaxBuffers - iTotalBuffers; + + // How many buffers to grow by? + TUint32 grow = mult_fx248(iTotalBuffers, iGrowByRatio); + if (grow == 0) // Handle round-to-zero + grow = 1; + if (grow > headroom) + grow = headroom; + + TUint i; + for (i = 0; i < grow; ++i) + { + DMemModelAlignedShBuf *buf = new DMemModelAlignedShBuf(this); + + if (buf == NULL) + { + r = KErrNoMemory; + break; + } + + TInt r = buf->Construct(); + + if (r != KErrNone) + { + buf->DObject::Close(NULL); + break; + } + + temp.Add(&buf->iObjLink); + } + + r = UpdateMappingsAndReservedHandles(i); + + if (r == KErrNone) + { + LockPool(); + iFreeList.MoveFrom(&temp); + iFreeBuffers += i; + iTotalBuffers += i; + UnlockPool(); + } + else + { + // couldn't create either the mappings or reserve handles so have no choice but to + // delete the buffers + SDblQueLink *pLink; + while ((pLink = temp.GetFirst()) != NULL) + { + DShBuf* buf = _LOFF(pLink, DShBuf, iObjLink); + buf->DObject::Close(NULL); + } + } + + CalculateGrowShrinkTriggers(); + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelAlignedShPool::ShrinkPool()")) + + Kern::MutexWait(*iProcessLock); + + TUint32 grownBy = iTotalBuffers - iInitialBuffers; + + // How many buffers to shrink by? + TUint32 shrink = mult_fx248(iTotalBuffers, iShrinkByRatio); + if (shrink == 0) // Handle round-to-zero + shrink = 1; + if (shrink > grownBy) + shrink = grownBy; + if (shrink > iFreeBuffers) + shrink = iFreeBuffers; + + // work backwards as the grown buffers should be at the back + TUint i; + for (i = 0; i < shrink; i++) + { + LockPool(); + + if (iFreeList.IsEmpty()) + { + UnlockPool(); + break; + } + + DShBuf* buf = _LOFF(iFreeList.Last(), DShBuf, iObjLink); + + // can't delete initial buffers + if (buf >= iInitialBuffersArray && buf < (iInitialBuffersArray + iInitialBuffers)) + { + UnlockPool(); + break; + } + + buf->iObjLink.Deque(); + --iFreeBuffers; + --iTotalBuffers; + UnlockPool(); + buf->DObject::Close(NULL); + } + + TInt r = UpdateMappingsAndReservedHandles(-i); + + // If we couldn't shrink the pool by this many buffers, wait until we Free() another + // buffer before trying to shrink again. + if (i < shrink) + iPoolFlags |= EShPoolSuppressShrink; + + CalculateGrowShrinkTriggers(); + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelAlignedShPool::Alloc (DShBuf)")); + + TInt r = KErrNoMemory; + aShBuf = NULL; + + LockPool(); + + if (!iFreeList.IsEmpty()) + { + aShBuf = _LOFF(iFreeList.GetFirst(), DShBuf, iObjLink); + +#ifdef _DEBUG + iAllocated.Add(&aShBuf->iObjLink); + iAllocatedBuffers++; +#endif + --iFreeBuffers; + Open(); // increment pool reference count + r = KErrNone; + } + + UnlockPool(); + + if (HaveWorkToDo()) + KickManagementDfc(); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShBuf::DMemModelNonAlignedShBuf()")); + } + +DMemModelNonAlignedShBuf::~DMemModelNonAlignedShBuf() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShBuf::~DMemModelNonAlignedShBuf()")); + } + +TInt DMemModelNonAlignedShBuf::Close(TAny* aPtr) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShBuf::Close(0x%08x)", aPtr)); + + if (aPtr) + { + DProcess* pP = reinterpret_cast(aPtr); + + // there no per buffer resources for kernel clients for non-aligned buffers + if (pP != K::TheKernelProcess) + iPool->CloseClient(pP); + } + + return DShBuf::Close(aPtr); + } + +TInt DMemModelNonAlignedShBuf::AddToProcess(DProcess* aProcess, TUint /* aAttr */) + { + __KTRACE_OPT(KMMU, Kern::Printf("Adding DMemModelShBuf %O to process %O", this, aProcess)); + TUint flags; + + return iPool->OpenClient(aProcess, flags); + } + + +TUint8* DMemModelNonAlignedShBuf::Base(DProcess* aProcess) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShBuf::Base(0x%x)", aProcess)); + + TUint8* base = reinterpret_cast(iPool)->Base(aProcess) + (TUint)iRelAddress; + + return base; + } + +TUint8* DMemModelNonAlignedShBuf::Base() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShBuf::Base()")); + + TUint8* base = reinterpret_cast(iPool)->Base(); + + return base ? base + iRelAddress : NULL; + } + +TInt DMemModelNonAlignedShBuf::Map(TUint /* aMapAttr */, DProcess* /* aProcess */, TLinAddr& /* aBase */) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShBuf::Map()")); + + return KErrNotSupported; + } + +TInt DMemModelNonAlignedShBuf::UnMap(DProcess* /* aProcess */) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShBuf::UnMap()")); + + return KErrNotSupported; + } + +TInt DMemModelNonAlignedShBuf::Pin(TPhysicalPinObject* aPinObject, TBool aReadOnly, TPhysAddr& aAddress, TPhysAddr* aPages, TUint32& aMapAttr, TUint& aColour) + { + CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL,"DMemModelNonAlignedShBuf::Pin"); + + DMemModelNonAlignedShPool* pool = reinterpret_cast(iPool); + + NKern::ThreadEnterCS(); + + TInt startPage = iRelAddress >> KPageShift; + TInt lastPage = MM::RoundToPageCount(iRelAddress + Size()); + + TInt pages = lastPage - startPage; + + if (!pages) pages++; + + TInt r = MM::PinPhysicalMemory(pool->iMemoryObject, (DPhysicalPinMapping*)aPinObject, + startPage, pages, aReadOnly, aAddress, aPages, aMapAttr, aColour); + + // adjust physical address to start of the buffer + if (r == KErrNone) + { + aAddress += (iRelAddress - (startPage << KPageShift)); + } + NKern::ThreadLeaveCS(); + return r; + } + +DMemModelNonAlignedShPool::DMemModelNonAlignedShPool() : DMemModelShPool() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::DMemModelNonAlignedShPool")); + } + +DMemModelNonAlignedShPool::~DMemModelNonAlignedShPool() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::~DMemModelNonAlignedShPool")); + + MM::MemoryDestroy(iMemoryObject); + + delete iPagesMap; + delete iBufMap; + } + +TInt DMemModelNonAlignedShPool::DoCreate(TShPoolCreateInfo& aInfo) + { + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::DoCreate(%d, %d, %d)", aInfo.iInfo.iMaxBufs, iBufGap, iBufSize)); + + TInt r; + TUint64 maxSize64 = static_cast(aInfo.iInfo.iMaxBufs) * static_cast(iBufGap); + + if (maxSize64 > static_cast(KMaxTInt) || maxSize64 <= static_cast(0)) + return KErrArgument; + + TInt maxPages = MM::RoundToPageCount(static_cast(maxSize64)); + + iBufMap = TBitMapAllocator::New(aInfo.iInfo.iMaxBufs, (TBool)ETrue); + if (iBufMap == NULL) + return KErrNoMemory; + + iPagesMap = TBitMapAllocator::New(maxPages, (TBool)ETrue); + if (iPagesMap == NULL) + return KErrNoMemory; + + // Memory attributes + TMemoryAttributes attr = EMemoryAttributeStandard; + + // Memory type + TMemoryObjectType memoryType = (iPoolFlags & EShPoolPhysicalMemoryPool) ? EMemoryObjectHardware : EMemoryObjectUnpaged; + + // Memory flags + TMemoryCreateFlags memoryFlags = EMemoryCreateDefault; // Don't leave previous contents of memory + + // Now create the memory object + r = MM::MemoryNew(iMemoryObject, memoryType, maxPages, memoryFlags, attr); + if (r != KErrNone) + return r; + + // Make sure we give the caller the number of buffers they were expecting + iCommittedPages = MM::RoundToPageCount(iInitialBuffers * iBufGap); + + if (iPoolFlags & EShPoolPhysicalMemoryPool) + { + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::DoCreate(iCommittedPages = 0x%08x, aInfo.iPhysAddr.iPhysAddrList = 0x%08x )", iCommittedPages, aInfo.iPhysAddr.iPhysAddrList)); + if (iPoolFlags & EShPoolContiguous) + { + r = MM::MemoryAddContiguous(iMemoryObject, 0, iCommittedPages, aInfo.iPhysAddr.iPhysAddr); + } + else + { + r = MM::MemoryAddPages(iMemoryObject, 0, iCommittedPages, aInfo.iPhysAddr.iPhysAddrList); + } + + iMaxPages = iCommittedPages; + } + else + { + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::DoCreate(iCommittedPages = %d, contig = %d)", iCommittedPages, iPoolFlags & EShPoolContiguous)); + + if (iPoolFlags & EShPoolContiguous) + { + TPhysAddr paddr; + r = MM::MemoryAllocContiguous(iMemoryObject, 0, iCommittedPages, 0, paddr); + } + else + { + r = MM::MemoryAlloc(iMemoryObject, 0, iCommittedPages); + } + + iMaxPages = maxPages; + } + + iPagesMap->Alloc(0, iCommittedPages); + + return r; + } + +TUint8* DMemModelNonAlignedShPool::Base(DProcess* aProcess) + { + TUint8 *base = 0; + + LockPool(); + DMemModelNonAlignedShPoolClient* client = reinterpret_cast(iClientMap->Find(reinterpret_cast(aProcess))); + + __NK_ASSERT_DEBUG(client); // ASSERT because pool must be already opened in the clients address space + __NK_ASSERT_DEBUG(client->iMapping); // ASSERT because non-aligned buffers are mapped by default in user space + + base = reinterpret_cast(MM::MappingBase(client->iMapping)); + + UnlockPool(); + + return base; + } + +TInt DMemModelNonAlignedShPool::CreateInitialBuffers() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::CreateInitialBuffers")); + + iInitialBuffersArray = reinterpret_cast(Kern::Alloc(iInitialBuffers * sizeof(DMemModelNonAlignedShBuf))); + + if (iInitialBuffersArray == NULL) + return KErrNoMemory; + + TLinAddr offset = 0; + for (TUint i = 0; i < iInitialBuffers; i++) + { + DMemModelNonAlignedShBuf *buf = new (&iInitialBuffersArray[i]) DMemModelNonAlignedShBuf(this, offset); + TInt r = buf->Construct(); + + if (r == KErrNone) + { + iFreeList.Add(&buf->iObjLink); + } + else + { + iInitialBuffers = i; + return KErrNoMemory; + } + + offset += iBufGap; + } + + iFreeBuffers = iInitialBuffers; + iTotalBuffers = iInitialBuffers; + iBufMap->Alloc(0, iInitialBuffers); + + return KErrNone; + } + +TInt DMemModelNonAlignedShPool::AddToProcess(DProcess* aProcess, TUint aAttr) + { + // Must be in critical section so we don't leak os asid references. + __ASSERT_CRITICAL; + __KTRACE_OPT(KMMU, Kern::Printf("Adding DMemModelShPool %O to process %O", this, aProcess)); + + DMemoryMapping* mapping = NULL; + + TBool write = (TBool)EFalse; + + // User = ETrue, ReadOnlyWrite = ETrue, Execute = EFalse + if (aAttr & EShPoolWriteable) + write = (TBool)ETrue; + + TMappingPermissions perm = MM::MappingPermissions(ETrue, // user + write, // writeable + EFalse); // execute + + TMappingCreateFlags mappingFlags = EMappingCreateDefault; + + DMemModelProcess* pP = reinterpret_cast(aProcess); + + Kern::MutexWait(*iProcessLock); + TInt r = KErrNone; + + LockPool(); + DMemModelNonAlignedShPoolClient* client = reinterpret_cast(iClientMap->Find(reinterpret_cast(aProcess))); + UnlockPool(); + + if (!client) + { + client = new DMemModelNonAlignedShPoolClient; + + if (client) + { + // map non aligned pools in userside processes by default + if (aAttr & EShPoolAutoMapBuf || pP != K::TheKernelProcess) + { + // Open a reference on the os asid so it doesn't get freed and reused. + TInt osAsid = pP->TryOpenOsAsid(); + if (osAsid < 0) + {// The process freed its os asid so can't create a new mapping. + r = KErrDied; + } + else + { + r = MM::MappingNew(mapping, iMemoryObject, perm, osAsid, mappingFlags); + // Close the reference as the mapping will be destroyed if the process dies. + pP->CloseOsAsid(); + } + + if ((r == KErrNone) && (pP == K::TheKernelProcess)) + { + iBaseAddress = MM::MappingBase(mapping); + } + } + + if (r == KErrNone) + { + client->iMapping = mapping; + client->iFlags = aAttr; + r = iClientMap->Add(reinterpret_cast(aProcess), client); + + if (r == KErrNone) + { + if (pP != K::TheKernelProcess) + { + r = aProcess->iHandles.Reserve(iTotalBuffers); + + if (r != KErrNone) + { + iClientMap->Remove(reinterpret_cast(aProcess)); + } + } + } + + if (r != KErrNone) + { + delete client; + MM::MappingDestroy(mapping); + } + } + else + { + delete client; + } + } + else + { + r = KErrNoMemory; + } + } + else + { + LockPool(); + client->iAccessCount++; + UnlockPool(); + } + + Kern::MutexSignal(*iProcessLock); + + return r; + } + +TInt DMemModelNonAlignedShPool::DeleteInitialBuffers() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::DeleteInitialBuffers")); + + if (iInitialBuffersArray != NULL) + { + for (TUint i = 0; i < iInitialBuffers; i++) + { + iInitialBuffersArray[i].iObjLink.Deque(); // remove from free list + iInitialBuffersArray[i].Dec(); + iInitialBuffersArray[i].~DMemModelNonAlignedShBuf(); + } + } + + Kern::Free(iInitialBuffersArray); + iInitialBuffersArray = NULL; + + return KErrNone; + } + +TInt DMemModelNonAlignedShPool::DestroyAllMappingsAndReservedHandles(DProcess* aProcess) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::DestroyAllMappingsAndReservedHandles(0x%08x)", aProcess)); + + TInt r = KErrNone; + Kern::MutexWait(*iProcessLock); + DMemModelNonAlignedShPoolClient* client = reinterpret_cast(iClientMap->Remove(reinterpret_cast(aProcess))); + + __NK_ASSERT_DEBUG(client); + __NK_ASSERT_DEBUG(client->iAccessCount == 0); + + if (client->iMapping) + { + MM::MappingDestroy(client->iMapping); + } + delete client; + + if (aProcess != K::TheKernelProcess) + { + // Remove reserved handles + r = aProcess->iHandles.Reserve(-(iTotalBuffers)); + } + else + { + iBaseAddress = 0; + } + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::Close(0x%08x)", aPtr)); + + if (aPtr) + { + DProcess* pP = reinterpret_cast(aPtr); + + CloseClient(pP); + } + + return DShPool::Close(aPtr); + } + +void DMemModelNonAlignedShPool::FreeBufferPages(TUint aOffset) + { + TLinAddr firstByte = aOffset; // offset of first byte in buffer + TLinAddr lastByte = firstByte+iBufGap-1; // offset of last byte in buffer + TUint firstPage = firstByte>>KPageShift; // index of first page containing part of the buffer + TUint lastPage = lastByte>>KPageShift; // index of last page containing part of the buffer + + TUint firstBuffer = (firstByte&~KPageMask)/iBufGap; // index of first buffer which lies in firstPage + TUint lastBuffer = (lastByte|KPageMask)/iBufGap; // index of last buffer which lies in lastPage + TUint thisBuffer = firstByte/iBufGap; // index of the buffer to be freed + + // Ensure lastBuffer is within bounds (there may be room in the last + // page for more buffers than we have allocated). + if (lastBuffer >= iMaxBuffers) + lastBuffer = iMaxBuffers-1; + + if(firstBuffer!=thisBuffer && iBufMap->NotFree(firstBuffer,thisBuffer-firstBuffer)) + { + // first page has other allocated buffers in it, + // so we can't free it and must move on to next one... + if (firstPage >= lastPage) + return; + ++firstPage; + } + + if(lastBuffer!=thisBuffer && iBufMap->NotFree(thisBuffer+1,lastBuffer-thisBuffer)) + { + // last page has other allocated buffers in it, + // so we can't free it and must step back to previous one... + if (lastPage <= firstPage) + return; + --lastPage; + } + + if(firstPage<=lastPage) + { + // we can free pages firstPage trough to lastPage... + TUint numPages = lastPage-firstPage+1; + iPagesMap->SelectiveFree(firstPage,numPages); + MM::MemoryLock(iMemoryObject); + MM::MemoryFree(iMemoryObject, firstPage, numPages); + MM::MemoryUnlock(iMemoryObject); + iCommittedPages -= numPages; + } + } + +TInt DMemModelNonAlignedShPool::GrowPool() + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::GrowPool()")); + + // Don't do anything with physical memory pools + if (iPoolFlags & EShPoolPhysicalMemoryPool) + return KErrNone; + + Kern::MutexWait(*iProcessLock); + + TUint32 headroom = iMaxBuffers - iTotalBuffers; + + // How many buffers to grow by? + TUint32 grow = mult_fx248(iTotalBuffers, iGrowByRatio); + if (grow == 0) // Handle round-to-zero + grow = 1; + if (grow > headroom) + grow = headroom; + + TInt r = KErrNone; + SDblQue temp; + + TUint i; + for (i = 0; i < grow; ++i) + { + TInt offset = iBufMap->Alloc(); + + if (offset < 0) + { + r = KErrNoMemory; + break; + } + + offset *= iBufGap; + + TInt lastPage = (offset + iBufSize - 1) >> KPageShift; + + // Allocate one page at a time. + for (TInt page = offset >> KPageShift; page <= lastPage; ++page) + { + // Is the page allocated? + if (iPagesMap->NotAllocated(page, 1)) + { + MM::MemoryLock(iMemoryObject); + r = MM::MemoryAlloc(iMemoryObject, page, 1); + MM::MemoryUnlock(iMemoryObject); + + if (r != KErrNone) + { + break; + } + + ++iCommittedPages; + iPagesMap->Alloc(page, 1); + } + } + + if (r != KErrNone) + { + iBufMap->Free(offset / iBufGap); + FreeBufferPages(offset); + break; + } + + DMemModelNonAlignedShBuf *buf = new DMemModelNonAlignedShBuf(this, offset); + + if (buf == NULL) + { + iBufMap->Free(offset / iBufGap); + FreeBufferPages(offset); + r = KErrNoMemory; + break; + } + + r = buf->Construct(); + + if (r != KErrNone) + { + iBufMap->Free(offset / iBufGap); + FreeBufferPages(offset); + buf->DObject::Close(NULL); + break; + } + + temp.Add(&buf->iObjLink); + } + + r = UpdateReservedHandles(i); + + if (r == KErrNone) + { + LockPool(); + iFreeList.MoveFrom(&temp); + iFreeBuffers += i; + iTotalBuffers += i; + UnlockPool(); + } + else + { + // couldn't reserve handles so have no choice but to + // delete the buffers + __KTRACE_OPT(KMMU, Kern::Printf("GrowPool failed with %d, deleting buffers", r)); + SDblQueLink *pLink; + while ((pLink = temp.GetFirst()) != NULL) + { + DShBuf* buf = _LOFF(pLink, DShBuf, iObjLink); + TLinAddr offset = buf->iRelAddress; + iBufMap->Free(offset / iBufGap); + FreeBufferPages(offset); + buf->DObject::Close(NULL); + } + __KTRACE_OPT(KMMU, Kern::Printf("Buffers deleted")); + } + + CalculateGrowShrinkTriggers(); + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::ShrinkPool()")); + + // Don't do anything with physical memory pools + if (iPoolFlags & EShPoolPhysicalMemoryPool) + return KErrNone; + + Kern::MutexWait(*iProcessLock); + + TUint32 grownBy = iTotalBuffers - iInitialBuffers; + + // How many buffers to shrink by? + TUint32 shrink = mult_fx248(iTotalBuffers, iShrinkByRatio); + if (shrink == 0) // Handle round-to-zero + shrink = 1; + if (shrink > grownBy) + shrink = grownBy; + if (shrink > iFreeBuffers) + shrink = iFreeBuffers; + + TUint i; + for (i = 0; i < shrink; ++i) + { + LockPool(); + + if (iFreeList.IsEmpty()) + { + UnlockPool(); + break; + } + + // work from the back of the queue + SDblQueLink *pLink = iFreeList.Last(); + + DShBuf* pBuf = _LOFF(pLink, DShBuf, iObjLink); + + if (pBuf >= iInitialBuffersArray && pBuf < (iInitialBuffersArray + iInitialBuffers)) + { + UnlockPool(); + break; + } + + --iFreeBuffers; + --iTotalBuffers; + pLink->Deque(); + UnlockPool(); + + TLinAddr offset = pBuf->iRelAddress; + iBufMap->Free(offset / iBufGap); + FreeBufferPages(offset); + + pBuf->DObject::Close(NULL); + } + + UpdateReservedHandles(-(TInt)i); + + // If we couldn't shrink the pool by this many buffers, wait until we Free() another + // buffer before trying to shrink again. + if (i < shrink) + iPoolFlags |= EShPoolSuppressShrink; + + CalculateGrowShrinkTriggers(); + + Kern::MutexSignal(*iProcessLock); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::UpdateFreeList")); + + SDblQue temp; + + LockPool(); + while(!iAltFreeList.IsEmpty()) + { + // sort a temporary list of 'n' object with the lowest index first + for (TInt n = 0; n < 8 && !iAltFreeList.IsEmpty(); ++n) + { + // bit of an assumption, lets assume that the lower indexes will be allocated and freed first + // and therefore will be nearer the front of the list + DShBuf* buf = _LOFF(iAltFreeList.GetFirst(), DShBuf, iObjLink); + + SDblQueLink* anchor = reinterpret_cast(&temp); + SDblQueLink* pLink = temp.Last(); + + while (ETrue) + { + // traverse the list starting at the back + if ((pLink != anchor) && (_LOFF(pLink, DShBuf, iObjLink)->iRelAddress > buf->iRelAddress)) + { + pLink = pLink->iPrev; + } + else + { + buf->iObjLink.InsertAfter(pLink); + break; + } + } + } + + // now merge with the free list + while(!temp.IsEmpty()) + { + if (iFreeList.IsEmpty()) + { + iFreeList.MoveFrom(&temp); + break; + } + + // working backwards with the highest index + DShBuf* buf = _LOFF(temp.Last(), DShBuf, iObjLink); + SDblQueLink* anchor = reinterpret_cast(&iFreeList); + SDblQueLink* pLink = iFreeList.Last(); + + while (!NKern::FMFlash(&iLock)) + { + if ((pLink != anchor) && (_LOFF(pLink, DShBuf, iObjLink)->iRelAddress > buf->iRelAddress)) + { + pLink = pLink->iPrev; + } + else + { + buf->iObjLink.Deque(); + buf->iObjLink.InsertAfter(pLink); + // next buffer + if (temp.IsEmpty()) + break; + buf = _LOFF(temp.Last(), DShBuf, iObjLink); + } + } + } + NKern::FMFlash(&iLock); + } + UnlockPool(); + + __KTRACE_OPT(KMMU, Kern::Printf("DMemModelNonAlignedShPool::Free (aBuf = 0x%08x, aBuf->Base() 0x%08x)", aBuf, aBuf->iRelAddress)); + + LockPool(); +#ifdef _DEBUG + // Remove from allocated list + aBuf->iObjLink.Deque(); +#endif + + // we want to put the initial buffers at the head of the free list + // and the grown buffers at the tail as this makes shrinking more efficient + if (aBuf >= iInitialBuffersArray && aBuf < (iInitialBuffersArray + iInitialBuffers)) + { + iFreeList.AddHead(&aBuf->iObjLink); + } + else + { + iAltFreeList.Add(&aBuf->iObjLink); + } + + ++iFreeBuffers; +#ifdef _DEBUG + --iAllocatedBuffers; +#endif + iPoolFlags &= ~EShPoolSuppressShrink; // Allow shrinking again, if it was blocked + UnlockPool(); + + // queue ManagementDfc which completes notifications as appropriate + if (HaveWorkToDo()) + KickManagementDfc(); + + DShPool::Close(NULL); // decrement pool reference count + } + +// Kernel side API +TInt DMemModelNonAlignedShPool::Alloc(DShBuf*& aShBuf) + { + __KTRACE_OPT(KMMU, Kern::Printf(">DMemModelNonAlignedShPool::Alloc (DShBuf)")); + + aShBuf = NULL; + + LockPool(); + + if (!iFreeList.IsEmpty()) + { + aShBuf = _LOFF(iFreeList.GetFirst(), DShBuf, iObjLink); +#ifdef _DEBUG + iAllocated.Add(&aShBuf->iObjLink); + iAllocatedBuffers++; +#endif + } + else + { + // try alternative free list + if (!iAltFreeList.IsEmpty()) + { + aShBuf = _LOFF(iAltFreeList.GetFirst(), DShBuf, iObjLink); +#ifdef _DEBUG + iAllocated.Add(&aShBuf->iObjLink); + iAllocatedBuffers++; +#endif + } + else + { + UnlockPool(); + KickManagementDfc(); // Try to grow + return KErrNoMemory; + } + } + + --iFreeBuffers; + Open(); // increment pool reference count + + UnlockPool(); + + if (HaveWorkToDo()) + KickManagementDfc(); + + __KTRACE_OPT(KMMU, Kern::Printf("