kernel/eka/memmodel/epoc/flexible/mmu/mpager.cpp
branchRCL_3
changeset 36 bbf8bed59bcb
parent 28 5b5d147c7838
child 39 2bb754abd467
--- a/kernel/eka/memmodel/epoc/flexible/mmu/mpager.cpp	Tue May 25 14:09:55 2010 +0300
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpager.cpp	Wed Jun 09 11:10:19 2010 +0300
@@ -498,56 +498,86 @@
 	}
 
 
-TInt DPager::TryStealOldestPage(SPageInfo*& aPageInfoOut)
+SPageInfo* DPager::StealOrAllocPage(TBool aAllowAlloc, Mmu::TRamAllocFlags aAllocFlags)
 	{
 	__NK_ASSERT_DEBUG(RamAllocLock::IsHeld());
 	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
-	
-	// The PageCleaningLock may or may not be held.  This method will release the RamAllocLock if it
-	// has to wait for the PageCleaningLock
+
+	// The PageCleaningLock may or may not be held to start with
 	TBool pageCleaningLockAcquired = EFalse;
 
-	// find oldest page in list...
 	SDblQueLink* link;
+	SPageInfo* pageInfo ;
+	
+restart:
+
+	// if there is a free page in the live list then use that (it will be at the end)...
+	if (iOldestCleanCount)
+		{
+		__NK_ASSERT_DEBUG(!iOldestCleanList.IsEmpty());
+		link = iOldestCleanList.Last();
+		pageInfo = SPageInfo::FromLink(link);
+		if(pageInfo->Type()==SPageInfo::EUnused)
+			goto try_steal_from_page_info;
+		}
+	
+	// maybe try getting a free page from the system pool...
+	if (aAllowAlloc && !HaveMaximumPages())
+		{
+		MmuLock::Unlock();
+		pageInfo = GetPageFromSystem(aAllocFlags);
+		MmuLock::Lock();
+		if (pageInfo)
+			goto exit;
+		}
+	
+	// try stealing the oldest clean page on the  live list if there is one...
 	if (iOldestCleanCount)
 		{
 		__NK_ASSERT_DEBUG(!iOldestCleanList.IsEmpty());
 		link = iOldestCleanList.Last();
+		goto try_steal_from_link;
 		}
-	else if (iOldestDirtyCount)
+
+	// no clean oldest pages, see if we can clean multiple dirty pages in one go...
+	if (iOldestDirtyCount > 1 && iPagesToClean > 1)
 		{
 		__NK_ASSERT_DEBUG(!iOldestDirtyList.IsEmpty());
 
-		// see if we can clean multiple dirty pages in one go...
-		if (iPagesToClean > 1 && iOldestDirtyCount > 1)
+		// check if we hold page cleaning lock
+		TBool needPageCleaningLock = !PageCleaningLock::IsHeld();
+		if (needPageCleaningLock)
 			{
-			if (!PageCleaningLock::IsHeld())
-				{
-				// temporarily release ram alloc mutex and acquire page cleaning mutex
-				MmuLock::Unlock();
-				RamAllocLock::Unlock();
-				PageCleaningLock::Lock();
-				MmuLock::Lock();
-				pageCleaningLockAcquired = ETrue;
-				}
-
-			// there may be clean pages now if we've waited on the page cleaning mutex, if so don't
-			// bother cleaning but just restart
-			if (iOldestCleanCount == 0 && iOldestDirtyCount >= 1)
-				CleanSomePages(EFalse);
-
-			if (pageCleaningLockAcquired)
-				{
-				// release page cleaning mutex and re-aquire ram alloc mutex
-				MmuLock::Unlock();
-				PageCleaningLock::Unlock();			
-				RamAllocLock::Lock();
-				MmuLock::Lock();
-				}
-			
-			return 1;  // tell caller to restart their operation
+			// temporarily release ram alloc mutex and acquire page cleaning mutex
+			MmuLock::Unlock();
+			RamAllocLock::Unlock();
+			PageCleaningLock::Lock();
+			MmuLock::Lock();
 			}
-		
+
+		// there may be clean pages now if we've waited on the page cleaning mutex, if so don't
+		// bother cleaning but just restart
+		if (iOldestCleanCount == 0 && iOldestDirtyCount >= 1)
+			CleanSomePages(EFalse);
+
+		if (needPageCleaningLock)
+			{
+			// release page cleaning mutex and re-aquire ram alloc mutex
+			MmuLock::Unlock();
+			PageCleaningLock::Unlock();			
+			RamAllocLock::Lock();
+			MmuLock::Lock();
+			}
+
+		// if there are now some clean pages we restart so as to take one of them
+		if (iOldestCleanCount > 0)
+			goto restart;
+		}
+
+	// otherwise just try to steal the oldest page...
+	if (iOldestDirtyCount)
+		{
+		__NK_ASSERT_DEBUG(!iOldestDirtyList.IsEmpty());
 		link = iOldestDirtyList.Last();
 		}
 	else if (iOldCount)
@@ -561,32 +591,44 @@
 		__NK_ASSERT_ALWAYS(!iYoungList.IsEmpty());
 		link = iYoungList.Last();
 		}
