kerneltest/e32test/examples/defrag/d_defrag_ref.cpp
changeset 0 a41df078684a
--- /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 <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);
+	}