// 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 <kernel/kern_priv.h>
#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("<DDefragChannel::DefragComplete"));
// Close the handle on this channel - WARNING this channel may be
// deleted immmediately after this call so don't access any members
AsyncClose();
}
/**
Close the chunk.
@return KErrNone on success or one of the system wide error codes.
*/
TInt DDefragChannel::DoChunkClose()
{
if (iBufChunk == NULL)
{// Someone tried to close the chunk before using it
return KErrNotFound;
}
// Rely on the chunk cleanup object being called as that
// is what will actually free the physical RAM commited to the chunk.
Kern::ChunkClose(iBufChunk);
return KErrNone;
}
/**
The chunk has now been destroyed so reset the pointers to allow a new
chunk to be created.
*/
void DDefragChannel::ChunkDestroyed()
{
__e32_atomic_store_ord_ptr(&iBufChunk, 0);
__e32_atomic_store_ord_ptr(&iChunkCleanup, 0);
}
/**
Contruct a Shared Chunk cleanup object which will free the chunk's discontiguous
physical memory when a chunk is destroyed.
@param aDevice The device to inform when the chunk is destroyed.
@param aBufBase The physical base addresses of each of the chunk's memory pages.
@param aBufPages The total number of the chunk's pages.
*/
TChunkCleanup::TChunkCleanup(DDefragChannel* aDevice, TPhysAddr* aBufAddrs, TUint aBufPages)
: TDfc((TDfcFn)TChunkCleanup::ChunkDestroyed,this,Kern::SvMsgQue(),0),
iBufAddrs(aBufAddrs),
iBufSize(aBufPages),
iBufContiguous(EFalse),
iDevice(aDevice)
{}
/**
Contruct a Shared Chunk cleanup object which will free the chunk's contiguous
physical memory when a chunk is destroyed.
@param aDevice The device to inform when the chunk is destroyed.
@param aBufBase The physical base address of the chunk's memory.
@param aBufBytes The total number of the chunk's bytes.
*/
TChunkCleanup::TChunkCleanup(DDefragChannel* aDevice, TPhysAddr aBufBase, TUint aBufBytes)
: TDfc((TDfcFn)TChunkCleanup::ChunkDestroyed,this,Kern::SvMsgQue(),0),
iBufBase(aBufBase),
iBufSize(aBufBytes),
iBufContiguous(ETrue),
iDevice(aDevice)
{}
/**
Callback function which is called the DFC runs, i.e. when a chunk is destroyed
and frees the physical memory allocated when the chunk was created.
@param aSelf Pointer to the cleanup object associated with the chunk that has
been destroyed.
*/
void TChunkCleanup::ChunkDestroyed(TChunkCleanup* aSelf)
{
aSelf->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);
}