kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp
branchRCL_3
changeset 28 5b5d147c7838
parent 26 c734af59ce98
--- a/kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp	Tue May 11 17:28:22 2010 +0300
+++ b/kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp	Tue May 25 14:09:55 2010 +0300
@@ -1330,26 +1330,37 @@
 		Kern::Printf("ZoneClearPages: ID 0x%x, req 0x%x", aZone.iId, aRequiredPages));
 	// Discard the required number of discardable pages.
 	TUint offset = 0;
-	TInt r = NextAllocatedPage(&aZone, offset, EPageDiscard);
-	while (r == KErrNone && aRequiredPages)
+	for (; aRequiredPages; offset++)
 		{
+		TInt r = NextAllocatedPage(&aZone, offset, EPageDiscard);
+		if (r != KErrNone)
+			break;
+		if (iContiguousReserved && aZone.iBma[EPageFixed]->NotFree(offset, 1))
+			{
+			offset++;
+			continue;
+			}
 		TPhysAddr physAddr = (offset << KPageShift) + aZone.iPhysBase;
-		TInt discarded = M::DiscardPage(physAddr, aZone.iId, EFalse);
+		TInt discarded = M::DiscardPage(physAddr, aZone.iId, M::EMoveDisMoveDirty);
 		if (discarded == KErrNone)
 			{// The page was successfully discarded.
 			aRequiredPages--;
 			}
-		offset++;
-		r = NextAllocatedPage(&aZone, offset, EPageDiscard);
 		}
 	// Move the required number of movable pages.
-	offset = 0;
-	r = NextAllocatedPage(&aZone, offset, EPageMovable);
-	while(r == KErrNone && aRequiredPages)
+	for (offset = 0; aRequiredPages; offset++)
 		{
+		TInt r = NextAllocatedPage(&aZone, offset, EPageMovable);
+		if (r != KErrNone)
+			break;
+		if (iContiguousReserved && aZone.iBma[EPageFixed]->NotFree(offset, 1))
+			{
+			offset++;
+			continue;
+			}
 		TPhysAddr physAddr = (offset << KPageShift) + aZone.iPhysBase;
 		TPhysAddr newAddr = KPhysAddrInvalid;
-		if (M::MovePage(physAddr, newAddr, aZone.iId, EFalse) == KErrNone)
+		if (M::MovePage(physAddr, newAddr, aZone.iId, 0) == KErrNone)
 			{// The page was successfully moved.
 #ifdef _DEBUG
 			TInt newOffset = 0;
@@ -1358,8 +1369,6 @@
 #endif
 			aRequiredPages--;
 			}
-		offset++;
-		r = NextAllocatedPage(&aZone, offset, EPageMovable);
 		}
 	}
 
@@ -1441,14 +1450,15 @@
 	TPhysAddr* pageListBase = aPageList;
 	TUint32 numMissing = aNumPages;
 
+	if ((TUint)aNumPages > iTotalFreeRamPages)
+		{// Not enough free pages to fulfill this request so return the amount required
+		return aNumPages - iTotalFreeRamPages;
+		}
+
 	if (aType == EPageFixed)
 		{// Currently only a general defrag operation should set this and it won't
 		// allocate fixed pages.
 		__NK_ASSERT_DEBUG(!aBlockRest);
-		if ((TUint)aNumPages > iTotalFreeRamPages + M::NumberOfFreeDpPages())
-			{// Not enough free space and not enough freeable pages.
-			goto exit;
-			}
 
 		// Search through each zone in preference order until all pages allocated or
 		// have reached the end of the preference list
@@ -1484,11 +1494,6 @@
 		}
 	else
 		{
-		if ((TUint)aNumPages > iTotalFreeRamPages)
-			{// Not enough free pages to fulfill this request so return amount required
-			return aNumPages - iTotalFreeRamPages;
-			}
-
 		// Determine if there are enough free pages in the RAM zones in use.
 		TUint totalFreeInUse = 0;
 		SDblQueLink* link = iZoneLeastMovDis;
@@ -1725,7 +1730,7 @@
 
 
 #if !defined(__MEMMODEL_MULTIPLE__) && !defined(__MEMMODEL_MOVING__)
-void DRamAllocator::BlockContiguousRegion(TPhysAddr aAddrBase, TUint aNumPages)
+TUint DRamAllocator::BlockContiguousRegion(TPhysAddr aAddrBase, TUint aNumPages)
 	{
 	// Shouldn't be asked to block zero pages, addrEndPage would be wrong if we did.
 	__NK_ASSERT_DEBUG(aNumPages);
@@ -1734,6 +1739,7 @@
 	TInt tmpOffset;
 	SZone* endZone = GetZoneAndOffset(addrEndPage, tmpOffset);
 	SZone* tmpZone;
+	TUint totalUnreserved = aNumPages;
 	do
 		{
 		tmpZone = GetZoneAndOffset(addr, tmpOffset);
@@ -1754,11 +1760,13 @@
 #endif
 			ZoneAllocPages(tmpZone, reserved, EPageFixed);
 			iTotalFreeRamPages -= reserved;
+			totalUnreserved -= reserved;
 			}
 		tmpZone->iBma[EPageFixed]->Alloc(tmpOffset, runLength);
 		addr = tmpZone->iPhysEnd + 1;
 		}
 	while (tmpZone != endZone);
+	return totalUnreserved;
 	}
 
 