-	SPageInfo* pageInfo = SPageInfo::FromLink(link);
-
-	if (pageInfo->IsDirty())
+
+try_steal_from_link:
+
+	// lookup page info
+	__NK_ASSERT_DEBUG(link);
+	pageInfo = SPageInfo::FromLink(link);
+	
+try_steal_from_page_info:
+	
+	// if the page is dirty and we don't hold the page cleaning mutex then we have to wait on it,
+	// and restart - we clean with the ram alloc mutex held in this case
+	if (pageInfo->IsDirty() && !PageCleaningLock::IsHeld())
 		{		
 		MmuLock::Unlock();
 		PageCleaningLock::Lock();
 		MmuLock::Lock();
 		pageCleaningLockAcquired = ETrue;
+		goto restart;
 		}
 	
 	// try to steal it from owning object...
-	TInt r = StealPage(pageInfo);	
-	if (r == KErrNone)
-		{
-		BalanceAges();
-		aPageInfoOut = pageInfo;
-		}
-
+	if (StealPage(pageInfo) != KErrNone)
+		goto restart;
+	
+	BalanceAges();
+	
+exit:
 	if (pageCleaningLockAcquired)
 		{		
 		MmuLock::Unlock();
 		PageCleaningLock::Unlock();
 		MmuLock::Lock();
 		}
+
+	__NK_ASSERT_DEBUG(RamAllocLock::IsHeld());
+	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
 	
-	return r;
+	return pageInfo;
 	}
 
 
@@ -1050,20 +1092,20 @@
 	__NK_ASSERT_DEBUG(MmuLock::IsHeld());
 	__NK_ASSERT_DEBUG(iNumberOfFreePages>0);
 
-	SPageInfo* pageInfo = NULL;
-	if (TryStealOldestPage(pageInfo) == KErrNone)
+	SPageInfo* pageInfo = StealOrAllocPage(EFalse, (Mmu::TRamAllocFlags)0);
+	
+	// StealOrAllocPage may have released the MmuLock, so check there are still enough pages
+	// to remove one from the live list
+	if (iNumberOfFreePages>0)
 		{
-		// TryStealOldestPage may have released the MmuLock, so check there are still enough pages
-		// to remove one from the live list
-		if (iNumberOfFreePages>0)
-			{
-			ReturnPageToSystem(*pageInfo);
-			return ETrue;
-			}
-		else
-			AddAsFreePage(pageInfo);
+		ReturnPageToSystem(*pageInfo);
+		return ETrue;
 		}
-	return EFalse;
+	else
+		{
+		AddAsFreePage(pageInfo);
+		return EFalse;
+		}
 	}
 
 
@@ -1092,51 +1134,23 @@
 
 SPageInfo* DPager::PageInAllocPage(Mmu::TRamAllocFlags aAllocFlags)
 	{
-	SPageInfo* pageInfo;
-	TPhysAddr pagePhys;
-	TInt r = KErrGeneral;
+	// ram alloc mutex may or may not be held
+	__NK_ASSERT_DEBUG(!MmuLock::IsHeld());
 	
 	RamAllocLock::Lock();
-	MmuLock::Lock();
-
-find_a_page:
-	// try getting a free page from our live list...
-	if (iOldestCleanCount)
+	
+	MmuLock::Lock();	
+	SPageInfo* pageInfo = StealOrAllocPage(ETrue, aAllocFlags);
+	TBool wasAllocated = pageInfo->Type() == SPageInfo::EUnknown;
+	MmuLock::Unlock();
+
+	if (!wasAllocated)
 		{
-		pageInfo = SPageInfo::FromLink(iOldestCleanList.Last());
-		if(pageInfo->Type()==SPageInfo::EUnused)
-			goto try_steal_oldest_page;
-		}
-
-	// try getting a free page from the system pool...
-	if(!HaveMaximumPages())
-		{
-		MmuLock::Unlock();
-		pageInfo = GetPageFromSystem(aAllocFlags);
-		if(pageInfo)
-			goto done;
-		MmuLock::Lock();
+		// make page state same as a freshly allocated page...
+		TPhysAddr pagePhys = pageInfo->PhysAddr();
+		TheMmu.PagesAllocated(&pagePhys,1,aAllocFlags);
 		}
 
-	// otherwise steal a page from the live list...
-try_steal_oldest_page:
-	__NK_ASSERT_ALWAYS(iOldestCleanCount|iOldestDirtyCount|iOldCount|iYoungCount);
-	r = TryStealOldestPage(pageInfo);
-	
-	// if this fails we restart whole process.
-	// failure can be either KErrInUse if the page was used while we were stealing, or 1 to indicate
-	// that some pages were cleaned and the operation should be restarted
-	if (r != KErrNone)
-		goto find_a_page;
-
-	// otherwise we're done!
-	MmuLock::Unlock();
-
-	// make page state same as a freshly allocated page...
-	pagePhys = pageInfo->PhysAddr();
-	TheMmu.PagesAllocated(&pagePhys,1,aAllocFlags);
-
-done:
 	RamAllocLock::Unlock();
 
 	return pageInfo;
@@ -2046,9 +2060,9 @@
 	NKern::ThreadEnterCS();
 	RamAllocLock::Lock();
 
-	// We must hold this otherwise TryStealOldestPage will release the RamAllocLock while waiting
-	// for it.  Note this method is not used in producton, so it's ok to hold both locks for longer
-	// than would otherwise happen.
+	// We must hold this otherwise StealOrAllocPage will release the RamAllocLock while waiting for
+	// it.  Note this method is not used in producton, so it's ok to hold both locks for longer than
+	// would otherwise happen.
 	PageCleaningLock::Lock();  
 
 	MmuLock::Lock();