kerneltest/e32test/demandpaging/t_pagetable_limit.cpp
branchRCL_3
changeset 44 3e88ff8f41d5
parent 43 c1f20ce4abcf
--- a/kerneltest/e32test/demandpaging/t_pagetable_limit.cpp	Tue Aug 31 16:34:26 2010 +0300
+++ b/kerneltest/e32test/demandpaging/t_pagetable_limit.cpp	Wed Sep 01 12:34:56 2010 +0100
@@ -33,24 +33,9 @@
 #include <hal.h>
 
 #include "t_dpcmn.h"
-#include "../mmu/freeram.h"
 
 RTest test(_L("T_PAGETABLE_LIMIT"));
 
-// The flexible memory model reserves 0xF800000-0xFFF00000 for page tables, which allows 130,048
-// pages tables.
-//
-// We attempt to map 40 * 40 * 100 == 160,000 page tables.
-// 
-// So that the limit is reached in the middle, half the chunks are mapped as paged and half as
-// unpaged.
-
-const TUint KPageTablesPerChunk = 40;
-const TUint KChunksPerProcess = 40;
-const TUint KNumProcesses = 100;
-
-const TUint KSizeMappedByPageTable = 1024 * 1024;  // the amount of RAM mapped by one page table
-
 
 _LIT(KClientPtServerName, "CClientPtServer");
 _LIT(KClientProcessName, "T_PAGETABLE_LIMIT");
@@ -61,7 +46,6 @@
 	EClientDisconnect = -2,
 	EClientGetChunk = 0,
 	EClientReadChunks = 1,
-	EClientGetParentProcess = 2,
 	};
 
 class RDataPagingSession : public RSessionBase
@@ -73,244 +57,176 @@
 		}
 	TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
 		{
-		return SendReceive(aFunction, aPtr);
+		return (SendReceive(aFunction, aPtr));
+		}
+	TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr)
+		{
+		return (Send(aFunction, aPtr));
 		}
 	};
 
-#define CLIENT_TEST_IMPL(condition, code, line)			\
-	if (!(condition))									\
-		{												\
-		RDebug::Printf("Test %s failed at line %d");	\
-		r = (code);										\
-		goto exit;										\
-		}
-
-#define CLIENT_TEST(condition, code) CLIENT_TEST_IMPL(condition, code, __LINE__)
 
 TInt ClientProcess(TInt aLen)
 	{
-	// Read the command line to get the number of chunk to map and whether or not to access their
-	// data.
+	// Read the command line to get the number of chunk to map and whether or 
+	// not to access their data.
 	HBufC* buf = HBufC::New(aLen);
 	test(buf != NULL);
 	TPtr ptr = buf->Des();
 	User::CommandLine(ptr);
+
 	TLex lex(ptr);
-
-	RChunk* chunks = NULL;
-	RDataPagingSession session;
-	RProcess parent;
-	TRequestStatus parentStatus;
-	TInt offset = 0;
-	TInt i;
-	
 	TInt chunkCount;
 	TInt r = lex.Val(chunkCount);
-	CLIENT_TEST(r == KErrNone, r);	
+	test_KErrNone(r);
 	lex.SkipSpace();
-	chunks = new RChunk[chunkCount];
-	CLIENT_TEST(chunks, KErrNoMemory);
 
 	TBool accessData;
 	r = lex.Val(accessData);
-	CLIENT_TEST(r == KErrNone, r);
+	test_KErrNone(r);
 
-	r = session.CreateSession(KClientPtServerName, 1);
-	CLIENT_TEST(r == KErrNone, r);
 
-	r = parent.SetReturnedHandle(session.PublicSendReceive(EClientGetParentProcess, TIpcArgs()));
-	CLIENT_TEST(r == KErrNone, r);
-	
-	for (i = 0; i < chunkCount; i++)
+	RDataPagingSession session;
+	test_KErrNone(session.CreateSession(KClientPtServerName, 1));
+
+	RChunk* chunks = new RChunk[chunkCount];
+	for (TInt i = 0; i < chunkCount; i++)
 		{
-		r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i)));
+		TInt r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i)));
 		if (r != KErrNone)
 			{
-			RDebug::Printf("Failed to create a handle to chunk %d r=%d", i, r);
-			goto exit;
+			test.Printf(_L("Failed to create a handle to the server's chunk r=%d\n"), r);
+			for (TInt j = 0; j < i; j++)
+				chunks[j].Close();
+			session.Close();
+			return r;
 			}
