First public contribution.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32test\demandpaging\t_pagetable_limit.cpp
15 // Tests to expose the limit of page table virtual address space.
19 //! @SYMTestCaseID KBASE-T_PAGETABLE_LIMIT
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
28 #define __E32TEST_EXTENSION__
37 RTest test(_L("T_PAGETABLE_LIMIT"));
40 _LIT(KClientPtServerName, "CClientPtServer");
41 _LIT(KClientProcessName, "T_PAGETABLE_LIMIT");
46 EClientDisconnect = -2,
48 EClientReadChunks = 1,
51 class RDataPagingSession : public RSessionBase
54 TInt CreateSession(const TDesC& aServerName, TInt aMsgSlots)
56 return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots);
58 TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
60 return (SendReceive(aFunction, aPtr));
62 TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr)
64 return (Send(aFunction, aPtr));
69 TInt ClientProcess(TInt aLen)
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);
75 TPtr ptr = buf->Des();
76 User::CommandLine(ptr);
80 TInt r = lex.Val(chunkCount);
85 r = lex.Val(accessData);
89 RDataPagingSession session;
90 test_KErrNone(session.CreateSession(KClientPtServerName, 1));
92 RChunk* chunks = new RChunk[chunkCount];
93 for (TInt i = 0; i < chunkCount; i++)
95 TInt r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i)));
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++)
104 test_Value(chunks[i].Size(), chunks[i].Size() >= gPageSize);
108 // Touch the 1st page of each of the chunks.
109 for (TInt i = 0; i < chunkCount; i++)
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;
116 // Tell parent we've touched each chunk.
117 TInt r = (TThreadId)session.PublicSendReceive(EClientReadChunks,TIpcArgs()); // Assumes id is only 32-bit.
120 {// Wake up every 100ms to be killed by the main process.
129 for (TInt i = 0; i < chunkCount; i++)
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);
136 if (++offset >= (gPageSize >> 1))
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));
155 HAL::Get(HALData::EMemoryRAMFree, freeRam);
156 if (freeRam < KMinFreeRam)
158 test.Printf(_L("Only 0x%x bytes of free RAM not enough to perform the test. Skipping test.\n"), freeRam);
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));
170 TInt r = ptServer.CreateGlobal(KClientPtServerName);
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);
185 for (; i < KPagedChunksStart; i++)
187 r = chunks[i].Create(createInfo);
190 // Create paged chunks.
191 createInfo.SetPaging(TChunkCreateInfo::EPaged);
192 for (; i< KNumChunks; i++)
194 r = chunks[i].Create(createInfo);
198 // Start remote processes, giving each process handles to each chunk.
199 RProcess* processes = new RProcess[KNumProcesses];
201 TUint processIndex = 0;
202 TUint processLimit = 0;
203 for (; processIndex < KNumProcesses; processIndex++)
205 // Start the process.
206 test.Printf(_L("Creating process %d\n"), processIndex);
208 args.AppendFormat(_L("%d %d"), KNumChunks, EFalse);
209 r = processes[processIndex].Create(KClientProcessName, args);
212 processes[processIndex].Logon(s);
213 test_Equal(KRequestPending, s.Int());
214 processes[processIndex].Resume();
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)
227 TUint index = ptMessage.Int0();
228 ptMessage.Complete(chunks[index]);
231 if (func != EClientGetChunk)
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));
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);
247 // Should have mapped all the required chunks.
248 test_Equal(KNumChunks, chunkIndex);
250 // Should hit page table limit before KNumProcesses have been created.
251 test_Value(processIndex, processIndex < KNumProcesses - 1);
252 processLimit = processIndex;
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.
258 TUint excessProcesses = KNumProcesses - processIndex;
259 TUint pagedIndexEnd = (excessProcesses > 10)? processIndex + 10 : processIndex + excessProcesses;
260 for (; processIndex < pagedIndexEnd; processIndex++)
262 // Start the process.
263 test.Printf(_L("Creating process %d\n"), processIndex);
265 args.AppendFormat(_L("%d %d"), KNumChunks-KPagedChunksStart, ETrue);
266 r = processes[processIndex].Create(KClientProcessName, args);
268 {// Have hit the limit of processes.
270 // Should have created at least one more process.
271 test_Value(processIndex, processIndex > processLimit);
275 processes[processIndex].Logon(s);
276 test_Equal(KRequestPending, s.Int());
277 processes[processIndex].Resume();
279 ptServer.Receive(ptMessage);
280 test_Equal(EClientConnect, ptMessage.Function());
281 ptMessage.Complete(KErrNone);
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)
291 TUint index = ptMessage.Int0() + KPagedChunksStart;
292 ptMessage.Complete(chunks[index]);
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);
303 // Should have mapped all the required chunks.
304 test_Equal(KNumChunks, chunkIndex);
306 // If we reached the end of then ensure that we kill only the running processes.
307 if (processIndex == pagedIndexEnd)
309 // Kill all the remote processes
310 for(TInt j = processIndex; j >= 0; j--)
312 test.Printf(_L("killing process %d\n"), j);
314 processes[j].Logon(req);
315 if (req == KRequestPending)
317 processes[j].Kill(KErrNone);
318 User::WaitForRequest(req);
320 processes[j].Close();
324 for (TUint k = 0; k < KNumChunks; k++)
328 test_KErrNone(DPTest::SetCacheSize(minCacheSize, maxCacheSize));
334 test_KErrNone(UserHal::PageSizeInBytes(gPageSize));
336 TUint len = User::CommandLineLength();
339 return ClientProcess(len);
343 test_KErrNone(GetGlobalPolicies());
345 if (!gDataPagingSupported)
347 test.Printf(_L("Data paging not enabled so skipping test...\n"));
351 test.Start(_L("Test the system can always acquire a paged page table"));