kerneltest/e32test/demandpaging/t_pagetable_limit.cpp
changeset 44 36bfc973b146
child 231 75252ea6123b
equal deleted inserted replaced
43:96e5fb8b040d 44:36bfc973b146
       
     1 // Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of the License "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // e32test\demandpaging\t_pagetable_limit.cpp
       
    15 // Tests to expose the limit of page table virtual address space.
       
    16 // 
       
    17 //
       
    18 
       
    19 //! @SYMTestCaseID			KBASE-T_PAGETABLE_LIMIT
       
    20 //! @SYMTestType			UT
       
    21 //! @SYMPREQ				PREQ1490
       
    22 //! @SYMTestCaseDesc		Tests to expose the limit of page table virtual address space.
       
    23 //! @SYMTestActions			Test that a paged page table can always be acquired.
       
    24 //! @SYMTestExpectedResults All tests should pass.
       
    25 //! @SYMTestPriority        High
       
    26 //! @SYMTestStatus          Implemented
       
    27 
       
    28 #define __E32TEST_EXTENSION__
       
    29 #include <e32test.h>
       
    30 #include <dptest.h>
       
    31 #include <e32svr.h>
       
    32 #include <u32std.h>
       
    33 #include <hal.h>
       
    34 
       
    35 #include "t_dpcmn.h"
       
    36 
       
    37 RTest test(_L("T_PAGETABLE_LIMIT"));
       
    38 
       
    39 
       
    40 _LIT(KClientPtServerName, "CClientPtServer");
       
    41 _LIT(KClientProcessName, "T_PAGETABLE_LIMIT");
       
    42 
       
    43 enum TClientMsgType
       
    44 	{
       
    45 	EClientConnect = -1,
       
    46 	EClientDisconnect = -2,
       
    47 	EClientGetChunk = 0,
       
    48 	EClientReadChunks = 1,
       
    49 	};
       
    50 
       
    51 class RDataPagingSession : public RSessionBase
       
    52 	{
       
    53 public:
       
    54 	TInt CreateSession(const TDesC& aServerName, TInt aMsgSlots) 
       
    55 		{ 
       
    56 		return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots);
       
    57 		}
       
    58 	TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
       
    59 		{
       
    60 		return (SendReceive(aFunction, aPtr));
       
    61 		}
       
    62 	TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr)
       
    63 		{
       
    64 		return (Send(aFunction, aPtr));
       
    65 		}
       
    66 	};
       
    67 
       
    68 
       
    69 TInt ClientProcess(TInt aLen)
       
    70 	{
       
    71 	// Read the command line to get the number of chunk to map and whether or 
       
    72 	// not to access their data.
       
    73 	HBufC* buf = HBufC::New(aLen);
       
    74 	test(buf != NULL);
       
    75 	TPtr ptr = buf->Des();
       
    76 	User::CommandLine(ptr);
       
    77 
       
    78 	TLex lex(ptr);
       
    79 	TInt chunkCount;
       
    80 	TInt r = lex.Val(chunkCount);
       
    81 	test_KErrNone(r);
       
    82 	lex.SkipSpace();
       
    83 
       
    84 	TBool accessData;
       
    85 	r = lex.Val(accessData);
       
    86 	test_KErrNone(r);
       
    87 
       
    88 
       
    89 	RDataPagingSession session;
       
    90 	test_KErrNone(session.CreateSession(KClientPtServerName, 1));
       
    91 
       
    92 	RChunk* chunks = new RChunk[chunkCount];
       
    93 	for (TInt i = 0; i < chunkCount; i++)
       
    94 		{
       
    95 		TInt r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i)));
       
    96 		if (r != KErrNone)
       
    97 			{
       
    98 			test.Printf(_L("Failed to create a handle to the server's chunk r=%d\n"), r);
       
    99 			for (TInt j = 0; j < i; j++)
       
   100 				chunks[j].Close();
       
   101 			session.Close();
       
   102 			return r;
       
   103 			}
       
   104 		test_Value(chunks[i].Size(), chunks[i].Size() >= gPageSize);
       
   105 		}
       
   106 	if (!accessData)
       
   107 		{
       
   108 		// Touch the 1st page of each of the chunks.
       
   109 		for (TInt i = 0; i < chunkCount; i++)
       
   110 			{
       
   111 			// Write the chunk data from top to bottom of the chunk's first page.
       
   112 			TUint8* base = chunks[i].Base();
       
   113 			TUint8* end = base + gPageSize - 1;
       
   114 			*base = *end;
       
   115 			}
       
   116 		// Tell parent we've touched each chunk.
       
   117 		TInt r =  (TThreadId)session.PublicSendReceive(EClientReadChunks,TIpcArgs());	// Assumes id is only 32-bit.
       
   118 		test_KErrNone(r);
       
   119 		for(;;)
       
   120 			{// Wake up every 100ms to be killed by the main process.
       
   121 			User::After(100000);
       
   122 			}
       
   123 		}
       
   124 	else
       
   125 		{
       
   126 		for (;;)
       
   127 			{
       
   128 			TInt offset = 0;
       
   129 			for (TInt i = 0; i < chunkCount; i++)
       
   130 				{
       
   131 				// Write the chunk data from top to bottom of the chunk's first page.
       
   132 				TUint8* base = chunks[i].Base();
       
   133 				TUint8* end = base + gPageSize - 1;
       
   134 				*(base + offset) = *(end - offset);
       
   135 				}
       
   136 			if (++offset >= (gPageSize >> 1))
       
   137 				offset = 0;
       
   138 			}
       
   139 		}
       
   140 	}
       
   141 
       
   142 
       
   143 void TestMaxPt()
       
   144 	{
       
   145 	// Flexible memory model reserves 0xF800000-0xFFF00000 for page tables
       
   146 	// this allows 130,048 pages tables.  Therefore mapping 1000 one 
       
   147 	// page chunks into 256 processes would require 256,000 page tables, i.e.
       
   148 	// more than enough to hit the limit.  So that the limit is reached in the middle,
       
   149 	// map 500 unpaged and 500 paged chunks in each process.
       
   150 	const TUint KNumChunks = 1000;
       
   151 	const TUint KPagedChunksStart = (KNumChunks >> 1);
       
   152 	const TUint KNumProcesses = 256;
       
   153 	const TInt KMinFreeRam = (1000 * gPageSize) + (130048 * (gPageSize>>2));
       
   154 	TInt freeRam;
       
   155 	HAL::Get(HALData::EMemoryRAMFree, freeRam);
       
   156 	if (freeRam < KMinFreeRam)
       
   157 		{
       
   158 		test.Printf(_L("Only 0x%x bytes of free RAM not enough to perform the test.  Skipping test.\n"), freeRam);
       
   159 		return;
       
   160 		}
       
   161 
       
   162 	// Remove the maximum limit on the cache size as the test requires that it can
       
   163 	// allocate as many page tables as possible but without stealing any pages as
       
   164 	// stealing pages may indirectly steal paged page table pages.
       
   165 	TUint minCacheSize, maxCacheSize, currentCacheSize;
       
   166 	DPTest::CacheSize(minCacheSize,maxCacheSize,currentCacheSize);
       
   167 	test_KErrNone(DPTest::SetCacheSize(minCacheSize, KMaxTUint));
       
   168 
       
   169 	RServer2 ptServer;
       
   170 	TInt r = ptServer.CreateGlobal(KClientPtServerName);
       
   171 	test_KErrNone(r);
       
   172 
       
   173 	// Create the global unpaged chunks.  They have one page committed
       
   174 	// but have a maximum size large enough to prevent their page tables being
       
   175 	// shared between the chunks.  On arm with 4KB pages each page table maps 1MB
       
   176 	// so make chunk 1MB+4KB so chunk requires 2 page tables and is not aligned on
       
   177 	// a 1MB boundary so it is a fine memory object.
       
   178 	const TUint KChunkSize = (1024 * 1024) + gPageSize;
       
   179 	RChunk* chunks = new RChunk[KNumChunks];
       
   180 	TChunkCreateInfo createInfo;
       
   181 	createInfo.SetNormal(gPageSize, KChunkSize);
       
   182 	createInfo.SetGlobal(KNullDesC);
       
   183 	createInfo.SetPaging(TChunkCreateInfo::EUnpaged);
       
   184 	TUint i = 0;
       
   185 	for (; i < KPagedChunksStart; i++)
       
   186 		{
       
   187 		r = chunks[i].Create(createInfo);
       
   188 		test_KErrNone(r);
       
   189 		}
       
   190 	// Create paged chunks.
       
   191 	createInfo.SetPaging(TChunkCreateInfo::EPaged);
       
   192 	for (; i< KNumChunks; i++)
       
   193 		{
       
   194 		r = chunks[i].Create(createInfo);
       
   195 		test_KErrNone(r);
       
   196 		}
       
   197 
       
   198 	// Start remote processes, giving each process handles to each chunk.
       
   199 	RProcess* processes = new RProcess[KNumProcesses];
       
   200 	RMessage2 ptMessage;
       
   201 	TUint processIndex = 0;
       
   202 	TUint processLimit = 0;
       
   203 	for (; processIndex < KNumProcesses; processIndex++)
       
   204 		{
       
   205 		// Start the process.
       
   206 		test.Printf(_L("Creating process %d\n"), processIndex);
       
   207 		TBuf<80> args;
       
   208 		args.AppendFormat(_L("%d %d"), KNumChunks, EFalse);
       
   209 		r = processes[processIndex].Create(KClientProcessName, args);
       
   210 		test_KErrNone(r);
       
   211 		TRequestStatus s;
       
   212 		processes[processIndex].Logon(s);
       
   213 		test_Equal(KRequestPending, s.Int());
       
   214 		processes[processIndex].Resume();
       
   215 
       
   216 		ptServer.Receive(ptMessage);
       
   217 		test_Equal(EClientConnect, ptMessage.Function());
       
   218 		ptMessage.Complete(KErrNone);
       
   219 		TInt func = EClientGetChunk;
       
   220 		TUint chunkIndex = 0;
       
   221 		for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++)
       
   222 			{// Pass handles to all the unpaged chunks to the new process.
       
   223 			ptServer.Receive(ptMessage);
       
   224 			func = ptMessage.Function();
       
   225 			if (func == EClientGetChunk)
       
   226 				{
       
   227 				TUint index = ptMessage.Int0();
       
   228 				ptMessage.Complete(chunks[index]);
       
   229 				}
       
   230 			}
       
   231 		if (func != EClientGetChunk)
       
   232 			{
       
   233 			// Should hit the limit of page tables and this process instance should exit
       
   234 			// sending a disconnect message in the process.
       
   235 			test_Equal(EClientDisconnect, func);
       
   236 			// Should only fail when mapping unpaged chunks.
       
   237 			test_Value(chunkIndex, chunkIndex < (KNumChunks >> 1));
       
   238 			break;
       
   239 			}
       
   240 		// Wait for the process to access all the chunks and therefore 
       
   241 		// allocate the paged page tables before moving onto the next process.
       
   242 		ptServer.Receive(ptMessage);
       
   243 		func = ptMessage.Function();
       
   244 		test_Equal(EClientReadChunks, func);
       
   245 		ptMessage.Complete(KErrNone);
       
   246 
       
   247 		// Should have mapped all the required chunks.
       
   248 		test_Equal(KNumChunks, chunkIndex);
       
   249 		}
       
   250 	// Should hit page table limit before KNumProcesses have been created.
       
   251 	test_Value(processIndex, processIndex < KNumProcesses - 1);
       
   252 	processLimit = processIndex;
       
   253 
       
   254 	// Now create more processes to access paged data even though the page table 
       
   255 	// address space has been exhausted.  Limit to 10 more processes as test takes 
       
   256 	// long enough already.
       
   257 	processIndex++;
       
   258 	TUint excessProcesses = KNumProcesses - processIndex;
       
   259 	TUint pagedIndexEnd = (excessProcesses > 10)? processIndex + 10 : processIndex + excessProcesses;
       
   260 	for (; processIndex < pagedIndexEnd; processIndex++)
       
   261 		{
       
   262 		// Start the process.
       
   263 		test.Printf(_L("Creating process %d\n"), processIndex);
       
   264 		TBuf<80> args;
       
   265 		args.AppendFormat(_L("%d %d"), KNumChunks-KPagedChunksStart, ETrue);
       
   266 		r = processes[processIndex].Create(KClientProcessName, args);
       
   267 		if (r != KErrNone)
       
   268 			{// Have hit the limit of processes.
       
   269 			processIndex--;
       
   270 			// Should have created at least one more process.
       
   271 			test_Value(processIndex, processIndex > processLimit);
       
   272 			break;
       
   273 			}
       
   274 		TRequestStatus s;
       
   275 		processes[processIndex].Logon(s);
       
   276 		test_Equal(KRequestPending, s.Int());
       
   277 		processes[processIndex].Resume();
       
   278 
       
   279 		ptServer.Receive(ptMessage);
       
   280 		test_Equal(EClientConnect, ptMessage.Function());
       
   281 		ptMessage.Complete(KErrNone);
       
   282 
       
   283 		TInt func = EClientGetChunk;
       
   284 		TUint chunkIndex = KPagedChunksStart;
       
   285 		for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++)
       
   286 			{// Pass handles to all the unpaged chunks to the new process.
       
   287 			ptServer.Receive(ptMessage);
       
   288 			func = ptMessage.Function();
       
   289 			if (func == EClientGetChunk)
       
   290 				{
       
   291 				TUint index = ptMessage.Int0() + KPagedChunksStart;
       
   292 				ptMessage.Complete(chunks[index]);
       
   293 				}
       
   294 			}
       
   295 		if (func != EClientGetChunk)
       
   296 			{// Reached memory limits so exit.
       
   297 			test_Equal(EClientDisconnect, func);
       
   298 			// Should have created at least one more process.
       
   299 			test_Value(processIndex, processIndex > processLimit+1);
       
   300 			break;
       
   301 			}
       
   302 
       
   303 		// Should have mapped all the required chunks.
       
   304 		test_Equal(KNumChunks, chunkIndex);
       
   305 		}
       
   306 	// If we reached the end of then ensure that we kill only the running processes.
       
   307 	if (processIndex == pagedIndexEnd)
       
   308 		processIndex--;
       
   309 	// Kill all the remote processes
       
   310 	for(TInt j = processIndex; j >= 0; j--)
       
   311 		{
       
   312 		test.Printf(_L("killing process %d\n"), j);
       
   313 		TRequestStatus req;
       
   314 		processes[j].Logon(req);
       
   315 		if (req == KRequestPending)
       
   316 			{
       
   317 			processes[j].Kill(KErrNone);
       
   318 			User::WaitForRequest(req);
       
   319 			}
       
   320 		processes[j].Close();
       
   321 		}
       
   322 	delete[] processes;
       
   323 	// Close the chunks.
       
   324 	for (TUint k = 0; k < KNumChunks; k++)
       
   325 		chunks[k].Close();
       
   326 	delete[] chunks;
       
   327 	
       
   328 	test_KErrNone(DPTest::SetCacheSize(minCacheSize, maxCacheSize));
       
   329 	}
       
   330 
       
   331 
       
   332 TInt E32Main()
       
   333 	{
       
   334 	test_KErrNone(UserHal::PageSizeInBytes(gPageSize));
       
   335 
       
   336 	TUint len = User::CommandLineLength();
       
   337 	if (len > 0)
       
   338 		{
       
   339 		return ClientProcess(len);
       
   340 		}
       
   341 
       
   342 	test.Title();
       
   343 	test_KErrNone(GetGlobalPolicies());
       
   344 
       
   345 	if (!gDataPagingSupported)
       
   346 		{
       
   347 		test.Printf(_L("Data paging not enabled so skipping test...\n"));
       
   348 		return KErrNone;
       
   349 		}
       
   350 	
       
   351 	test.Start(_L("Test the system can always acquire a paged page table"));
       
   352 	TestMaxPt();
       
   353 	
       
   354 	test.End();
       
   355 	return KErrNone;
       
   356 	}