-		CLIENT_TEST(chunks[i].Size() >= gPageSize, KErrGeneral);
+		test_Value(chunks[i].Size(), chunks[i].Size() >= gPageSize);
 		}
-
-	// Logon to parent process
-	parent.Logon(parentStatus);
-
-	// Touch each mapped page of all of the chunks.
-	do
+	if (!accessData)
 		{
+		// Touch the 1st page of each of the chunks.
 		for (TInt i = 0; i < chunkCount; i++)
 			{
-			for (TUint j = 0 ; j < KPageTablesPerChunk ; j++)
+			// Write the chunk data from top to bottom of the chunk's first page.
+			TUint8* base = chunks[i].Base();
+			TUint8* end = base + gPageSize - 1;
+			*base = *end;
+			}
+		// Tell parent we've touched each chunk.
+		TInt r =  (TThreadId)session.PublicSendReceive(EClientReadChunks,TIpcArgs());	// Assumes id is only 32-bit.
+		test_KErrNone(r);
+		for(;;)
+			{// Wake up every 100ms to be killed by the main process.
+			User::After(100000);
+			}
+		}
+	else
+		{
+		for (;;)
+			{
+			TInt offset = 0;
+			for (TInt i = 0; i < chunkCount; i++)
 				{
 				// Write the chunk data from top to bottom of the chunk's first page.
-				TUint8* base = chunks[i].Base() + j * KSizeMappedByPageTable;
+				TUint8* base = chunks[i].Base();
 				TUint8* end = base + gPageSize - 1;
 				*(base + offset) = *(end - offset);
-
-				User::After(0);
-
-				// Check whether main process is still running
-				if (parentStatus != KRequestPending)
-					{
-					// if we get here the test failed and the main process exited without killing
-					// us, so just exit quietly
-					User::WaitForRequest(parentStatus);
-					r = KErrGeneral;
-					goto exit;
-					}
 				}
+			if (++offset >= (gPageSize >> 1))
+				offset = 0;
 			}
-		offset = (offset + 1) % (gPageSize / 2);
 		}
-	while (accessData);
-
-	// Tell parent we've touched each page.
-	r = (TThreadId)session.PublicSendReceive(EClientReadChunks,TIpcArgs());	
-	CLIENT_TEST(r == KErrNone, r);
-		
-	// Wait till we get killed by the main process or the main process dies
-	User::WaitForRequest(parentStatus);
-	// if we get here the test failed and the main process exited without killing us, so just exit
-	r = KErrGeneral;
-
-exit:
-	if (chunks)
-		{
-		for (TInt i = 0 ; i < chunkCount; ++i)
-			chunks[i].Close();
-		}
-	session.Close();
-	parent.Close();
-			
-	return r;
 	}
 
