1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/kerneltest/e32test/demandpaging/t_pagetable_limit.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,356 @@
1.4 +// Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of the License "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// e32test\demandpaging\t_pagetable_limit.cpp
1.18 +// Tests to expose the limit of page table virtual address space.
1.19 +//
1.20 +//
1.21 +
1.22 +//! @SYMTestCaseID KBASE-T_PAGETABLE_LIMIT
1.23 +//! @SYMTestType UT
1.24 +//! @SYMPREQ PREQ1490
1.25 +//! @SYMTestCaseDesc Tests to expose the limit of page table virtual address space.
1.26 +//! @SYMTestActions Test that a paged page table can always be acquired.
1.27 +//! @SYMTestExpectedResults All tests should pass.
1.28 +//! @SYMTestPriority High
1.29 +//! @SYMTestStatus Implemented
1.30 +
1.31 +#define __E32TEST_EXTENSION__
1.32 +#include <e32test.h>
1.33 +#include <dptest.h>
1.34 +#include <e32svr.h>
1.35 +#include <u32std.h>
1.36 +#include <hal.h>
1.37 +
1.38 +#include "t_dpcmn.h"
1.39 +
1.40 +RTest test(_L("T_PAGETABLE_LIMIT"));
1.41 +
1.42 +
1.43 +_LIT(KClientPtServerName, "CClientPtServer");
1.44 +_LIT(KClientProcessName, "T_PAGETABLE_LIMIT");
1.45 +
1.46 +enum TClientMsgType
1.47 + {
1.48 + EClientConnect = -1,
1.49 + EClientDisconnect = -2,
1.50 + EClientGetChunk = 0,
1.51 + EClientReadChunks = 1,
1.52 + };
1.53 +
1.54 +class RDataPagingSession : public RSessionBase
1.55 + {
1.56 +public:
1.57 + TInt CreateSession(const TDesC& aServerName, TInt aMsgSlots)
1.58 + {
1.59 + return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots);
1.60 + }
1.61 + TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr)
1.62 + {
1.63 + return (SendReceive(aFunction, aPtr));
1.64 + }
1.65 + TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr)
1.66 + {
1.67 + return (Send(aFunction, aPtr));
1.68 + }
1.69 + };
1.70 +
1.71 +
1.72 +TInt ClientProcess(TInt aLen)
1.73 + {
1.74 + // Read the command line to get the number of chunk to map and whether or
1.75 + // not to access their data.
1.76 + HBufC* buf = HBufC::New(aLen);
1.77 + test(buf != NULL);
1.78 + TPtr ptr = buf->Des();
1.79 + User::CommandLine(ptr);
1.80 +
1.81 + TLex lex(ptr);
1.82 + TInt chunkCount;
1.83 + TInt r = lex.Val(chunkCount);
1.84 + test_KErrNone(r);
1.85 + lex.SkipSpace();
1.86 +
1.87 + TBool accessData;
1.88 + r = lex.Val(accessData);
1.89 + test_KErrNone(r);
1.90 +
1.91 +
1.92 + RDataPagingSession session;
1.93 + test_KErrNone(session.CreateSession(KClientPtServerName, 1));
1.94 +
1.95 + RChunk* chunks = new RChunk[chunkCount];
1.96 + for (TInt i = 0; i < chunkCount; i++)
1.97 + {
1.98 + TInt r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i)));
1.99 + if (r != KErrNone)
1.100 + {
1.101 + test.Printf(_L("Failed to create a handle to the server's chunk r=%d\n"), r);
1.102 + for (TInt j = 0; j < i; j++)
1.103 + chunks[j].Close();
1.104 + session.Close();
1.105 + return r;
1.106 + }
1.107 + test_Value(chunks[i].Size(), chunks[i].Size() >= gPageSize);
1.108 + }
1.109 + if (!accessData)
1.110 + {
1.111 + // Touch the 1st page of each of the chunks.
1.112 + for (TInt i = 0; i < chunkCount; i++)
1.113 + {
1.114 + // Write the chunk data from top to bottom of the chunk's first page.
1.115 + TUint8* base = chunks[i].Base();
1.116 + TUint8* end = base + gPageSize - 1;
1.117 + *base = *end;
1.118 + }
1.119 + // Tell parent we've touched each chunk.
1.120 + TInt r = (TThreadId)session.PublicSendReceive(EClientReadChunks,TIpcArgs()); // Assumes id is only 32-bit.
1.121 + test_KErrNone(r);
1.122 + for(;;)
1.123 + {// Wake up every 100ms to be killed by the main process.
1.124 + User::After(100000);
1.125 + }
1.126 + }
1.127 + else
1.128 + {
1.129 + for (;;)
1.130 + {
1.131 + TInt offset = 0;
1.132 + for (TInt i = 0; i < chunkCount; i++)
1.133 + {
1.134 + // Write the chunk data from top to bottom of the chunk's first page.
1.135 + TUint8* base = chunks[i].Base();
1.136 + TUint8* end = base + gPageSize - 1;
1.137 + *(base + offset) = *(end - offset);
1.138 + }
1.139 + if (++offset >= (gPageSize >> 1))
1.140 + offset = 0;
1.141 + }
1.142 + }
1.143 + }
1.144 +
1.145 +
1.146 +void TestMaxPt()
1.147 + {
1.148 + // Flexible memory model reserves 0xF800000-0xFFF00000 for page tables
1.149 + // this allows 130,048 pages tables. Therefore mapping 1000 one
1.150 + // page chunks into 256 processes would require 256,000 page tables, i.e.
1.151 + // more than enough to hit the limit. So that the limit is reached in the middle,
1.152 + // map 500 unpaged and 500 paged chunks in each process.
1.153 + const TUint KNumChunks = 1000;
1.154 + const TUint KPagedChunksStart = (KNumChunks >> 1);
1.155 + const TUint KNumProcesses = 256;
1.156 + const TInt KMinFreeRam = (1000 * gPageSize) + (130048 * (gPageSize>>2));
1.157 + TInt freeRam;
1.158 + HAL::Get(HALData::EMemoryRAMFree, freeRam);
1.159 + if (freeRam < KMinFreeRam)
1.160 + {
1.161 + test.Printf(_L("Only 0x%x bytes of free RAM not enough to perform the test. Skipping test.\n"), freeRam);
1.162 + return;
1.163 + }
1.164 +
1.165 + // Remove the maximum limit on the cache size as the test requires that it can
1.166 + // allocate as many page tables as possible but without stealing any pages as
1.167 + // stealing pages may indirectly steal paged page table pages.
1.168 + TUint minCacheSize, maxCacheSize, currentCacheSize;
1.169 + DPTest::CacheSize(minCacheSize,maxCacheSize,currentCacheSize);
1.170 + test_KErrNone(DPTest::SetCacheSize(minCacheSize, KMaxTUint));
1.171 +
1.172 + RServer2 ptServer;
1.173 + TInt r = ptServer.CreateGlobal(KClientPtServerName);
1.174 + test_KErrNone(r);
1.175 +
1.176 + // Create the global unpaged chunks. They have one page committed
1.177 + // but have a maximum size large enough to prevent their page tables being
1.178 + // shared between the chunks. On arm with 4KB pages each page table maps 1MB
1.179 + // so make chunk 1MB+4KB so chunk requires 2 page tables and is not aligned on
1.180 + // a 1MB boundary so it is a fine memory object.
1.181 + const TUint KChunkSize = (1024 * 1024) + gPageSize;
1.182 + RChunk* chunks = new RChunk[KNumChunks];
1.183 + TChunkCreateInfo createInfo;
1.184 + createInfo.SetNormal(gPageSize, KChunkSize);
1.185 + createInfo.SetGlobal(KNullDesC);
1.186 + createInfo.SetPaging(TChunkCreateInfo::EUnpaged);
1.187 + TUint i = 0;
1.188 + for (; i < KPagedChunksStart; i++)
1.189 + {
1.190 + r = chunks[i].Create(createInfo);
1.191 + test_KErrNone(r);
1.192 + }
1.193 + // Create paged chunks.
1.194 + createInfo.SetPaging(TChunkCreateInfo::EPaged);
1.195 + for (; i< KNumChunks; i++)
1.196 + {
1.197 + r = chunks[i].Create(createInfo);
1.198 + test_KErrNone(r);
1.199 + }
1.200 +
1.201 + // Start remote processes, giving each process handles to each chunk.
1.202 + RProcess* processes = new RProcess[KNumProcesses];
1.203 + RMessage2 ptMessage;
1.204 + TUint processIndex = 0;
1.205 + TUint processLimit = 0;
1.206 + for (; processIndex < KNumProcesses; processIndex++)
1.207 + {
1.208 + // Start the process.
1.209 + test.Printf(_L("Creating process %d\n"), processIndex);
1.210 + TBuf<80> args;
1.211 + args.AppendFormat(_L("%d %d"), KNumChunks, EFalse);
1.212 + r = processes[processIndex].Create(KClientProcessName, args);
1.213 + test_KErrNone(r);
1.214 + TRequestStatus s;
1.215 + processes[processIndex].Logon(s);
1.216 + test_Equal(KRequestPending, s.Int());
1.217 + processes[processIndex].Resume();
1.218 +
1.219 + ptServer.Receive(ptMessage);
1.220 + test_Equal(EClientConnect, ptMessage.Function());
1.221 + ptMessage.Complete(KErrNone);
1.222 + TInt func = EClientGetChunk;
1.223 + TUint chunkIndex = 0;
1.224 + for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++)
1.225 + {// Pass handles to all the unpaged chunks to the new process.
1.226 + ptServer.Receive(ptMessage);
1.227 + func = ptMessage.Function();
1.228 + if (func == EClientGetChunk)
1.229 + {
1.230 + TUint index = ptMessage.Int0();
1.231 + ptMessage.Complete(chunks[index]);
1.232 + }
1.233 + }
1.234 + if (func != EClientGetChunk)
1.235 + {
1.236 + // Should hit the limit of page tables and this process instance should exit
1.237 + // sending a disconnect message in the process.
1.238 + test_Equal(EClientDisconnect, func);
1.239 + // Should only fail when mapping unpaged chunks.
1.240 + test_Value(chunkIndex, chunkIndex < (KNumChunks >> 1));
1.241 + break;
1.242 + }
1.243 + // Wait for the process to access all the chunks and therefore
1.244 + // allocate the paged page tables before moving onto the next process.
1.245 + ptServer.Receive(ptMessage);
1.246 + func = ptMessage.Function();
1.247 + test_Equal(EClientReadChunks, func);
1.248 + ptMessage.Complete(KErrNone);
1.249 +
1.250 + // Should have mapped all the required chunks.
1.251 + test_Equal(KNumChunks, chunkIndex);
1.252 + }
1.253 + // Should hit page table limit before KNumProcesses have been created.
1.254 + test_Value(processIndex, processIndex < KNumProcesses - 1);
1.255 + processLimit = processIndex;
1.256 +
1.257 + // Now create more processes to access paged data even though the page table
1.258 + // address space has been exhausted. Limit to 10 more processes as test takes
1.259 + // long enough already.
1.260 + processIndex++;
1.261 + TUint excessProcesses = KNumProcesses - processIndex;
1.262 + TUint pagedIndexEnd = (excessProcesses > 10)? processIndex + 10 : processIndex + excessProcesses;
1.263 + for (; processIndex < pagedIndexEnd; processIndex++)
1.264 + {
1.265 + // Start the process.
1.266 + test.Printf(_L("Creating process %d\n"), processIndex);
1.267 + TBuf<80> args;
1.268 + args.AppendFormat(_L("%d %d"), KNumChunks-KPagedChunksStart, ETrue);
1.269 + r = processes[processIndex].Create(KClientProcessName, args);
1.270 + if (r != KErrNone)
1.271 + {// Have hit the limit of processes.
1.272 + processIndex--;
1.273 + // Should have created at least one more process.
1.274 + test_Value(processIndex, processIndex > processLimit);
1.275 + break;
1.276 + }
1.277 + TRequestStatus s;
1.278 + processes[processIndex].Logon(s);
1.279 + test_Equal(KRequestPending, s.Int());
1.280 + processes[processIndex].Resume();
1.281 +
1.282 + ptServer.Receive(ptMessage);
1.283 + test_Equal(EClientConnect, ptMessage.Function());
1.284 + ptMessage.Complete(KErrNone);
1.285 +
1.286 + TInt func = EClientGetChunk;
1.287 + TUint chunkIndex = KPagedChunksStart;
1.288 + for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++)
1.289 + {// Pass handles to all the unpaged chunks to the new process.
1.290 + ptServer.Receive(ptMessage);
1.291 + func = ptMessage.Function();
1.292 + if (func == EClientGetChunk)
1.293 + {
1.294 + TUint index = ptMessage.Int0() + KPagedChunksStart;
1.295 + ptMessage.Complete(chunks[index]);
1.296 + }
1.297 + }
1.298 + if (func != EClientGetChunk)
1.299 + {// Reached memory limits so exit.
1.300 + test_Equal(EClientDisconnect, func);
1.301 + // Should have created at least one more process.
1.302 + test_Value(processIndex, processIndex > processLimit+1);
1.303 + break;
1.304 + }
1.305 +
1.306 + // Should have mapped all the required chunks.
1.307 + test_Equal(KNumChunks, chunkIndex);
1.308 + }
1.309 + // If we reached the end of then ensure that we kill only the running processes.
1.310 + if (processIndex == pagedIndexEnd)
1.311 + processIndex--;
1.312 + // Kill all the remote processes
1.313 + for(TInt j = processIndex; j >= 0; j--)
1.314 + {
1.315 + test.Printf(_L("killing process %d\n"), j);
1.316 + TRequestStatus req;
1.317 + processes[j].Logon(req);
1.318 + if (req == KRequestPending)
1.319 + {
1.320 + processes[j].Kill(KErrNone);
1.321 + User::WaitForRequest(req);
1.322 + }
1.323 + processes[j].Close();
1.324 + }
1.325 + delete[] processes;
1.326 + // Close the chunks.
1.327 + for (TUint k = 0; k < KNumChunks; k++)
1.328 + chunks[k].Close();
1.329 + delete[] chunks;
1.330 +
1.331 + test_KErrNone(DPTest::SetCacheSize(minCacheSize, maxCacheSize));
1.332 + }
1.333 +
1.334 +
1.335 +TInt E32Main()
1.336 + {
1.337 + test_KErrNone(UserHal::PageSizeInBytes(gPageSize));
1.338 +
1.339 + TUint len = User::CommandLineLength();
1.340 + if (len > 0)
1.341 + {
1.342 + return ClientProcess(len);
1.343 + }
1.344 +
1.345 + test.Title();
1.346 + test_KErrNone(GetGlobalPolicies());
1.347 +
1.348 + if (!gDataPagingSupported)
1.349 + {
1.350 + test.Printf(_L("Data paging not enabled so skipping test...\n"));
1.351 + return KErrNone;
1.352 + }
1.353 +
1.354 + test.Start(_L("Test the system can always acquire a paged page table"));
1.355 + TestMaxPt();
1.356 +
1.357 + test.End();
1.358 + return KErrNone;
1.359 + }