kernel/eka/memmodel/epoc/flexible/mmu/mpagearray.h
changeset 0 a41df078684a
child 22 2f92ad2dc5db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpagearray.h	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,864 @@
+// 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:
+//
+
+#ifndef MPAGEARRAY_H
+#define MPAGEARRAY_H
+
+#include "mmu.h"
+
+const TUint KPageArraySegmentShift = 4;
+
+/**
+Number of entries in each segment RPageArray::TSegment.
+*/
+const TUint KPageArraySegmentSize = (1<<KPageArraySegmentShift);
+
+const TUint KPageArraySegmentMask = KPageArraySegmentSize-1;
+
+/**
+Bit position in RPageArray::TSegment::iCount for the least significant bit
+of the 'AllocCount'. I.e. the number of entries which are not empty.
+*/
+const TUint KPageArraySegmentAllocCountShift = 31-KPageArraySegmentShift;
+
+const TUint KPageArraySegmentLockCountMask = (1<<KPageArraySegmentAllocCountShift)-1;
+
+/**
+Array which contains the physical addresses of all the pages contained in a DMemoryObject.
+This is a sparse array, therefore memory storage may not exist for unallocated pages entries.
+Where storage does exists for unallocated entries, a state value of ENotPresent indicates this.
+For allocated entries, the redundant least significant bits of each entry contain flags and state
+from from enum TFlags and TState.
+
+To add pages to the array:
+
+@code
+	RPageArray::TIter iter;
+	array.AddStart(index,count,iter);
+		RPageArray::TIter pageList;
+		while(n = iter.AddFind(pageList))
+			{
+			pageList.Add(n,pages);
+			// or pageList.AddContiguous
+			}
+	array.AddEnd(index,count);
+@endcode
+
+
+To remove pages from the array:
+
+@code
+	RPageArray::TIter iter;
+	array.FindStart(index,count,iter);
+		RPageArray::TIter pageList;
+		while(n = iter.RemoveFind(pageList))
+			{
+			pageList.Remove(n,pages);
+			iter.FindRelease(n);
+			}
+	array.FindEnd(index,count);
+@endcode
+
+Mutual exclusion must be used to ensure that only a single Add or Remove operation is in
+progress at any time.
+
+
+To query the contents of the array:
+
+@code
+	RPageArray::TIter iter;
+	array.FindStart(index,count,iter);
+		RPageArray::TIter pageList;
+		while(n=iter.Find(pageList));
+			{
+			TPhysAddr* pages;
+			while(n = pageList.Pages(pages,max))
+				{
+				// do something with pages
+				pageList.Skip(n);
+				}
+			iter.FindRelease(n);
+			}
+	array.FindEnd(index,count);
+@endcode
+
+*/
+class RPageArray
+	{
+public:
+	class TSegment;
+	class TIter;
+
+	/**
+	States for pages stored in the array. These are stored in least significant part of each entry.
+	*/
+	enum TState
+		{
+		ENotPresent			= 0x000, ///< No page present.
+		EDecommitted		= 0x001, ///< Paged Decommitted, but is pinned
+		EDecommitting		= 0x002, ///< Page is in the process of being decommitted
+		EStealing			= 0x003, ///< Page is in the process of being stolen
+		ERestrictingNA		= 0x004, ///< Page is in the process of having no-access restrictions applied
+		EMoving				= 0x005, ///< Page is in the process of being moved to another physical page
+		ECommitted			= 0x006, ///< Page is committed
+
+		EStateShift			= 3,	 ///< Number of bits needed to store state values.
+		EStateMask			= (1<<EStateShift)-1, ///< Mask for state values
+
+		EEmptyEntry			= ENotPresent ///< Value of an empty array entry
+		};
+
+	/**
+	Flags stored in array entries in addition to the state.
+	*/
+	enum TFlags
+		{
+		EUnmapVetoed		= 1<<EStateShift, ///< A Steal or Decommit operation on the page has been vetoed
+
+		EFlagsShift			= 1,	 ///< Number of bits needed to store flags.
+		EFlagsMask			= ((1<<EFlagsShift)-1)<<EStateShift ///< Mask for flags values
+		};
+
+	/**
+	Return true if the array entry \a aPage is currently being decommitted.
+	*/
+	static FORCE_INLINE TBool TargetStateIsDecommitted(TPhysAddr aPage)
+		{
+		return State(aPage)<=EStealing;
+		}
+
+	/**
+	Return true if the array entry \a aPage is currently committed and may be being moved.
+	*/
+	static FORCE_INLINE TBool TargetStateIsCommitted(TPhysAddr aPage)
+		{
+		__ASSERT_COMPILE(RPageArray::EMoving == RPageArray::ECommitted - 1);
+		return State(aPage)>=EMoving;
+		}
+
+	/**
+	Return true if the array entry \a aPage is not present.
+	*/
+	static FORCE_INLINE TBool IsPresent(TPhysAddr aPage)
+		{
+		return State(aPage)!=ENotPresent;
+		}
+
+	/**
+	Return the TState value in the array entry \a aPage.
+	*/
+	static FORCE_INLINE TState State(TPhysAddr aPage)
+		{
+		return (TState)(aPage&EStateMask);
+		}
+
+	/**
+	Page moving has ended so set the page back to committed if no other 
+	operation has occurred/is occurring.
+
+	@param aEntry		A reference to the entry to update.
+	*/
+	static FORCE_INLINE void MovePageEnd(TPhysAddr& aEntry)
+		{
+		if (State(aEntry) == EMoving)
+			aEntry = (aEntry & ~EStateMask) | ECommitted;
+		}
+
+	/**
+	Update the physical address in the array entry \a aEntry.
+	@param aEntry		A reference to the entry to update.
+	@param aPhysAddr	The new physical address.
+	*/
+	static FORCE_INLINE void PageMoveNewAddr(TPhysAddr& aEntry, TPhysAddr aPhysAddr)
+		{
+		__NK_ASSERT_DEBUG(!(aPhysAddr & EStateMask));
+		__NK_ASSERT_DEBUG(State(aEntry) == EMoving);
+		aEntry = (aEntry & EStateMask) | aPhysAddr;
+		}
+
+	static void Init2A();
+	static void Init2B(DMutex* aLock);
+
+	RPageArray();
+	~RPageArray();
+
+	/**
+	Second stage constructor for the array.
+
+	@param aMaxPages			The maximum number of entries to be stored in the array.
+	@param aPreallocateMemory	If true, then all the memory required to store the array
+								entries is allocated immediately - rather than on demand
+								as entries are added.
+	*/
+	TInt Construct(TUint aMaxPages, TBool aPreallocateMemory=EFalse);
+
+	/**
+	Allocate all memory required to store array entries.
+	This is only for use during system boot.
+	*/
+	TInt PreallocateMemory();
+
+	/**
+	Ensures the memory to store a region of array entries is allocated and locked.
+
+	@param aIndex	Start index of region.
+	@param aCount	Number of pages in region.
+
+	@see RPageArray::Free()
+	*/
+	TInt Alloc(TUint aIndex, TUint aCount);
+
+	/**
+	Revert the action of #Alloc by unlocking the memory used for a region of array entries.
+	Note, calling #Free for any entry more times than #Alloc was used will have
+	unpredictable results.
+
+	@param aIndex	Start index of region.
+	@param aCount	Number of pages in region.
+	*/
+	void Free(TUint aIndex, TUint aCount);
+
+	/**
+	Prepare to add (commit) pages to a region in this array.
+	This ensures the memory to store the entries is allocated and locked. It also,
+	optionally and by default, check that these entries are empty.
+
+	@param aIndex			Start index of region.
+	@param aCount			Number of pages in region.
+	@param[out] aIter		An iterator which covers the specified region.
+	@param aAllowExisting	True if the region may contain non-empty entries.
+							False to assert entries are empty.
+
+	@see RPageArray::AddEnd()
+	*/
+	TInt AddStart(TUint aIndex, TUint aCount, TIter& aIter, TBool aAllowExisting=EFalse);
+
+	/**
+	End an 'add' operation started with #AddStart.
+	This must be called to unlock any page array memory which #AddStart locked.
+
+	@param aIndex	Start index of region. Must be same value as corresponding call to #AddStart.
+	@param aCount	Number of pages in region. Must be same value as corresponding call to #AddStart.
+	*/
+	void AddEnd(TUint aIndex, TUint aCount);
+
+	/**
+	Prepare to search a region in this array.
+
+	@param aIndex			Start index of region.
+	@param aCount			Number of pages in region.
+	@param[out] aIter		An iterator which covers the specified region.
+
+	@see RPageArray::AddEnd()
+	*/
+	void FindStart(TUint aIndex, TUint aCount, TIter& aIter);
+
+	/**
+	End a find operation started with #FindStart.
+
+	@param aIndex	Start index of region. Must be same value as corresponding call to #FindStart.
+	@param aCount	Number of pages in region. Must be same value as corresponding call to #FindStart.
+	*/
+	void FindEnd(TUint aIndex, TUint aCount);
+
+	/**
+	Prepare to add (commit) a single page to this array.
+	This ensures the memory to store the entry is allocated and locked.
+
+	@param aIndex			Index of entry.
+	@param[out] aPageList	An iterator represents the single array entry.
+
+	@return Pointer to the array entry,
+			or the null pointer if memory allocation failed.
+
+	@see RPageArray::AddPage()
+	@see RPageArray::AddPageEnd()
+	*/
+	TPhysAddr* AddPageStart(TUint aIndex, TIter& aPageList);
+
+	/**
+	Add (commit) a single page to the array.
+
+	@param aPageEntry	The address of the array entry as returned by AddPageStart.
+	@param aPage		The physical address of the page being added.
+	*/
+	static void AddPage(TPhysAddr* aPageEntry, TPhysAddr aPage);
+
+	/**
+	End an 'add' operation started with #AddPageStart.
+	This must be called to unlock any page array memory which #AddPageStart locked.
+
+	@param aIndex	Index of entry. Must be same value as corresponding call to #AddPageStart.
+	@param aDelta	1 (one), if the array entry was changed from ENotPresent state (e.g. AddPage called),
+					zero otherwise.
+	*/
+	void AddPageEnd(TUint aIndex, TInt aDelta);
+
+	/**
+	Prepare to remove (decommit) a single page from this array.
+
+	This function is similar to TIter::RemoveFind and updates the array entry and
+	memory locking in the same way.
+
+	@param aIndex			Index of entry.
+	@param[out] aPageList	An iterator representing the single array entry.
+							Not set if this method returns the null pointer.
+
+	@return Pointer to the array entry,
+			or the null pointer if entry does not need decommitting.
+
+	@see RPageArray::RemovePage()
+	@see RPageArray::RemovePageEnd()
+	*/
+	TPhysAddr* RemovePageStart(TUint aIndex, TIter& aPageList);
+
+	/**
+	Remove a single page from the array.
+
+	This function is similar to TIter::Remove and updates the array entry in the same way.
+
+	@param aPageEntry	The address of the array entry as returned by RemovePageStart.
+
+	@return The physical address of the page which was removed,
+			or KPhysAddrInvalid if no page was removed.
+	*/
+	static TPhysAddr RemovePage(TPhysAddr* aPageEntry);
+
+	/**
+	End an 'remove' operation started with #RemovePageStart.
+	This must be called to unlock any page array memory which #RemovePageStart locked.
+
+	@param aIndex	Index of entry. Must be same value as corresponding call to #RemovePageStart.
+	@param aDelta	1 (one), if the array entry was set ENotPresent state (RemovePage succeeded),
+					zero otherwise.
+	*/
+	void RemovePageEnd(TUint aIndex, TInt aDelta);
+
+	/**
+	Prepare to restrict access to a single page in this array.
+
+	If the page entry state indicates that the page is already more restricted
+	than being requested, then the function returns the null pointer and does nothing.
+
+	If the page does need its access restricting then its entry in the array is set to
+	ERestrictingNA and the memory for the entry is locked.
+
+	@param aIndex			Index of entry.
+	@param[out] aPageList	An iterator representing the single array entry.
+							Not set if this method returns the null pointer.
+
+	@return Pointer to the array entry,
+			or the null pointer if entry does not need it's access restricting further.
+
+	@see RPageArray::RestrictPageNAEnd()
+	*/
+	TPhysAddr* RestrictPageNAStart(TUint aIndex, TIter& aPageList);
+
+	/**
+	End an 'restrict' operation started with #RestrictPageStart.
+	This must be called to unlock any page array memory which #RestrictPageStart locked.
+
+	@param aIndex	Index of entry. Must be same value as corresponding call to #RestrictPageStart.
+	*/
+	void RestrictPageNAEnd(TUint aIndex);
+
+	/**
+	Prepare to steal a single page from this array.
+
+	The memory for the entry is locked and if the page entry is is in one of the committed
+	states then it is changed to state EStealing.
+
+	@param aIndex			Index of entry.
+	@param[out] aPageList	An iterator representing the single array entry.
+							Not set if this method returns the null pointer.
+
+	@return Pointer to the array entry.
+
+	@see RPageArray::StealPageEnd()
+	*/
+	TPhysAddr* StealPageStart(TUint aIndex, TIter& aPageList);
+
+	/**
+	End an 'steal' operation started with #StealPageStart.
+	This must be called to unlock any page array memory which #StealPageStart locked.
+
+	@param aIndex	Index of entry. Must be same value as corresponding call to #StealPageStart.
+	@param aDelta	1 (one), if the array entry was set ENotPresent state (the page was stolen),
+					zero otherwise.
+	*/
+	void StealPageEnd(TUint aIndex, TInt aDelta);
+
+	/**
+	Prepare to move a page in this array by changing its state to EMoving.
+
+	Note - the memory entry isn't locked as the RamAllocLock mutex must be held 
+	through out	the page moving process and therefore the page cannot be removed.
+
+	@param aIndex			The index of the entry to be moved.
+	@param[out] aPageList	An iterator representing the single array entry.
+							Not set if this method returns the null pointer.
+
+	@return Pointer to the array entry, NULL if the page cannot be moved.
+	
+	@see RPageArray::MovePageEnd()
+	*/
+	TPhysAddr* MovePageStart(TUint aIndex, TIter& aPageList);
+
+	/**
+	Return the array entry for index \a aIndex.
+	*/
+	TPhysAddr Page(TUint aIndex);
+
+
+	/**
+	Return a pointer to the array entry for index \a aIndex.
+
+	@return Pointer to the array entry, NULL if the page cannot found.
+	*/
+	TPhysAddr* PageEntry(TUint aIndex);
+
+	/**
+	Return the physical address of the page at index \a aIndex, or KPhysAddrInvalid if none present.
+	*/
+	TPhysAddr PhysAddr(TUint aIndex);
+
+	/**
+	Get the physical address for the pages stored in the specified region in the array.
+
+	@param aIndex					Start index of region.
+	@param aCount					Number of pages in region.
+	@param[out] aPhysicalAddress	If all pages are physically contiguous this is set to the start address,
+									otherwise this is set to KPhysAddrInvalid.
+	@param[out] aPhysicalPageList	Pointer to array of \a aCount physical addresses which
+									will be filled with the physical addressed of each page in the region.
+
+	@return	0 (zero) if all pages in region are physically contiguous;
+			1 (one) if pages are not physically contiguous;
+			KErrNotFound, if any page in the region is not present.		
+	*/
+	TInt PhysAddr(TUint aIndex, TUint aCount, TPhysAddr& aPhysicalAddress, TPhysAddr* aPhysicalPageList);
+
+	enum
+		{
+		/**
+		Maximum number of bits which can be stored in an array entry by SetPagingManagerData.
+		*/
+		KPagingManagerDataBits		= 32-(EFlagsShift+EStateShift),
+		};
+
+	enum
+		{
+		/**
+		Maximum value which can be stored in an array entry by SetPagingManagerData.
+		*/
+		KMaxPagingManagerData		= (1u<<KPagingManagerDataBits)-1u
+		};
+
+	/**
+	Write \a aValue to the paging manager data for index \a aIndex.
+	The value must not exceed KMaxPagingManagerData.
+
+	This value is stored in the page array entry, if it's state is ENotPresent;
+	otherwise it is stored in the SPageInfo object for the page in the array entry.
+	*/
+	void SetPagingManagerData(TUint aIndex, TUint aValue);
+
+	/**
+	Return the paging manager data for index \a aIndex.
+	@see RPageArray::SetPagingManagerData()
+	*/
+	TUint PagingManagerData(TUint aIndex);
+
+
+private:
+	/**
+	Unlock the memory used for a region of array entries.
+
+	@param aSegments	Copy of RPageArray::iSegments from the array.
+	@param aIndex		Start index of region.
+	@param aCount		Number of pages in region.
+	*/
+	static void Release(TSegment** aSegments, TUint aIndex, TUint aCount);
+
+	/**
+	Unlocking the memory used for a single array entry.
+	This also updates
+
+	@param aIndex The index of the array entry.
+	@param aDelta The change in the 'present' state for the entry. This is
+				  1 if the entry was added (state changed from ENotPresent),
+				  -1 if the entry was removed (state changed to ENotPresent),
+				  0 otherwise.
+	*/
+	void ReleasePage(TUint aIndex, TInt aDelta);
+
+	/**
+	Return the array segment in at \a aSegmentEntry, allocating a new one to this if
+	none previously existed. Return the null pointer in no segment could be allocated
+	(out of memory).
+
+	The returned segment is locked (TSegment::Lock) \a aLockCount times; this normally
+	represents the number of entries in the segment which are to be accesses.
+	*/
+	TSegment* GetOrAllocateSegment(TSegment** aSegmentEntry, TUint aLockCount);
+private:
+	TUint8 iPreallocatedMemory; ///< Set true, if this array was constructed with pre-allocated memory. See #Construct.
+	TUint iNumSegments;			///< The number of segments in array iSegments.
+	TSegment** iSegments;		///< Array of TSegment objects allocated for this array. May contain null pointers.
+
+public:
+	/**
+	Class for iterating through and manipulating a section of entries in an RPageArray.
+	*/
+	class TIter
+		{
+	public:
+		/**
+		Find the next region of empty entries.
+
+		@param[out] aPageList	The found region.
+								The #Add or #AddContiguous method is normally subsequently used on this.
+
+		@return The number of pages in the found region. Zero indicating no more empty entries were found.
+
+		@post This iterator is updated start immediately after the found region returned in \a aPageList.
+		*/
+		TUint AddFind(TIter& aPageList);
+
+		/**
+		Add pages to the array, setting each entry state as ECommitted.
+
+		@param aCount		The number of pages to add.
+		@param aPages		Pointer to list of \a aCount physical page addresses to add.
+		*/
+		void Add(TUint aCount, TPhysAddr* aPages);
+
+		/**
+		Add contiguous pages to the array, setting each entry state as ECommitted.
+
+		@param aCount		The number of pages to add.
+		@param aPhysAddr	The physical address of the first page to add.
+		*/
+		void AddContiguous(TUint aCount, TPhysAddr aPhysAddr);
+
+		/**
+		Update iterator and array state as if pages had been added with #Add.
+		This is used after array entries have been directly manipulated rather
+		than being updated through #Add.
+
+		@param aCount	The number of pages to move this iterator on by.
+		@param aChanged	The number of new entries which have been added to the array.
+		*/
+		void Added(TUint aCount, TUint aChanged);
+
+		/**
+		Find the next region of non-empty entries and lock the memory used to store these.
+
+		@param[out] aPageList	The found region.
+								The #Pages method is normally subsequently used on this.
+
+		@return The number of pages in the found region. Zero indicating no more empty entries were found.
+
+		@post This iterator is updated to start at the first found entry.
+
+		@see RPageArray::FindRelease()
+		*/
+		TUint Find(TIter& aPageList);
+
+		/**
+		Unlock the page array memory locked by #Find or #RemoveFind and move this iterator
+		past this region in preparation for a subsequent find operation.
+
+		@param aCount	The number of pages returned by the corresponding find function.
+
+		@post This iterator is updated to start immediately after the region
+			  returned by the corresponding find function.
+		*/
+		void FindRelease(TUint aCount);
+
+		/**
+		Find the next region of entries to be removed (decommitted).
+		The entries found are those which are neither empty nor in state EDecommitted.
+		They are updated to state EDecommitting and the memory used to store these entries
+		is locked. To unlock the memory and continue searching FindRelease
+
+		@param[out] aPageList	The found region.
+								The #Remove method is normally subsequently used on this.
+
+		@return The number of pages in the found region. Zero indicating no more empty entries were found.
+
+		@post This iterator is updated to start at the first found entry.
+
+		@see RPageArray::FindRelease()
+		@see RPageArray::Remove()
+		*/
+		TUint RemoveFind(TIter& aPageList);
+
+		/**
+		Remove pages from the array.
+
+		For each entry found to be in the EDecommitting state (as set by #RemoveFind)
+		the page address in the entry is appended to the supplied array (\a aPages) and
+		the entry set to EEmptyEntry. However, if the array entry has the EUnmapVetoed flag set
+		then instead the entry state is set to EDecommitted and the page address is not appended
+		to \a aPages.
+
+		@param aMaxCount	The maximum number of pages to remove.
+		@param[out] aPages	Pointer to array of \a aMaxCount physical addresses which
+							will be set to the physical addresses of the pages removed.
+
+		@return The number of pages removed from the array and stored at \a aPages.
+
+		@post This iterator is updated start immediately after the last removed entry.
+		*/
+		TUint Remove(TUint aMaxCount, TPhysAddr* aPages);
+
+		/**
+		Return a pointer to the array entries represented by this iterator.
+
+		As array entries may not be stored contiguously in memory this method returns the
+		number of valid entries.
+
+		This method should only be used for array entries which have had their
+		memory locked or for which there are other guarantees that the memory is present.
+
+		@param[out] aStart	Set to the address of the first array entry.
+		@param aMaxCount	The maximum count this function should return.
+
+		@return The number of array entries starting at \a aStart which are valid.
+		*/
+		TUint Pages(TPhysAddr*& aStart, TUint aMaxCount=~0u);
+
+		/**
+		Move this iterator on by \a aCount pages.
+		*/
+		void Skip(TUint aCount);
+
+		/**
+		Prevent pages in the region covered by this iterator from having their
+		access restricted. This is achieved by returning any entries currently
+		in the specified 'being restricted' state to be fully committed again (state ECommitted).
+
+		@param aPageMoving ETrue to veto pages being restricted for page moving (EMoving). Set to EFalse otherwise.
+		*/
+		void VetoRestrict(TBool aPageMoving);
+
+		/**
+		Prevent pages in the region covered by this iterator from being removed from the array.
+		This is achieved by setting the EUnmapVetoed flag for all entries with a current state
+		indicating they are being decommitted, c.f. TargetStateIsDecommitted.
+		*/
+		void VetoUnmap();
+
+		/**
+		Default constructor which does not initialise members.
+		*/
+		TIter();
+
+		/**
+		Return a new iterator which represents the array region [aIndex..aEndIndex).
+		The new region is asserted to be within that specified by this iterator.
+		*/
+		TIter Slice(TUint aIndex, TUint aEndIndex);
+
+		/**
+		Return a new iterator which represents the first \a aCount pages of this one.
+		*/
+		TIter Left(TUint aCount);
+
+		/**
+		Return the start index of the region being represented by this iterator.
+		*/
+		FORCE_INLINE TUint Index() const
+			{ return iIndex; }
+
+		/**
+		Return the index immediately after the being represented by this iterator.
+		*/
+		FORCE_INLINE TUint IndexEnd() const
+			{ return iEndIndex; }
+
+		/**
+		Return the number of entries in the region represented by this iterator.
+		*/
+		FORCE_INLINE TUint Count() const
+			{ return iEndIndex-iIndex; }
+
+	private:
+		TIter(TSegment** aSegments, TUint aIndex, TUint aEndIndex);
+		void Set(TSegment** aSegments, TUint aIndex, TUint aEndIndex);
+	private:
+		TSegment** iSegments;	///< Copy of RPageArray::iSegments of the array being represented by this iterator.
+		TUint iIndex;			///< Start index of the array region being represented by this iterator.
+		TUint iEndIndex;		///< The index immediately after the array region being represented by this iterator.
+
+		friend class RPageArray;
+		};
+
+	/**
+	Class representing the memory storage for a 'segment' of entries in an RPageArray.
+	Each segment contains storage for #KPageArraySegmentSize entries and the number
+	of these which are not #EEmptyEntry are counted by the 'alloc count'.
+	Each segment also has a 'lock count' which acts as a reference count preventing
+	the segment from being deleted whilst it is being manipulated.
+	Both of these counts are combined in #iCounts.
+	*/
+	class TSegment
+		{
+	private:
+		/**
+		Return a newly allocated segment or the null pointer if out-ot-memory.
+		*/
+		static TSegment* New();
+
+		/**
+		Delete \a aSegment and return the null pointer.
+		*/
+		static TSegment* Delete(TSegment* aSegment);
+
+		/**
+		Lock this segment \a aCount times. This prevents the segment being deleted.
+		*/
+		void Lock(TUint aCount=1);
+
+		/**
+		Unlock \a aSegment \a aCount times.
+		If the lock count reaches zero and the segment has no allocated entries
+		then it is deleted and \a aSegment set to the null pointer.
+		*/
+		static TBool Unlock(TSegment*& aSegment, TUint aCount=1);
+
+		/**
+		Adjust the allocation count for this segment by \a aDelta.
+		The allocation count keeps count of the number of entries which are not #EEmptyEntry.
+		*/
+		void AdjustAllocCount(TInt aDelta);
+
+		/**
+		Debug function which outputs the contents of this segment to the kernel debug port.
+		*/
+		void Dump();
+	private:
+		/**
+		Storage for each array entry.
+		*/
+		TPhysAddr iPages[KPageArraySegmentSize];
+
+		/**
+		Two count values are stored in this member.
+		Bits 0..KPageArraySegmentAllocCountShift-1 is the 'lock count' modified by the
+		Lock and Unlock methods.
+		Bits KPageArraySegmentAllocCountShift..31 is the 'alloc count' modified by the
+		AdjustAllocCount method.
+		When both counts are zero, this segment is empty and not being used,
+		and can therefore be deleted.
+		Note, the alloc count is only valid when the lock count is zero, i.e.
+		after all users have finished updating this segment.
+		*/
+		TUint iCounts;
+
+		friend class RPageArray;
+		friend class TIter;
+		};
+
+	friend class RPageArray::TSegment;
+	friend class RPageArray::TIter;
+	};
+
+
+
+//
+// RPageArray::TIter
+//
+
+FORCE_INLINE RPageArray::TIter::TIter()
+	{
+#ifdef _DEBUG
+	iSegments = 0;
+	iIndex = 0;
+	iEndIndex = ~0u;
+#endif
+	}
+
+FORCE_INLINE RPageArray::TIter::TIter(RPageArray::TSegment** aSegments, TUint aIndex, TUint aEndIndex)
+	: iSegments(aSegments), iIndex(aIndex), iEndIndex(aEndIndex)
+	{
+	__NK_ASSERT_DEBUG(iEndIndex>=aIndex);
+	}
+
+FORCE_INLINE RPageArray::TIter RPageArray::TIter::Slice(TUint aIndex, TUint aEndIndex)
+	{
+	__NK_ASSERT_DEBUG(aEndIndex>=aIndex);
+	__NK_ASSERT_DEBUG(aIndex>=iIndex);
+	__NK_ASSERT_DEBUG(aEndIndex<=iEndIndex);
+	return TIter(iSegments,aIndex,aEndIndex);
+	}
+
+FORCE_INLINE RPageArray::TIter RPageArray::TIter::Left(TUint aCount)
+	{
+	__NK_ASSERT_DEBUG(aCount<=Count());
+	return TIter(iSegments,iIndex,iIndex+aCount);
+	}
+
+FORCE_INLINE void RPageArray::TIter::Skip(TUint aCount)
+	{
+	__NK_ASSERT_DEBUG(iIndex+aCount>=iIndex);
+	__NK_ASSERT_DEBUG(iIndex+aCount<=iEndIndex);
+	iIndex += aCount;
+	}
+
+
+//
+// RPageArray
+//
+
+FORCE_INLINE void RPageArray::FindEnd(TUint /*aIndex*/, TUint /*aCount*/)
+	{
+	// nothing to do
+	}
+
+FORCE_INLINE void RPageArray::AddPageEnd(TUint aIndex, TInt aDelta)
+	{
+	MmuLock::Lock();
+	ReleasePage(aIndex,aDelta);
+	MmuLock::Unlock();
+	}
+
+FORCE_INLINE void RPageArray::AddPage(TPhysAddr* aPageEntry, TPhysAddr aPage)
+	{
+	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
+	__NK_ASSERT_DEBUG((aPage&KPageMask)==0);
+	__NK_ASSERT_DEBUG(!RPageArray::IsPresent(*aPageEntry));
+	*aPageEntry = aPage|RPageArray::ECommitted;
+	}
+
+FORCE_INLINE void RPageArray::RestrictPageNAEnd(TUint aIndex)
+	{
+	ReleasePage(aIndex,0);
+	}
+
+FORCE_INLINE void RPageArray::StealPageEnd(TUint aIndex, TInt aDelta)
+	{
+	ReleasePage(aIndex,-aDelta);
+	}
+
+FORCE_INLINE void RPageArray::RemovePageEnd(TUint aIndex, TInt aDelta)
+	{
+	MmuLock::Lock();
+	ReleasePage(aIndex,-aDelta);
+	MmuLock::Unlock();
+	}
+
+#endif