-TInt FreeSwap()
-	{
-	SVMSwapInfo swapInfo;
-	test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo, 0));
-	return swapInfo.iSwapFree;
-	}
-
-void PrintFreeRam()
-	{
-	test.Printf(_L("%d KB RAM / %d KB swap free\n"), FreeRam() / 1024, FreeSwap() / 1024);
-	}
-
-void CreateChunk(RChunk& aChunk, TInt aChunkIndex, TInt aPageTables, TBool aPaged)
-	{
-	// Creates a global chunk.
-	//
-	// The chunk uses KPageTablesPerChunk page tables by committing that number of pages at 1MB
-	// intervals.  Its max size is set so that it is not a multiple of 1MB and hence the FMM will
-	// use a fine mapping objects whose page tables are not shared between processes.
-	
-	test.Printf(_L("  creating chunk %d: "), aChunkIndex);
-	PrintFreeRam();
-	
-	TChunkCreateInfo createInfo;
-	createInfo.SetDisconnected(0, 0, aPageTables * KSizeMappedByPageTable + gPageSize);
-	createInfo.SetPaging(aPaged ? TChunkCreateInfo::EPaged : TChunkCreateInfo::EUnpaged);
-	TBuf<32> name;
-	name.AppendFormat(_L("t_pagetable_limit chunk %d"), aChunkIndex);
-	createInfo.SetGlobal(name);
-	test_KErrNone(aChunk.Create(createInfo));
-	for (TInt i = 0 ; i < aPageTables ; ++i)
-		test_KErrNone(aChunk.Commit(i * KSizeMappedByPageTable, gPageSize));
-	}
-
-void CreateProcess(RProcess& aProcess, TInt aProcessIndex, TInt aNumChunks, TBool aAccessData)
-	{
-	test.Printf(_L("  creating process %d: "), aProcessIndex);
-	PrintFreeRam();
-	
-	TBuf<80> args;
-	args.AppendFormat(_L("%d %d"), aNumChunks, aAccessData);
-	test_KErrNone(aProcess.Create(KClientProcessName, args));
-	aProcess.SetPriority(EPriorityLow);
-	}
 
 void TestMaxPt()
 	{
-	test.Printf(_L("Waiting for system idle and kernel cleanup\n"));
-	// If this test was run previously, there may be lots of dead processes waiting to be cleaned up
-	TInt r;
-	while((r = FreeRam(10 * 1000)), r == KErrTimedOut)
-		{
-		test.Printf(_L("  waiting: "));
-		PrintFreeRam();
-		}
-
-	// Remove the maximum limit on the cache size as the test requires that it can
-	// allocate as many page tables as possible but without stealing any pages as
-	// stealing pages may indirectly steal paged page table pages.
-	test.Printf(_L("Set paging cache max size unlimited\n"));
-	TUint minCacheSize, maxCacheSize, currentCacheSize;
-	test_KErrNone(DPTest::CacheSize(minCacheSize,maxCacheSize,currentCacheSize));
-	test_KErrNone(DPTest::SetCacheSize(minCacheSize, KMaxTUint));
-
+	// Flexible memory model reserves 0xF800000-0xFFF00000 for page tables
+	// this allows 130,048 pages tables.  Therefore mapping 1000 one 
+	// page chunks into 256 processes would require 256,000 page tables, i.e.
+	// more than enough to hit the limit.  So that the limit is reached in the middle,
+	// map 500 unpaged and 500 paged chunks in each process.
+	const TUint KNumChunks = 1000;
+	const TUint KPagedChunksStart = (KNumChunks >> 1);
+	const TUint KNumProcesses = 256;
 	const TInt KMinFreeRam = (1000 * gPageSize) + (130048 * (gPageSize>>2));
-
-	// Ensure enough RAM available
-	PrintFreeRam();
-	TInt freeRam = FreeRam();
+	TInt freeRam;
+	HAL::Get(HALData::EMemoryRAMFree, freeRam);
 	if (freeRam < KMinFreeRam)
 		{
 		test.Printf(_L("Only 0x%x bytes of free RAM not enough to perform the test.  Skipping test.\n"), freeRam);
 		return;
 		}
 
-	test.Printf(_L("Start server\n"));
+	// Remove the maximum limit on the cache size as the test requires that it can
+	// allocate as many page tables as possible but without stealing any pages as
+	// stealing pages may indirectly steal paged page table pages.
+	TUint minCacheSize, maxCacheSize, currentCacheSize;
+	DPTest::CacheSize(minCacheSize,maxCacheSize,currentCacheSize);
+	test_KErrNone(DPTest::SetCacheSize(minCacheSize, KMaxTUint));
+
 	RServer2 ptServer;
-	r = ptServer.CreateGlobal(KClientPtServerName);
+	TInt r = ptServer.CreateGlobal(KClientPtServerName);
 	test_KErrNone(r);
 
-	test.Printf(_L("Create chunks\n"));
-	const TUint KPagedChunksStart = (KChunksPerProcess >> 1);
-	RChunk* chunks = new RChunk[KChunksPerProcess];
-	test_NotNull(chunks);
+	// Create the global unpaged chunks.  They have one page committed
+	// but have a maximum size large enough to prevent their page tables being
+	// shared between the chunks.  On arm with 4KB pages each page table maps 1MB
+	// so make chunk 1MB+4KB so chunk requires 2 page tables and is not aligned on
+	// a 1MB boundary so it is a fine memory object.
+	const TUint KChunkSize = (1024 * 1024) + gPageSize;
+	RChunk* chunks = new RChunk[KNumChunks];
+	TChunkCreateInfo createInfo;
+	createInfo.SetNormal(gPageSize, KChunkSize);
+	createInfo.SetGlobal(KNullDesC);
+	createInfo.SetPaging(TChunkCreateInfo::EUnpaged);
 	TUint i = 0;
-	for (i = 0 ; i< KChunksPerProcess; i++)
-		CreateChunk(chunks[i], i, KPageTablesPerChunk, i >= KPagedChunksStart);
+	for (; i < KPagedChunksStart; i++)
+		{
+		r = chunks[i].Create(createInfo);
+		test_KErrNone(r);
+		}
+	// Create paged chunks.
+	createInfo.SetPaging(TChunkCreateInfo::EPaged);
+	for (; i< KNumChunks; i++)
+		{
+		r = chunks[i].Create(createInfo);
+		test_KErrNone(r);
+		}
 
 	// Start remote processes, giving each process handles to each chunk.
-	test.Printf(_L("Start remote processes\n"));
 	RProcess* processes = new RProcess[KNumProcesses];
-	test_NotNull(processes);
-	TRequestStatus* statuses = new TRequestStatus[KNumProcesses];
-	test_NotNull(statuses);
-	TUint processIndex = 0;	
+	RMessage2 ptMessage;
+	TUint processIndex = 0;
+	TUint processLimit = 0;
 	for (; processIndex < KNumProcesses; processIndex++)
 		{
 		// Start the process.
-		CreateProcess(processes[processIndex], processIndex, KChunksPerProcess, EFalse);
-		
-		// logon to process
-		processes[processIndex].Logon(statuses[processIndex]);
-		test_Equal(KRequestPending, statuses[processIndex].Int());
+		test.Printf(_L("Creating process %d\n"), processIndex);
+		TBuf<80> args;
+		args.AppendFormat(_L("%d %d"), KNumChunks, EFalse);
+		r = processes[processIndex].Create(KClientProcessName, args);
+		test_KErrNone(r);
+		TRequestStatus s;
+		processes[processIndex].Logon(s);
+		test_Equal(KRequestPending, s.Int());
 		processes[processIndex].Resume();
 
-		// wait for connect message
-		RMessage2 ptMessage;
 		ptServer.Receive(ptMessage);
 		test_Equal(EClientConnect, ptMessage.Function());
 		ptMessage.Complete(KErrNone);
-
-		// pass client a handle to this process
-		ptServer.Receive(ptMessage);
-		test_Equal(EClientGetParentProcess, ptMessage.Function());
-		ptMessage.Complete(RProcess());
-
-		// pass client chunk handles
-		TInt func;
+		TInt func = EClientGetChunk;
 		TUint chunkIndex = 0;
-		for (; chunkIndex < KChunksPerProcess ; chunkIndex++)
+		for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++)
 			{// Pass handles to all the unpaged chunks to the new process.
 			ptServer.Receive(ptMessage);
 			func = ptMessage.Function();
-			if (func != EClientGetChunk)
-				break;
-			ptMessage.Complete(chunks[ptMessage.Int0()]);
+			if (func == EClientGetChunk)
+				{
+				TUint index = ptMessage.Int0();
+				ptMessage.Complete(chunks[index]);
+				}
 			}
 		if (func != EClientGetChunk)
 			{
@@ -318,10 +234,9 @@
 			// sending a disconnect message in the process.
 			test_Equal(EClientDisconnect, func);
 			// Should only fail when mapping unpaged chunks.
-			test_Value(chunkIndex, chunkIndex < (KChunksPerProcess >> 1));
+			test_Value(chunkIndex, chunkIndex < (KNumChunks >> 1));
 			break;
 			}
-		
 		// Wait for the process to access all the chunks and therefore 
 		// allocate the paged page tables before moving onto the next process.
 		ptServer.Receive(ptMessage);
@@ -330,44 +245,45 @@
 		ptMessage.Complete(KErrNone);
 
 		// Should have mapped all the required chunks.
-		test_Equal(KChunksPerProcess, chunkIndex);
+		test_Equal(KNumChunks, chunkIndex);
 		}
