diff -r 000000000000 -r a41df078684a kerneltest/e32test/examples/defrag/d_defrag_ref.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/examples/defrag/d_defrag_ref.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,861 @@ +// Copyright (c) 2007-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: +// e32test\examples\defrag\d_defrag_ref.cpp +// Reference LDD for invoking defrag APIs. +// +// + +#include +#include "platform.h" +#include "nk_priv.h" +#include "d_defrag_ref.h" + +const TInt KMajorVersionNumber=0; +const TInt KMinorVersionNumber=1; +const TInt KBuildVersionNumber=1; + +#if 1 // Set true for tracing +#define TRACE(x) x +#else +#define TRACE(x) +#endif + +const TInt KDefragCompleteThreadPriority = 27; +const TInt KDefragRamThreadPriority = 1; +_LIT(KDefragCompleteThread,"DefragCompleteThread"); + +class DDefragChannel; + +/** + Clean up item responsible for ensuring all memory commmited to a chunk is + freed once the chunk is destroyed +*/ +class TChunkCleanup : public TDfc + { +public: + TChunkCleanup(DDefragChannel* aDevice, TPhysAddr* aBufAddrs, TUint aBufPages); + TChunkCleanup(DDefragChannel* aDevice, TPhysAddr aBufBase, TUint aBufBytes); + static void ChunkDestroyed(TChunkCleanup* aSelf); + void RemoveDevice(); + +private: + void DoChunkDestroyed(); + +private: + TPhysAddr* iBufAddrs; /**< Pointer to an array of the addresses of discontiguous buffer pages*/ + TPhysAddr iBufBase; /**< Physical base address of a physically contiguous the buffer*/ + TUint iBufSize; /**< The number of pages or bytes in the buffer depending if this is + discontiguous or contiguous buffer, repsectively*/ + TBool iBufContiguous; /**< ETrue when the memory to be freed is contiguous, EFalse otherwise*/ + DDefragChannel* iDevice; /**< The device to be informed when the chunk is destroyed */ + }; + + +/** + Reference defrag LDD factory. +*/ +class DDefragChannelFactory : public DLogicalDevice + { +public: + DDefragChannelFactory(); + ~DDefragChannelFactory(); + virtual TInt Install(); //overriding pure virtual + virtual void GetCaps(TDes8& aDes) const; //overriding pure virtual + virtual TInt Create(DLogicalChannelBase*& aChannel);//overriding pure virtual + + TDynamicDfcQue* iDfcQ; + }; + + +/** + Reference defrag logical channel. +*/ +class DDefragChannel : public DLogicalChannelBase + { +public: + DDefragChannel(TDfcQue* aDfcQ); + ~DDefragChannel(); + void ChunkDestroyed(); +protected: + virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer); + virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2); + + TInt DoAllocLowestZone(); + TInt DoClaimLowestZone(); + TInt DoChunkClose(); + TInt FindLowestPrefZone(); + + static void DefragCompleteDfc(TAny* aSelf); + void DefragComplete(); + +private: + TInt iPageShift; /**< The system's page shift */ + DSemaphore* iDefragSemaphore;/**< Semaphore to ensure only one defrag operation is active per channel*/ + TClientRequest* iCompleteReq;/**< Pointer to a request status that will signal to the user side client once the defrag has completed*/ + DThread* iRequestThread; /**< Pointer to the thread that made the defrag request*/ + TRamDefragRequest iDefragReq;/**< The defrag request used to queue defrag operations*/ + DChunk* iBufChunk; /**< Pointer to a chunk that can be mapped to a physical RAM area*/ + TChunkCleanup* iChunkCleanup;/**< Pointer to iBufChunk's cleanup object */ + TDfcQue* iDfcQ; /**< The DFC queue used for driver functions */ + TDfc iDefragCompleteDfc; /**< DFC to be queued once a defrag operation has completed */ + TBool iDefragDfcFree; /**< Set to fase whenever a dfc defrag operation is still pending*/ + TUint iLowestPrefZoneId; /**< The ID of the least preferable RAM zone*/ + TUint iLowestPrefZonePages; /**< The number of pages in the least preferable RAM zone*/ + TUint iLowestPrefZoneIndex; /**< The test HAL function index of the least preferable RAM zone*/ + }; + +/** +Utility functions to wait for chunk clean dfc to be queued by waiting for the +idle thread to be queued. +*/ +void signal_sem(TAny* aPtr) + { + NKern::FSSignal((NFastSemaphore*)aPtr); + } + +TInt WaitForIdle() + {// Wait for chunk to be destroyed and then for the chunk cleanup dfc to run. + for (TUint i = 0; i < 2; i++) + { + NFastSemaphore s(0); + TDfc idler(&signal_sem, &s, Kern::SvMsgQue(), 0); // supervisor thread, priority 0, so will run after destroyed DFC + NTimer timer(&signal_sem, &s); + idler.QueueOnIdle(); + timer.OneShot(NKern::TimerTicks(5000), ETrue); // runs in DFCThread1 + NKern::FSWait(&s); // wait for either idle DFC or timer + TBool timeout = idler.Cancel(); // cancel idler, return TRUE if it hadn't run + TBool tmc = timer.Cancel(); // cancel timer, return TRUE if it hadn't expired + if (!timeout && !tmc) + NKern::FSWait(&s); // both the DFC and the timer went off - wait for the second one + if (timeout) + return KErrTimedOut; + } + return KErrNone; + } + +/** + Standard logical device driver entry point. + Called the first time this device driver is loaded. +*/ +DECLARE_STANDARD_LDD() + { + DDefragChannelFactory* factory = new DDefragChannelFactory; + if (factory) + { + // Allocate a kernel thread to run the DFC + TInt r = Kern::DynamicDfcQCreate(factory->iDfcQ, KDefragCompleteThreadPriority, KDefragCompleteThread); + + if (r != KErrNone) + { + // Must close rather than delete factory as it is a DObject object. + factory->AsyncClose(); + return NULL; + } + } + return factory; + } + + +/** + Constructor +*/ +DDefragChannelFactory::DDefragChannelFactory() + { + iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); + } + + +/** + Destructor +*/ +DDefragChannelFactory::~DDefragChannelFactory() + { + if (iDfcQ != NULL) + {// Destroy the DFC queue created when this device drvier was loaded. + iDfcQ->Destroy(); + } + } + + +/** + Create a new DDefragChannel on this logical device. + +@param aChannel On successful return this will point to the new channel. +@return KErrNone on success or KErrNoMemory if the channel couldn't be created. +*/ +TInt DDefragChannelFactory::Create(DLogicalChannelBase*& aChannel) + { + aChannel = new DDefragChannel(iDfcQ); + return (aChannel)? KErrNone : KErrNoMemory; + } + + +/** + Install the LDD - overriding pure virtual + +@return KErrNone on success or one of the system wide error codes. +*/ +TInt DDefragChannelFactory::Install() + { + return SetName(&KLddName); + } + + +/** + Get capabilities - overriding pure virtual + +@param aDes A descriptor to be loaded with the capabilities. +*/ +void DDefragChannelFactory::GetCaps(TDes8& aDes) const + { + TCapsDefragTestV01 b; + b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); + Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b)); + } + + +/** + Constructor + +@param aDfcQ The DFC queue to use for defrag completion DFCs. +*/ +DDefragChannel::DDefragChannel(TDfcQue* aDfcQ) + : + iDefragSemaphore(NULL), + iCompleteReq(NULL), + iBufChunk(NULL), + iChunkCleanup(NULL), + iDfcQ(aDfcQ), + iDefragCompleteDfc(DefragCompleteDfc, (TAny*)this, 1) // DFC is priority '1', it is the only type of dfc on this queue. + { + } + + +/** + Create channel. + +@param aVer The version number required. +@return KErrNone on success, KErrNotSupported if the device doesn't support defragmentation. +*/ +TInt DDefragChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer) + { + // Check the client has ECapabilityPowerMgmt capability. + if(!Kern::CurrentThreadHasCapability(ECapabilityPowerMgmt, __PLATSEC_DIAGNOSTIC_STRING("Checked by DDefragChannel"))) + { + return KErrPermissionDenied; + } + TInt pageSize; + TInt r = Kern::HalFunction(EHalGroupKernel, EKernelHalPageSizeInBytes, &pageSize, 0); + if (r != KErrNone) + { + TRACE(Kern::Printf("ERROR - Unable to determine page size")); + return r; + } + TUint32 pageMask = pageSize; + TUint i = 0; + for (; i < 32; i++) + { + if (pageMask & 1) + { + if (pageMask & ~1u) + { + TRACE(Kern::Printf("ERROR - page size not a power of 2")); + return KErrNotSupported; + } + iPageShift = i; + break; + } + pageMask >>= 1; + } + + // Check the client is a supported version. + if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer)) + { + return KErrNotSupported; + } + + // Check this system has more than one RAM zone defined. + // A real driver shouldn't need to do this as any driver that uses defrag should + // only be loaded on devices that support it. + TInt ret = FindLowestPrefZone(); + if (ret != KErrNone) + {// Only one zone so can't move pages anywhere or empty a zone + return KErrNotSupported; + } + + // Create a semaphore to protect defrag invocation. OK to just use one name as + // the semaphore is not global so it's name doesn't need to be unique. + ret = Kern::SemaphoreCreate(iDefragSemaphore, _L("DefragRefSem"), 1); + if (ret != KErrNone) + { + return ret; + } + + // Create a client request for completing dfc defrag requests. + ret = Kern::CreateClientRequest(iCompleteReq); + if (ret != KErrNone) + { + iDefragSemaphore->Close(NULL); + return ret; + } + + // Setup a DFC to be invoked when a defrag operation completes. + iDefragCompleteDfc.SetDfcQ(iDfcQ); + iDefragDfcFree = ETrue; + + return KErrNone; + } + + +/** + Destructor +*/ +DDefragChannel::~DDefragChannel() + { + // Clean up any heap objects. + if (iDefragSemaphore != NULL) + { + iDefragSemaphore->Close(NULL); + } + + // Unregister from any chunk cleanup object as we are to be deleted. + if (iChunkCleanup != NULL) + { + iChunkCleanup->RemoveDevice(); + } + // Clean up any client request object. + if (iCompleteReq) + { + Kern::DestroyClientRequest(iCompleteReq); + } + // Free any existing chunk. + DoChunkClose(); + } + + +/** + Handle the requests for this channel. + +@param aFunction The operation the LDD should perform. +@param a1 The first argument for the operation. +@param a2 The second argument for the operation. +@return KErrNone on success or one of the system wide error codes. +*/ +TInt DDefragChannel::Request(TInt aFunction, TAny* a1, TAny* a2) + { + TInt r = KErrNone; + NKern::ThreadEnterCS(); + + Kern::SemaphoreWait(*iDefragSemaphore); + if (!iDefragDfcFree && aFunction != RDefragChannel::EControlGeneralDefragDfcComplete) + {// Only allow a single defrag operation at a time. + r = KErrInUse; + goto exit; + } + + switch (aFunction) + { + case RDefragChannel::EControlGeneralDefragDfc: + // Queue a defrag operation so that on completion it queues a + // DFC on this driver. + iRequestThread = &Kern::CurrentThread(); + iRequestThread->Open(); + + // Open a reference on this channel to stop the destructor running before + // the defrag request has completed. + Open(); + r = iCompleteReq->SetStatus((TRequestStatus*)a1); + if (r == KErrNone) + r = iDefragReq.DefragRam(&iDefragCompleteDfc, KDefragRamThreadPriority); + if (r != KErrNone) + {// defrag operation didn't start so close all openned handles + AsyncClose(); + iRequestThread->AsyncClose(); + iRequestThread = NULL; + } + else + iDefragDfcFree = EFalse; + break; + + case RDefragChannel::EControlGeneralDefragDfcComplete: + if (iRequestThread != NULL) + {// The defrag dfc hasn't completed so this shouldn't have been invoked. + r = KErrGeneral; + } + else + { + iDefragDfcFree = ETrue; + } + break; + + case RDefragChannel::EControlGeneralDefragSem: + {// Queue a defrag operation so that it will signal a fast mutex once + // it has completed. + NFastSemaphore sem; + NKern::FSSetOwner(&sem, 0); + r = iDefragReq.DefragRam(&sem, KDefragRamThreadPriority); + + if (r != KErrNone) + {// Error occurred attempting to queue the defrag operation. + break; + } + + // Defrag operation has now been queued so wait for it to finish. + // Could do some extra kernel side work here before waiting on the + // semaphore. + NKern::FSWait(&sem); + r = iDefragReq.Result(); + } + break; + + case RDefragChannel::EControlGeneralDefrag: + // Synchronously perform a defrag. + { + r = iDefragReq.DefragRam(KDefragRamThreadPriority); + } + break; + + case RDefragChannel::EControlAllocLowestZone: + // Allocate from the lowest preference zone + r = DoAllocLowestZone(); + break; + + case RDefragChannel::EControlClaimLowestZone: + // Claims the lowest preference zone + r = DoClaimLowestZone(); + break; + + case RDefragChannel::EControlCloseChunk: + // Have finished with the chunk so close it then free the RAM mapped by it + r = DoChunkClose(); + TRACE( if (r != KErrNone) {Kern::Printf("ChunkClose returns %d", r);}); + break; + + default: + r=KErrNotSupported; + break; + } +exit: + Kern::SemaphoreSignal(*iDefragSemaphore); + NKern::ThreadLeaveCS(); + TRACE(if (r!=KErrNone) {Kern::Printf("DDefragChannel::Request returns %d", r); }); + return r; + } + + +/** + Allocates RAM from the lowest preference zone and maps it to a shared chunk. + + Real drivers would not need to determine which zone to allocate from as they + will know the zone's ID. + +@return KErrNone on success, otherwise one of the system wide error codes. +*/ +TInt DDefragChannel::DoAllocLowestZone() + { + TInt r = KErrNone; + TLinAddr chunkAddr = NULL; + TUint32 mapAttr = NULL; + TChunkCreateInfo createInfo; + TLinAddr bufBaseAddr; + TUint bufPages; + TPhysAddr* bufAddrs; + + if (iBufChunk != NULL) + {// The buffer chunk is already mapped so can't use again until it is + // freed/closed. Wait a short while for it to be freed as it may be in the + // process of being destroyed. + if (WaitForIdle() != KErrNone || iBufChunk != NULL) + {// chunk still hasn't been freed so can't proceed. + r = KErrInUse; + goto exit; + } + } + + // Attempt to allocate all the pages it should be possible to allocate. + // Real device drivers will now how much they need to allocate so they + // wouldn't determine it here. + SRamZoneUtilisation zoneUtil; + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)iLowestPrefZoneIndex, (TAny*)&zoneUtil); + bufPages = iLowestPrefZonePages - (zoneUtil.iAllocFixed + zoneUtil.iAllocUnknown + zoneUtil.iAllocOther); + bufAddrs = new TPhysAddr[bufPages]; + if (!bufAddrs) + { + TRACE(Kern::Printf("Failed to allocate an array for bufAddrs")); + r = KErrNoMemory; + goto exit; + } + + // Update the page count as bufAddrs allocation may have caused the kernel + // heap to grow. + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)iLowestPrefZoneIndex, (TAny*)&zoneUtil); + bufPages = iLowestPrefZonePages - (zoneUtil.iAllocFixed + zoneUtil.iAllocUnknown + zoneUtil.iAllocOther); + + // Allocate discontiguous pages from the zone + r = Epoc::ZoneAllocPhysicalRam(iLowestPrefZoneId, bufPages, bufAddrs); + if (r != KErrNone && r != KErrNoMemory) + { + TRACE(Kern::Printf("Zone Alloc returns %d bufPages %x", r, bufPages)); + goto exit; + } + // If we couldn't allocate all the required pages then empty the zone + // and retry. + if (r == KErrNoMemory) + { + r = iDefragReq.EmptyRamZone(iLowestPrefZoneId, TRamDefragRequest::KInheritPriority); + if (r != KErrNone) + { + TRACE(Kern::Printf("Empty returns %d", r)); + goto exit; + } + r = Epoc::ZoneAllocPhysicalRam(iLowestPrefZoneId, bufPages, bufAddrs); + if (r != KErrNone) + { + TRACE(Kern::Printf("ZoneAlloc1 returns %d bufPages %x", r, bufPages)); + goto exit; + } + } + + // Create a chunk cleanup object which will free the physical RAM when the + // chunk is detroyed + iChunkCleanup = new TChunkCleanup(this, bufAddrs, bufPages); + if (!iChunkCleanup) + { + TRACE(Kern::Printf("iChunkCleanup creation failed")); + r = Epoc::FreePhysicalRam(bufPages, bufAddrs); + if (r != KErrNone) + { + TRACE(Kern::Printf("ERROR - freeing physical memory when chunkCleanup create failed")); + } + else + { + r = KErrNoMemory; + } + goto exit; + } + + // Map the allocated buffer pages to a chunk so we can use it. + createInfo.iType = TChunkCreateInfo::ESharedKernelSingle; // could also be ESharedKernelMultiple + createInfo.iMaxSize = bufPages << iPageShift; + createInfo.iMapAttr = EMapAttrFullyBlocking; // Non-cached - See TMappingAttributes for all options + createInfo.iOwnsMemory = EFalse; // Must be false as the physical RAM has already been allocated + createInfo.iDestroyedDfc = iChunkCleanup; + r = Kern::ChunkCreate(createInfo, iBufChunk, chunkAddr, mapAttr); + if (r != KErrNone) + { + TRACE(Kern::Printf("ChunkCreate returns %d size %x pages %x", r, createInfo.iMaxSize, bufPages)); + goto exit; + } + + // Map the physical memory to the chunk + r = Kern::ChunkCommitPhysical(iBufChunk, 0, createInfo.iMaxSize, bufAddrs); + if (r != KErrNone) + { + TRACE(Kern::Printf("CommitPhys returns %d", r)); + goto exit; + } + + // Now that the RAM is mapped into a chunk get the kernel-side virtual + // base address of the buffer. + r = Kern::ChunkAddress(iBufChunk, 0, createInfo.iMaxSize, bufBaseAddr); + + // Using bufBaseAddr a real driver may now do something with the buffer. We'll just return. + +exit: + return r; + } + + +/** + Claims the lowest preference zone and maps it to a shared chunk. + + Real drivers would not need to determine which zone to allocate from as they + will know the zone's ID. + +@return KErrNone on success, otherwise one of the system wide error codes. +*/ +TInt DDefragChannel::DoClaimLowestZone() + { + TInt r = KErrNone; + TChunkCreateInfo createInfo; + TLinAddr bufBaseAddr; + TLinAddr chunkAddr; + TUint32 mapAttr = NULL; + TPhysAddr bufBase; + TUint bufBytes; + + if (iBufChunk != NULL) + {// The buffer chunk is already mapped so can't use again until it is + // freed/closed. Wait a short while for it to be freed as it may be in the + // process of being destroyed. + if (WaitForIdle() != KErrNone || iBufChunk != NULL) + {// chunk still hasn't been freed so can't proceed. + r = KErrInUse; + goto exit; + } + } + + // Claim the zone the base address of which will be stored in iBufBase. + r = iDefragReq.ClaimRamZone(iLowestPrefZoneId, bufBase, TRamDefragRequest::KInheritPriority); + if (r != KErrNone) + { + TRACE(Kern::Printf("Claim returns %d", r)); + goto exit; + } + + // Create a chunk cleanup object which will free the physical RAM when the + // chunk is detroyed + bufBytes = iLowestPrefZonePages << iPageShift; + iChunkCleanup = new TChunkCleanup(this, bufBase, bufBytes); + if (!iChunkCleanup) + { + TRACE(Kern::Printf("chunkCleanup creation failed")); + r = Epoc::FreePhysicalRam(bufBytes, bufBase); + if (r != KErrNone) + { + TRACE(Kern::Printf("ERROR - freeing physical memory when chunkCleanup create failed")); + } + else + { + r = KErrNoMemory; + } + goto exit; + } + + // Map the allocated buffer pages to a chunk so we can use it. + createInfo.iType = TChunkCreateInfo::ESharedKernelSingle; // could also be ESharedKernelMultiple + createInfo.iMaxSize = bufBytes; + createInfo.iMapAttr = EMapAttrFullyBlocking; // Non-cached - See TMappingAttributes for all options + createInfo.iOwnsMemory = EFalse; // Must be false as the physical RAM has already been allocated + createInfo.iDestroyedDfc = iChunkCleanup; + r = Kern::ChunkCreate(createInfo, iBufChunk, chunkAddr, mapAttr); + if (r != KErrNone) + { + TRACE(Kern::Printf("ChunkCreate returns %d size %x bytes %x", r, createInfo.iMaxSize, bufBytes)); + goto exit; + } + + // Map the physically contiguous memory to the chunk + r = Kern::ChunkCommitPhysical(iBufChunk, 0, createInfo.iMaxSize, bufBase); + if (r != KErrNone) + { + TRACE(Kern::Printf("CommitPhys returns %d", r)); + goto exit; + } + + // Now that the RAM is mapped into a chunk get the kernel-side virtual + // base address of the buffer. + r = Kern::ChunkAddress(iBufChunk, 0, createInfo.iMaxSize, bufBaseAddr); + + // Using bufBaseAddr a real driver may now do something with the buffer. We'll just return. + +exit: + return r; + } + + +/** + Determine the lowest preference zone. + +@return KErrNone on success or KErrNotFound if there is only one zone. +*/ +TInt DDefragChannel::FindLowestPrefZone() + { + TUint zoneCount; + TInt r = Kern::HalFunction(EHalGroupRam, ERamHalGetZoneCount, (TAny*)&zoneCount, NULL); + if(r!=KErrNone) + return r; + + if (zoneCount == 1) + {// Only one zone so can't move pages anywhere or empty a zone + return KErrNotFound; + } + + SRamZoneConfig zoneConfig; + SRamZoneUtilisation zoneUtil; + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneConfig, (TAny*)0, (TAny*)&zoneConfig); + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)0, (TAny*)&zoneUtil); + TUint lowestPref = zoneConfig.iPref; + TUint lowestFreePages = zoneUtil.iFreePages; + iLowestPrefZoneIndex = 0; + iLowestPrefZoneId = zoneConfig.iZoneId; + TUint i = 1; + for (; i < zoneCount; i++) + { + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneConfig, (TAny*)i, (TAny*)&zoneConfig); + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneUtilisation, (TAny*)i, (TAny*)&zoneUtil); + // When zones have the same preference the zone higher in the zone list is picked. + if (zoneConfig.iPref > lowestPref || + (zoneConfig.iPref == lowestPref && zoneUtil.iFreePages >= lowestFreePages)) + { + lowestPref = zoneConfig.iPref; + lowestFreePages = zoneUtil.iFreePages; + iLowestPrefZoneIndex = i; + iLowestPrefZoneId = zoneConfig.iZoneId; + } + } + // Now that we know the current least preferable zone store its size. + Kern::HalFunction(EHalGroupRam, ERamHalGetZoneConfig, (TAny*)iLowestPrefZoneIndex, (TAny*)&zoneConfig); + iLowestPrefZonePages = zoneConfig.iPhysPages; + TRACE(Kern::Printf("LowestPrefZone %x size %x", iLowestPrefZoneId, iLowestPrefZonePages)); + return KErrNone; + } + + +/** + DFC callback called when a defrag operation has completed. + +@param aSelf A pointer to the DDefragChannel that requested the defrag operation +*/ +void DDefragChannel::DefragCompleteDfc(TAny* aSelf) + { + // Just call non-static method + ((DDefragChannel*)aSelf)->DefragComplete(); + } + + +/** + Invoked by the DFC callback which is called when a defrag + operation has completed. +*/ +void DDefragChannel::DefragComplete() + { + TRACE(Kern::Printf(">DDefragChannel::DefragComplete")); + TInt result = iDefragReq.Result(); + TRACE(Kern::Printf("complete code %d", result)); + + Kern::SemaphoreWait(*iDefragSemaphore); + + Kern::QueueRequestComplete(iRequestThread, iCompleteReq, result); + iRequestThread->AsyncClose(); + iRequestThread = NULL; + + Kern::SemaphoreSignal(*iDefragSemaphore); + + TRACE(Kern::Printf("DoChunkDestroyed(); + + // We've finished so now delete ourself + delete aSelf; + } + + +/** + The chunk has been destroyed so free the physical RAM that was allocated + for its use and inform iDevice that it has been destroyed. +*/ +void TChunkCleanup::DoChunkDestroyed() + { + if (iBufContiguous) + { + __NK_ASSERT_ALWAYS(Epoc::FreePhysicalRam(iBufBase, iBufSize) == KErrNone); + } + else + { + __NK_ASSERT_ALWAYS(Epoc::FreePhysicalRam(iBufSize, iBufAddrs) == KErrNone); + } + + if (iDevice != NULL) + {// Allow iDevice to perform any cleanup it requires for this chunk. + iDevice->ChunkDestroyed(); + } + } + + +/** + Remove the device so its ChunkDestroyed() method isn't invoked when the chunk is + destroyed. +*/ +void TChunkCleanup::RemoveDevice() + { + __e32_atomic_store_ord_ptr(&iDevice, 0); + }