os/kernelhwsrv/kerneltest/e32test/demandpaging/t_pagetable_limit.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     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 	}