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