-	
 	// Should hit page table limit before KNumProcesses have been created.
 	test_Value(processIndex, processIndex < KNumProcesses - 1);
-	TUint processLimit = processIndex;
+	processLimit = processIndex;
 
-	// Now create more processes to access paged data even though the page table address space has
-	// been exhausted.  Limit to 10 more processes as test takes long enough already.
-	test.Printf(_L("Start accessor processes\n"));
+	// Now create more processes to access paged data even though the page table 
+	// address space has been exhausted.  Limit to 10 more processes as test takes 
+	// long enough already.
 	processIndex++;
 	TUint excessProcesses = KNumProcesses - processIndex;
 	TUint pagedIndexEnd = (excessProcesses > 10)? processIndex + 10 : processIndex + excessProcesses;
 	for (; processIndex < pagedIndexEnd; processIndex++)
 		{
-		// start the process.
-		CreateProcess(processes[processIndex], processIndex, KChunksPerProcess-KPagedChunksStart, ETrue);
-
-		// logon to process
-		processes[processIndex].Logon(statuses[processIndex]);
-		test_Equal(KRequestPending, statuses[processIndex].Int());
+		// Start the process.
+		test.Printf(_L("Creating process %d\n"), processIndex);
+		TBuf<80> args;
+		args.AppendFormat(_L("%d %d"), KNumChunks-KPagedChunksStart, ETrue);
+		r = processes[processIndex].Create(KClientProcessName, args);
+		if (r != KErrNone)
+			{// Have hit the limit of processes.
+			processIndex--;
+			// Should have created at least one more process.
+			test_Value(processIndex, processIndex > processLimit);
+			break;
+			}
+		TRequestStatus s;
+		processes[processIndex].Logon(s);
+		test_Equal(KRequestPending, s.Int());
 		processes[processIndex].Resume();
 
-		// wait for connect message
-		RMessage2 ptMessage;
 		ptServer.Receive(ptMessage);
 		test_Equal(EClientConnect, ptMessage.Function());
 		ptMessage.Complete(KErrNone);
 
-		// pass client a handle to this process
-		ptServer.Receive(ptMessage);
-		test_Equal(EClientGetParentProcess, ptMessage.Function());
-		ptMessage.Complete(RProcess());
-		
 		TInt func = EClientGetChunk;
 		TUint chunkIndex = KPagedChunksStart;
-		for (; chunkIndex < KChunksPerProcess && func == EClientGetChunk; chunkIndex++)
-			{// Pass handles to all the paged chunks to the new process.
+		for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++)
+			{// Pass handles to all the unpaged chunks to the new process.
 			ptServer.Receive(ptMessage);
 			func = ptMessage.Function();
 			if (func == EClientGetChunk)
@@ -385,36 +301,30 @@
 			}
 
 		// Should have mapped all the required chunks.
-		test_Equal(KChunksPerProcess, chunkIndex);
+		test_Equal(KNumChunks, chunkIndex);
 		}
