--- 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 <hal.h>
#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));
}