diff -r c1f20ce4abcf -r 3e88ff8f41d5 kerneltest/e32test/demandpaging/t_pagetable_limit.cpp --- 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 #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)); }