@@ -1834,13 +1842,43 @@
 	}
 
 
-TBool DRamAllocator::ClearContiguousRegion(TPhysAddr aAddrBase, TPhysAddr aZoneBase, TUint aNumPages, TInt& aOffset)
+TUint DRamAllocator::CountPagesInRun(TPhysAddr aAddrBase, TPhysAddr aAddrEndPage, TZonePageType aType)
+	{
+	__NK_ASSERT_DEBUG(aAddrBase <= aAddrEndPage);
+	TUint totalAllocated = 0;
+	TPhysAddr addr = aAddrBase;
+	TUint tmpOffset;
+	SZone* endZone = GetZoneAndOffset(aAddrEndPage, (TInt&)tmpOffset);
+	SZone* tmpZone;
+	do
+		{
+		tmpZone = GetZoneAndOffset(addr, (TInt&)tmpOffset);
+		__NK_ASSERT_DEBUG(tmpZone != NULL);
+		TUint runLength = 	(aAddrEndPage < tmpZone->iPhysEnd)? 
+							((aAddrEndPage - addr) >> KPageShift) + 1: 
+							tmpZone->iPhysPages - tmpOffset;
+		TUint runEnd = tmpOffset + runLength - 1;
+		while (tmpOffset <= runEnd)
+			{
+			TUint run = NextAllocatedRun(tmpZone, tmpOffset, runEnd, aType);
+			totalAllocated += run;
+			tmpOffset += run;
+			}
+		addr = tmpZone->iPhysEnd + 1;
+		}
+	while (tmpZone != endZone);
+	return totalAllocated;
+	}
+
+
+TInt DRamAllocator::ClearContiguousRegion(TPhysAddr aAddrBase, TPhysAddr aZoneBase, TUint aNumPages, TInt& aOffset, TUint aUnreservedPages)
 	{
 	TPhysAddr addr = aAddrBase;
-	TPhysAddr addrEnd = aAddrBase + (aNumPages << KPageShift);
+	TPhysAddr addrEndPage = aAddrBase + ((aNumPages -1 )<< KPageShift);
 	TInt contigOffset = 0;
 	SZone* contigZone = GetZoneAndOffset(addr, contigOffset);
-	for (; addr != addrEnd; addr += KPageSize, contigOffset++)
+	TUint unreservedPages = aUnreservedPages;
+	for (; addr <= addrEndPage; addr += KPageSize, contigOffset++)
 		{
 		if (contigZone->iPhysEnd < addr)
 			{
@@ -1852,17 +1890,41 @@
 		__NK_ASSERT_DEBUG(contigZone->iBma[EPageFixed]->NotFree(contigOffset, 1));
 		__NK_ASSERT_DEBUG(SPageInfo::SafeFromPhysAddr(addr) != NULL);
 
-		// WARNING - This may flash the ram alloc mutex.
-		TInt exRet = M::MoveAndAllocPage(addr, EPageFixed);
-		if (exRet != KErrNone)
+		if (unreservedPages > iTotalFreeRamPages)
+			{// May need to discard some pages so there is free space for the 
+			// pages in the contiguous run to be moved to.
+			TUint requiredPages = unreservedPages - iTotalFreeRamPages;
+			if (requiredPages)
+				{// Ask the pager to get free some pages.
+				M::GetFreePages(requiredPages);
+
+				// The ram alloc lock may have been flashed so ensure that we still have
+				// enough free ram to complete the allocation.
+				TUint remainingPages = ((addrEndPage - addr) >> KPageShift) + 1;
+				unreservedPages = remainingPages - CountPagesInRun(addr, addrEndPage, EPageFixed);
+				if (unreservedPages > iTotalFreeRamPages + M::NumberOfFreeDpPages())
+					{// Not enough free space and not enough freeable pages.
+					return KErrNoMemory;
+					}
+				}
+			}
+
+		TInt r = M::MoveAndAllocPage(addr, EPageFixed);
+		if (r != KErrNone)
 			{// This page couldn't be moved or discarded so 
 			// restart the search the page after this one.
-			__KTRACE_OPT(KMMU2, Kern::Printf("ContigMov fail contigOffset 0x%x exRet %d", contigOffset, exRet));
+			__KTRACE_OPT(KMMU2, Kern::Printf("ContigMov fail contigOffset 0x%x r %d", contigOffset, r));
 			aOffset = (addr < aZoneBase)? 0 : contigOffset + 1;
-			break;
+			return r;
 			}
+		__NK_ASSERT_DEBUG(contigZone->iBma[EPageFixed]->NotFree(contigOffset, 1));
+		__NK_ASSERT_DEBUG(contigZone->iBma[KBmaAllPages]->NotFree(contigOffset, 1));
+		__NK_ASSERT_DEBUG(contigZone->iBma[EPageDiscard]->NotAllocated(contigOffset, 1));
+		__NK_ASSERT_DEBUG(contigZone->iBma[EPageMovable]->NotAllocated(contigOffset, 1));
 		}
-	return addr == addrEnd;
+
+	// Successfully cleared the contiguous run
+	return KErrNone;
 	}
 
 
@@ -1889,6 +1951,13 @@
 		{// Not enough free space and not enough freeable pages.
 		return KErrNoMemory;
 		}
+	if (aNumPages > iTotalFreeRamPages)
+		{// Need to discard some pages so there is free space for the pages in 
+		// the contiguous run to be moved to.
+		TUint requiredPages = aNumPages - iTotalFreeRamPages;
+		if (!M::GetFreePages(requiredPages))
+			return KErrNoMemory;
+		}
 
 	TInt alignWrtPage = Max(aAlign - KPageShift, 0);
 	TUint32 alignmask = (1u << alignWrtPage) - 1;
@@ -1950,8 +2019,9 @@
 				
 				// Block the contiguous region from being allocated.
 				iContiguousReserved++;
-				BlockContiguousRegion(addrBase, aNumPages);
-				if (ClearContiguousRegion(addrBase, zone->iPhysBase, aNumPages, offset))
+				TUint unreservedPages = BlockContiguousRegion(addrBase, aNumPages);
+				TInt clearRet = ClearContiguousRegion(addrBase, zone->iPhysBase, aNumPages, offset, unreservedPages);
+				if (clearRet == KErrNone)
 					{// Cleared all the required pages.
 					// Return address of physical page at the start of the region.
 					iContiguousReserved--;
@@ -1977,6 +2047,11 @@
 					carryImmov = 0;
 					carryAll = 0;
 					__KTRACE_OPT(KMMU2, Kern::Printf("<AllocContigfail run 0x%08x - 0x%08x 0x%x", addrBase, addrBase + (aNumPages << KPageShift), TheCurrentThread));
+					if (clearRet == KErrNoMemory)
+						{// There are no longer enough free or discardable pages to 
+						// be able to fulfill this allocation.
+						return KErrNoMemory;
+						}
 					}
 				}
 			}
@@ -2099,7 +2174,7 @@
 						__NK_ASSERT_DEBUG(SPageInfo::SafeFromPhysAddr(addr) != NULL);
 
 						TPhysAddr newAddr;
-						TInt moveRet = M::MovePage(addr, newAddr, contigZone->iId, EFalse);
+						TInt moveRet = M::MovePage(addr, newAddr, contigZone->iId, 0);
 						if (moveRet != KErrNone && moveRet != KErrNotFound)
 							{// This page couldn't be moved or discarded so 
 							// restart the search the page after this one.
@@ -2462,8 +2537,7 @@
 				page in the zone should be found, on return it will be the offset 
 				of the next allocated page.
 @param aEndOffset The last offset within this RAM zone to check for allocated runs.
-@return The length of any run found, KErrNotFound if no more pages in
-the zone after aOffset are allocated, KErrArgument if aOffset is outside the zone.
+@return The length of any run found.
 */
 TInt DRamAllocator::NextAllocatedRun(SZone* aZone, TUint& aOffset, TUint aEndOffset, TZonePageType aType) const
 	{