55 { |
71 { |
56 return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots); |
72 return RSessionBase::CreateSession(aServerName,User::Version(),aMsgSlots); |
57 } |
73 } |
58 TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr) |
74 TInt PublicSendReceive(TInt aFunction, const TIpcArgs &aPtr) |
59 { |
75 { |
60 return (SendReceive(aFunction, aPtr)); |
76 return SendReceive(aFunction, aPtr); |
61 } |
|
62 TInt PublicSend(TInt aFunction, const TIpcArgs &aPtr) |
|
63 { |
|
64 return (Send(aFunction, aPtr)); |
|
65 } |
77 } |
66 }; |
78 }; |
67 |
79 |
|
80 #define CLIENT_TEST_IMPL(condition, code, line) \ |
|
81 if (!(condition)) \ |
|
82 { \ |
|
83 RDebug::Printf("Test %s failed at line %d"); \ |
|
84 r = (code); \ |
|
85 goto exit; \ |
|
86 } |
|
87 |
|
88 #define CLIENT_TEST(condition, code) CLIENT_TEST_IMPL(condition, code, __LINE__) |
68 |
89 |
69 TInt ClientProcess(TInt aLen) |
90 TInt ClientProcess(TInt aLen) |
70 { |
91 { |
71 // Read the command line to get the number of chunk to map and whether or |
92 // Read the command line to get the number of chunk to map and whether or not to access their |
72 // not to access their data. |
93 // data. |
73 HBufC* buf = HBufC::New(aLen); |
94 HBufC* buf = HBufC::New(aLen); |
74 test(buf != NULL); |
95 test(buf != NULL); |
75 TPtr ptr = buf->Des(); |
96 TPtr ptr = buf->Des(); |
76 User::CommandLine(ptr); |
97 User::CommandLine(ptr); |
77 |
|
78 TLex lex(ptr); |
98 TLex lex(ptr); |
|
99 |
|
100 RChunk* chunks = NULL; |
|
101 RDataPagingSession session; |
|
102 RProcess parent; |
|
103 TRequestStatus parentStatus; |
|
104 TInt offset = 0; |
|
105 TInt i; |
|
106 |
79 TInt chunkCount; |
107 TInt chunkCount; |
80 TInt r = lex.Val(chunkCount); |
108 TInt r = lex.Val(chunkCount); |
81 test_KErrNone(r); |
109 CLIENT_TEST(r == KErrNone, r); |
82 lex.SkipSpace(); |
110 lex.SkipSpace(); |
|
111 chunks = new RChunk[chunkCount]; |
|
112 CLIENT_TEST(chunks, KErrNoMemory); |
83 |
113 |
84 TBool accessData; |
114 TBool accessData; |
85 r = lex.Val(accessData); |
115 r = lex.Val(accessData); |
86 test_KErrNone(r); |
116 CLIENT_TEST(r == KErrNone, r); |
87 |
117 |
88 |
118 r = session.CreateSession(KClientPtServerName, 1); |
89 RDataPagingSession session; |
119 CLIENT_TEST(r == KErrNone, r); |
90 test_KErrNone(session.CreateSession(KClientPtServerName, 1)); |
120 |
91 |
121 r = parent.SetReturnedHandle(session.PublicSendReceive(EClientGetParentProcess, TIpcArgs())); |
92 RChunk* chunks = new RChunk[chunkCount]; |
122 CLIENT_TEST(r == KErrNone, r); |
93 for (TInt i = 0; i < chunkCount; i++) |
123 |
94 { |
124 for (i = 0; i < chunkCount; i++) |
95 TInt r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i))); |
125 { |
|
126 r = chunks[i].SetReturnedHandle(session.PublicSendReceive(EClientGetChunk, TIpcArgs(i))); |
96 if (r != KErrNone) |
127 if (r != KErrNone) |
97 { |
128 { |
98 test.Printf(_L("Failed to create a handle to the server's chunk r=%d\n"), r); |
129 RDebug::Printf("Failed to create a handle to chunk %d r=%d", i, r); |
99 for (TInt j = 0; j < i; j++) |
130 goto exit; |
100 chunks[j].Close(); |
131 } |
101 session.Close(); |
132 CLIENT_TEST(chunks[i].Size() >= gPageSize, KErrGeneral); |
102 return r; |
133 } |
103 } |
134 |
104 test_Value(chunks[i].Size(), chunks[i].Size() >= gPageSize); |
135 // Logon to parent process |
105 } |
136 parent.Logon(parentStatus); |
106 if (!accessData) |
137 |
107 { |
138 // Touch each mapped page of all of the chunks. |
108 // Touch the 1st page of each of the chunks. |
139 do |
|
140 { |
109 for (TInt i = 0; i < chunkCount; i++) |
141 for (TInt i = 0; i < chunkCount; i++) |
110 { |
142 { |
111 // Write the chunk data from top to bottom of the chunk's first page. |
143 for (TUint j = 0 ; j < KPageTablesPerChunk ; j++) |
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 { |
144 { |
131 // Write the chunk data from top to bottom of the chunk's first page. |
145 // Write the chunk data from top to bottom of the chunk's first page. |
132 TUint8* base = chunks[i].Base(); |
146 TUint8* base = chunks[i].Base() + j * KSizeMappedByPageTable; |
133 TUint8* end = base + gPageSize - 1; |
147 TUint8* end = base + gPageSize - 1; |
134 *(base + offset) = *(end - offset); |
148 *(base + offset) = *(end - offset); |
|
149 |
|
150 User::After(0); |
|
151 |
|
152 // Check whether main process is still running |
|
153 if (parentStatus != KRequestPending) |
|
154 { |
|
155 // if we get here the test failed and the main process exited without killing |
|
156 // us, so just exit quietly |
|
157 User::WaitForRequest(parentStatus); |
|
158 r = KErrGeneral; |
|
159 goto exit; |
|
160 } |
135 } |
161 } |
136 if (++offset >= (gPageSize >> 1)) |
162 } |
137 offset = 0; |
163 offset = (offset + 1) % (gPageSize / 2); |
138 } |
164 } |
139 } |
165 while (accessData); |
140 } |
166 |
141 |
167 // Tell parent we've touched each page. |
|
168 r = (TThreadId)session.PublicSendReceive(EClientReadChunks,TIpcArgs()); |
|
169 CLIENT_TEST(r == KErrNone, r); |
|
170 |
|
171 // Wait till we get killed by the main process or the main process dies |
|
172 User::WaitForRequest(parentStatus); |
|
173 // if we get here the test failed and the main process exited without killing us, so just exit |
|
174 r = KErrGeneral; |
|
175 |
|
176 exit: |
|
177 if (chunks) |
|
178 { |
|
179 for (TInt i = 0 ; i < chunkCount; ++i) |
|
180 chunks[i].Close(); |
|
181 } |
|
182 session.Close(); |
|
183 parent.Close(); |
|
184 |
|
185 return r; |
|
186 } |
|
187 |
|
188 TInt FreeSwap() |
|
189 { |
|
190 SVMSwapInfo swapInfo; |
|
191 test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo, 0)); |
|
192 return swapInfo.iSwapFree; |
|
193 } |
|
194 |
|
195 void PrintFreeRam() |
|
196 { |
|
197 test.Printf(_L("%d KB RAM / %d KB swap free\n"), FreeRam() / 1024, FreeSwap() / 1024); |
|
198 } |
|
199 |
|
200 void CreateChunk(RChunk& aChunk, TInt aChunkIndex, TInt aPageTables, TBool aPaged) |
|
201 { |
|
202 // Creates a global chunk. |
|
203 // |
|
204 // The chunk uses KPageTablesPerChunk page tables by committing that number of pages at 1MB |
|
205 // intervals. Its max size is set so that it is not a multiple of 1MB and hence the FMM will |
|
206 // use a fine mapping objects whose page tables are not shared between processes. |
|
207 |
|
208 test.Printf(_L(" creating chunk %d: "), aChunkIndex); |
|
209 PrintFreeRam(); |
|
210 |
|
211 TChunkCreateInfo createInfo; |
|
212 createInfo.SetDisconnected(0, 0, aPageTables * KSizeMappedByPageTable + gPageSize); |
|
213 createInfo.SetPaging(aPaged ? TChunkCreateInfo::EPaged : TChunkCreateInfo::EUnpaged); |
|
214 TBuf<32> name; |
|
215 name.AppendFormat(_L("t_pagetable_limit chunk %d"), aChunkIndex); |
|
216 createInfo.SetGlobal(name); |
|
217 test_KErrNone(aChunk.Create(createInfo)); |
|
218 for (TInt i = 0 ; i < aPageTables ; ++i) |
|
219 test_KErrNone(aChunk.Commit(i * KSizeMappedByPageTable, gPageSize)); |
|
220 } |
|
221 |
|
222 void CreateProcess(RProcess& aProcess, TInt aProcessIndex, TInt aNumChunks, TBool aAccessData) |
|
223 { |
|
224 test.Printf(_L(" creating process %d: "), aProcessIndex); |
|
225 PrintFreeRam(); |
|
226 |
|
227 TBuf<80> args; |
|
228 args.AppendFormat(_L("%d %d"), aNumChunks, aAccessData); |
|
229 test_KErrNone(aProcess.Create(KClientProcessName, args)); |
|
230 aProcess.SetPriority(EPriorityLow); |
|
231 } |
142 |
232 |
143 void TestMaxPt() |
233 void TestMaxPt() |
144 { |
234 { |
145 // Flexible memory model reserves 0xF800000-0xFFF00000 for page tables |
235 test.Printf(_L("Waiting for system idle and kernel cleanup\n")); |
146 // this allows 130,048 pages tables. Therefore mapping 1000 one |
236 // If this test was run previously, there may be lots of dead processes waiting to be cleaned up |
147 // page chunks into 256 processes would require 256,000 page tables, i.e. |
237 TInt r; |
148 // more than enough to hit the limit. So that the limit is reached in the middle, |
238 while((r = FreeRam(10 * 1000)), r == KErrTimedOut) |
149 // map 500 unpaged and 500 paged chunks in each process. |
239 { |
150 const TUint KNumChunks = 1000; |
240 test.Printf(_L(" waiting: ")); |
151 const TUint KPagedChunksStart = (KNumChunks >> 1); |
241 PrintFreeRam(); |
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 } |
242 } |
161 |
243 |
162 // Remove the maximum limit on the cache size as the test requires that it can |
244 // 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 |
245 // allocate as many page tables as possible but without stealing any pages as |
164 // stealing pages may indirectly steal paged page table pages. |
246 // stealing pages may indirectly steal paged page table pages. |
|
247 test.Printf(_L("Set paging cache max size unlimited\n")); |
165 TUint minCacheSize, maxCacheSize, currentCacheSize; |
248 TUint minCacheSize, maxCacheSize, currentCacheSize; |
166 DPTest::CacheSize(minCacheSize,maxCacheSize,currentCacheSize); |
249 test_KErrNone(DPTest::CacheSize(minCacheSize,maxCacheSize,currentCacheSize)); |
167 test_KErrNone(DPTest::SetCacheSize(minCacheSize, KMaxTUint)); |
250 test_KErrNone(DPTest::SetCacheSize(minCacheSize, KMaxTUint)); |
168 |
251 |
|
252 const TInt KMinFreeRam = (1000 * gPageSize) + (130048 * (gPageSize>>2)); |
|
253 |
|
254 // Ensure enough RAM available |
|
255 PrintFreeRam(); |
|
256 TInt freeRam = FreeRam(); |
|
257 if (freeRam < KMinFreeRam) |
|
258 { |
|
259 test.Printf(_L("Only 0x%x bytes of free RAM not enough to perform the test. Skipping test.\n"), freeRam); |
|
260 return; |
|
261 } |
|
262 |
|
263 test.Printf(_L("Start server\n")); |
169 RServer2 ptServer; |
264 RServer2 ptServer; |
170 TInt r = ptServer.CreateGlobal(KClientPtServerName); |
265 r = ptServer.CreateGlobal(KClientPtServerName); |
171 test_KErrNone(r); |
266 test_KErrNone(r); |
172 |
267 |
173 // Create the global unpaged chunks. They have one page committed |
268 test.Printf(_L("Create chunks\n")); |
174 // but have a maximum size large enough to prevent their page tables being |
269 const TUint KPagedChunksStart = (KChunksPerProcess >> 1); |
175 // shared between the chunks. On arm with 4KB pages each page table maps 1MB |
270 RChunk* chunks = new RChunk[KChunksPerProcess]; |
176 // so make chunk 1MB+4KB so chunk requires 2 page tables and is not aligned on |
271 test_NotNull(chunks); |
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; |
272 TUint i = 0; |
185 for (; i < KPagedChunksStart; i++) |
273 for (i = 0 ; i< KChunksPerProcess; i++) |
186 { |
274 CreateChunk(chunks[i], i, KPageTablesPerChunk, i >= KPagedChunksStart); |
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 |
275 |
198 // Start remote processes, giving each process handles to each chunk. |
276 // Start remote processes, giving each process handles to each chunk. |
|
277 test.Printf(_L("Start remote processes\n")); |
199 RProcess* processes = new RProcess[KNumProcesses]; |
278 RProcess* processes = new RProcess[KNumProcesses]; |
200 RMessage2 ptMessage; |
279 test_NotNull(processes); |
201 TUint processIndex = 0; |
280 TRequestStatus* statuses = new TRequestStatus[KNumProcesses]; |
202 TUint processLimit = 0; |
281 test_NotNull(statuses); |
|
282 TUint processIndex = 0; |
203 for (; processIndex < KNumProcesses; processIndex++) |
283 for (; processIndex < KNumProcesses; processIndex++) |
204 { |
284 { |
205 // Start the process. |
285 // Start the process. |
206 test.Printf(_L("Creating process %d\n"), processIndex); |
286 CreateProcess(processes[processIndex], processIndex, KChunksPerProcess, EFalse); |
207 TBuf<80> args; |
287 |
208 args.AppendFormat(_L("%d %d"), KNumChunks, EFalse); |
288 // logon to process |
209 r = processes[processIndex].Create(KClientProcessName, args); |
289 processes[processIndex].Logon(statuses[processIndex]); |
210 test_KErrNone(r); |
290 test_Equal(KRequestPending, statuses[processIndex].Int()); |
211 TRequestStatus s; |
|
212 processes[processIndex].Logon(s); |
|
213 test_Equal(KRequestPending, s.Int()); |
|
214 processes[processIndex].Resume(); |
291 processes[processIndex].Resume(); |
215 |
292 |
|
293 // wait for connect message |
|
294 RMessage2 ptMessage; |
216 ptServer.Receive(ptMessage); |
295 ptServer.Receive(ptMessage); |
217 test_Equal(EClientConnect, ptMessage.Function()); |
296 test_Equal(EClientConnect, ptMessage.Function()); |
218 ptMessage.Complete(KErrNone); |
297 ptMessage.Complete(KErrNone); |
219 TInt func = EClientGetChunk; |
298 |
|
299 // pass client a handle to this process |
|
300 ptServer.Receive(ptMessage); |
|
301 test_Equal(EClientGetParentProcess, ptMessage.Function()); |
|
302 ptMessage.Complete(RProcess()); |
|
303 |
|
304 // pass client chunk handles |
|
305 TInt func; |
220 TUint chunkIndex = 0; |
306 TUint chunkIndex = 0; |
221 for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++) |
307 for (; chunkIndex < KChunksPerProcess ; chunkIndex++) |
222 {// Pass handles to all the unpaged chunks to the new process. |
308 {// Pass handles to all the unpaged chunks to the new process. |
223 ptServer.Receive(ptMessage); |
309 ptServer.Receive(ptMessage); |
224 func = ptMessage.Function(); |
310 func = ptMessage.Function(); |
225 if (func == EClientGetChunk) |
311 if (func != EClientGetChunk) |
226 { |
312 break; |
227 TUint index = ptMessage.Int0(); |
313 ptMessage.Complete(chunks[ptMessage.Int0()]); |
228 ptMessage.Complete(chunks[index]); |
|
229 } |
|
230 } |
314 } |
231 if (func != EClientGetChunk) |
315 if (func != EClientGetChunk) |
232 { |
316 { |
233 // Should hit the limit of page tables and this process instance should exit |
317 // Should hit the limit of page tables and this process instance should exit |
234 // sending a disconnect message in the process. |
318 // sending a disconnect message in the process. |
235 test_Equal(EClientDisconnect, func); |
319 test_Equal(EClientDisconnect, func); |
236 // Should only fail when mapping unpaged chunks. |
320 // Should only fail when mapping unpaged chunks. |
237 test_Value(chunkIndex, chunkIndex < (KNumChunks >> 1)); |
321 test_Value(chunkIndex, chunkIndex < (KChunksPerProcess >> 1)); |
238 break; |
322 break; |
239 } |
323 } |
|
324 |
240 // Wait for the process to access all the chunks and therefore |
325 // Wait for the process to access all the chunks and therefore |
241 // allocate the paged page tables before moving onto the next process. |
326 // allocate the paged page tables before moving onto the next process. |
242 ptServer.Receive(ptMessage); |
327 ptServer.Receive(ptMessage); |
243 func = ptMessage.Function(); |
328 func = ptMessage.Function(); |
244 test_Equal(EClientReadChunks, func); |
329 test_Equal(EClientReadChunks, func); |
245 ptMessage.Complete(KErrNone); |
330 ptMessage.Complete(KErrNone); |
246 |
331 |
247 // Should have mapped all the required chunks. |
332 // Should have mapped all the required chunks. |
248 test_Equal(KNumChunks, chunkIndex); |
333 test_Equal(KChunksPerProcess, chunkIndex); |
249 } |
334 } |
|
335 |
250 // Should hit page table limit before KNumProcesses have been created. |
336 // Should hit page table limit before KNumProcesses have been created. |
251 test_Value(processIndex, processIndex < KNumProcesses - 1); |
337 test_Value(processIndex, processIndex < KNumProcesses - 1); |
252 processLimit = processIndex; |
338 TUint processLimit = processIndex; |
253 |
339 |
254 // Now create more processes to access paged data even though the page table |
340 // Now create more processes to access paged data even though the page table address space has |
255 // address space has been exhausted. Limit to 10 more processes as test takes |
341 // been exhausted. Limit to 10 more processes as test takes long enough already. |
256 // long enough already. |
342 test.Printf(_L("Start accessor processes\n")); |
257 processIndex++; |
343 processIndex++; |
258 TUint excessProcesses = KNumProcesses - processIndex; |
344 TUint excessProcesses = KNumProcesses - processIndex; |
259 TUint pagedIndexEnd = (excessProcesses > 10)? processIndex + 10 : processIndex + excessProcesses; |
345 TUint pagedIndexEnd = (excessProcesses > 10)? processIndex + 10 : processIndex + excessProcesses; |
260 for (; processIndex < pagedIndexEnd; processIndex++) |
346 for (; processIndex < pagedIndexEnd; processIndex++) |
261 { |
347 { |
262 // Start the process. |
348 // start the process. |
263 test.Printf(_L("Creating process %d\n"), processIndex); |
349 CreateProcess(processes[processIndex], processIndex, KChunksPerProcess-KPagedChunksStart, ETrue); |
264 TBuf<80> args; |
350 |
265 args.AppendFormat(_L("%d %d"), KNumChunks-KPagedChunksStart, ETrue); |
351 // logon to process |
266 r = processes[processIndex].Create(KClientProcessName, args); |
352 processes[processIndex].Logon(statuses[processIndex]); |
267 if (r != KErrNone) |
353 test_Equal(KRequestPending, statuses[processIndex].Int()); |
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(); |
354 processes[processIndex].Resume(); |
278 |
355 |
|
356 // wait for connect message |
|
357 RMessage2 ptMessage; |
279 ptServer.Receive(ptMessage); |
358 ptServer.Receive(ptMessage); |
280 test_Equal(EClientConnect, ptMessage.Function()); |
359 test_Equal(EClientConnect, ptMessage.Function()); |
281 ptMessage.Complete(KErrNone); |
360 ptMessage.Complete(KErrNone); |
282 |
361 |
|
362 // pass client a handle to this process |
|
363 ptServer.Receive(ptMessage); |
|
364 test_Equal(EClientGetParentProcess, ptMessage.Function()); |
|
365 ptMessage.Complete(RProcess()); |
|
366 |
283 TInt func = EClientGetChunk; |
367 TInt func = EClientGetChunk; |
284 TUint chunkIndex = KPagedChunksStart; |
368 TUint chunkIndex = KPagedChunksStart; |
285 for (; chunkIndex < KNumChunks && func == EClientGetChunk; chunkIndex++) |
369 for (; chunkIndex < KChunksPerProcess && func == EClientGetChunk; chunkIndex++) |
286 {// Pass handles to all the unpaged chunks to the new process. |
370 {// Pass handles to all the paged chunks to the new process. |
287 ptServer.Receive(ptMessage); |
371 ptServer.Receive(ptMessage); |
288 func = ptMessage.Function(); |
372 func = ptMessage.Function(); |
289 if (func == EClientGetChunk) |
373 if (func == EClientGetChunk) |
290 { |
374 { |
291 TUint index = ptMessage.Int0() + KPagedChunksStart; |
375 TUint index = ptMessage.Int0() + KPagedChunksStart; |