-	
 	// If we reached the end of then ensure that we kill only the running processes.
 	if (processIndex == pagedIndexEnd)
 		processIndex--;
-
-	// Let the accessor processes run awhile
-	test.Printf(_L("Waiting...\n"));
-	User::After(10 * 1000000);
-	
 	// Kill all the remote processes
-	test.Printf(_L("Killing processes...\n"));
 	for(TInt j = processIndex; j >= 0; j--)
 		{
-		test.Printf(_L("  killing process %d\n"), j);
-		if (statuses[j] == KRequestPending)
+		test.Printf(_L("killing process %d\n"), j);
+		TRequestStatus req;
+		processes[j].Logon(req);
+		if (req == KRequestPending)
+			{
 			processes[j].Kill(KErrNone);
+			User::WaitForRequest(req);
+			}
 		processes[j].Close();
-		User::WaitForRequest(statuses[j]);
 		}
-	delete [] processes;
-	delete [] statuses;
-	
+	delete[] processes;
 	// Close the chunks.
-	for (TUint k = 0; k < KChunksPerProcess; k++)
+	for (TUint k = 0; k < KNumChunks; k++)
 		chunks[k].Close();
 	delete[] chunks;
-
-	// Reset live list size
+	
 	test_KErrNone(DPTest::SetCacheSize(minCacheSize, maxCacheSize));
 	}