os/kernelhwsrv/kerneltest/f32test/demandpaging/t_nandpaging.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2006-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".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32test\mmu\t_nandpaging.cpp
    15 // Suite of tests specifically to test the demand paging subsystem when
    16 // booted from NAND.
    17 // 002 Read/Write and Page test
    18 // 003 Defering test
    19 // 
    20 //
    21 
    22 //! @SYMTestCaseID			KBASE-T_NANDPAGING-0332
    23 //! @SYMTestType			UT
    24 //! @SYMPREQ				PREQ1110
    25 //! @SYMTestCaseDesc		Demand Paging Nand Paging tests.
    26 //! @SYMTestActions			001 Check that the rom is paged
    27 //! @SYMTestExpectedResults All tests should pass.
    28 //! @SYMTestPriority        High
    29 //! @SYMTestStatus          Implemented
    30 
    31 #include <e32test.h>
    32 RTest test(_L("T_NANDPAGING"));
    33 
    34 #include <e32rom.h>
    35 #include <e32svr.h>
    36 #include <u32hal.h>
    37 #include <f32file.h>
    38 #include <f32dbg.h>
    39 #include "testdefs.h"
    40 #include <hal.h>
    41 
    42 
    43 TInt DriveNumber=-1;   // Parameter - Which drive?  -1 = autodetect.
    44 TInt locDriveNumber;
    45 
    46 TInt MaxDeferLoops=40; // Parameter - Defer test, for how long?
    47 TInt Maxloops=400;	   // Parameter - RW Soak, for how long?
    48 TBool Forever=EFalse;  // Parameter - RW Soak forever?
    49 
    50 TBool Testing=ETrue;	// Used to communicate when testing has finished between threads.
    51 
    52 RFs TheFs;
    53 TBusLocalDrive Drive;
    54 TLocalDriveCapsV4 DriveCaps;
    55 
    56 TInt PagedTrashCount=0; // Incremented by threads, is used to detect preemption.
    57 TInt GlobError=KErrNone; // To communicate an error between threads.
    58 TBool CtrlIoCollectGarbageSupported = ETrue;
    59 TBool CtrlIoGetDeferStatsSupported = ETrue;
    60 
    61 
    62 const TInt KDiskSectorShift=9;
    63 const TInt KBufSizeInSectors=8;
    64 const TInt KBufSizeInBytes=(KBufSizeInSectors<<KDiskSectorShift)*40;
    65 
    66 LOCAL_D TBuf8<KBufSizeInBytes> Buffer;
    67 
    68 
    69 
    70 // Three functions for the garbage test.
    71 // CreateFile creates a file, and sets up the buffer for WriteNumber.
    72 // After the code has finished writing numbers to the start,
    73 // CloseAndDestroy cleans up.
    74 
    75 void CreateFile(RFile &aFile,const TDesC& aFileName)
    76 	{
    77 	TBuf<256> fileName;	
    78 	fileName.Append((TChar)('A'+DriveNumber));
    79 	fileName+=_L(":\\f32-tst\\");
    80 	TInt r=TheFs.MkDirAll(fileName);
    81 	test(r==KErrNone || r== KErrAlreadyExists);
    82 
    83 	fileName += aFileName;	
    84 
    85 	r=aFile.Replace(TheFs,fileName,EFileWrite);
    86 	if (r!=KErrNone)
    87 		test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
    88 	test(r==KErrNone);	
    89 	Buffer.SetLength(4);
    90 	}
    91 	
    92 void CloseAndDestroy(RFile &aFile)
    93 	{
    94 	TBuf<256> fileName;	
    95 	aFile.FullName(fileName);
    96 	aFile.Close();
    97 	TheFs.Delete(fileName);
    98 	}
    99 
   100 TInt WriteNumber(RFile &aFile)
   101 	{
   102 	TInt r;
   103 	Buffer[0]++;
   104 	r = aFile.Write(0,Buffer);
   105 	if (r==KErrNone)
   106 		return aFile.Flush();
   107 	else
   108 		return r; 
   109 	}
   110 
   111 
   112 
   113 // The r/w soaktest leaves the drive in a mess.
   114 // Formatting is needed afterwards.
   115 
   116 void silentFormat(TInt driveNo) 
   117 	{    
   118     TBuf<4> driveBuf=_L("?:\\");
   119     RFormat format;
   120     TInt    count;
   121     
   122 	driveBuf[0] = (TText)(driveNo + 'A');
   123     
   124     TInt r = format.Open(TheFs, driveBuf, EHighDensity, count);
   125     test(r == KErrNone);
   126     
   127     while(count) 
   128 		{
   129         r=format.Next(count);
   130         test(r == KErrNone);
   131 		}
   132     
   133     format.Close();
   134 	}
   135 
   136 // Finds the 1st r/w NAND drive, or checks the specified one fits requirements  
   137 
   138 static TInt FindFsNANDDrive()
   139 	{
   140 	TDriveList driveList;
   141 	TDriveInfo driveInfo;
   142 	TInt r=TheFs.DriveList(driveList);
   143     test(r == KErrNone);
   144 	
   145 	for (TInt drvNum= (DriveNumber<0)?0:DriveNumber; drvNum<KMaxDrives; ++drvNum)
   146 		{
   147 	    if(!driveList[drvNum])
   148 	        continue;   //-- skip unexisting drive
   149 
   150 	    test(TheFs.Drive(driveInfo, drvNum) == KErrNone);
   151 
   152 		if ((driveInfo.iMediaAtt&KMediaAttPageable) &&
   153 			(driveInfo.iType == EMediaNANDFlash) && 
   154 			(driveInfo.iDriveAtt & KDriveAttInternal))
   155 			{
   156 			TBool readOnly = driveInfo.iMediaAtt & KMediaAttWriteProtected;		// skip ROFS partitions
   157 			if(!readOnly)
   158 				{
   159 				if ((drvNum==DriveNumber) || (DriveNumber<0))		// only test if running on this drive
   160 					{
   161 					return (drvNum);
   162 					}
   163 				}
   164 			}
   165 		}
   166 	return (-1);
   167 	}
   168 
   169 
   170 //
   171 // Writes to main area for the entire disk and reads back to verify.
   172 // The function is called from TestNandAccuratcy, which will have also
   173 // started the background RepeatedPagingThread
   174 //
   175 void testWriteMain()
   176 	{
   177 	TInt i;
   178 	TInt r;
   179 	TInt changeCount=0;
   180 	TInt totChangeCount=0;
   181 	TInt cCount=0;
   182 	TInt fullcCount=0;
   183 	TInt oldPagedTrashCount=0;
   184 	TInt delta=0;
   185 	TInt high=0;
   186 	TInt tot=0;
   187 	TInt fullTot=0;
   188 	TInt blockNo;
   189 
   190 	// read size is 64K
   191 	TInt readSize = (64*1024);	
   192 	TInt64 size = DriveCaps.iSize - (DriveCaps.iSize % readSize);
   193 	
   194 	// print position every 128K
   195 	TInt64 printBlockPos = 128 * 1024;
   196 	test (size > printBlockPos);
   197 
   198 	// check for paging activity every 1MB
   199 	TInt64 checkChangePos = 1024*1024;
   200 	while (checkChangePos > size)
   201 		checkChangePos>>= 1;
   202 
   203 	
   204 	SDeferStats stats;
   205 	TInt pageGarbageCount=0;
   206 	TInt pageOtherCount=0;	
   207 	TInt normalGarbageCount=0;
   208 	TInt normalOtherCount=0;
   209 	
   210 	
   211 	Buffer.SetLength(2*readSize);
   212 
   213 	TPtr8 subBuf1(&Buffer[0],readSize);
   214 	TPtrC8 subBuf2(&Buffer[readSize], readSize);
   215 	
   216 	test.Printf(_L("Page size = %d\n"), DriveCaps.iNumBytesMain);
   217 	test.Printf(_L("Erase block size = %d\n"), DriveCaps.iEraseBlockSize);
   218 	test.Printf(_L("Media size (rounded down) = %ld\n"), size);
   219 
   220 	for(i = 0; i<readSize; i++)
   221 		Buffer[readSize+i] = (char)(i%100);
   222 
   223 	// Zero Stats
   224 	if(CtrlIoGetDeferStatsSupported)
   225 		{
   226 		TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   227  		test(Drive.ControlIO(KNandGetDeferStats,statsBuf,0)==KErrNone);
   228 		}
   229 
   230 
   231 	while (((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone))
   232 		{
   233 		for(TInt64 pos=0;
   234 			(pos<size) && ((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone);
   235 			pos+=(TUint)(readSize))
   236 			{
   237 			blockNo=I64LOW(pos / DriveCaps.iEraseBlockSize);
   238 			if ((pos % printBlockPos) == 0)
   239 				test.Printf(_L("Block %d at pos %lu \r"), blockNo, pos);
   240 
   241 			//write the pattern
   242 			r = Drive.Write(pos,subBuf2);
   243 			test(r==KErrNone);
   244 
   245 			//read back and verify
   246 			r = Drive.Read(pos,readSize,subBuf1);
   247 			test(r==KErrNone);
   248 
   249 			for(i=0;i<readSize;i++)
   250 				if(Buffer[i]!=Buffer[readSize+i])
   251 					{
   252 					r = KErrCorrupt;
   253 					break;
   254 					}
   255 			delta = PagedTrashCount-oldPagedTrashCount;
   256 			cCount++;
   257 			if (delta)
   258 				{	
   259 				if (delta>high)
   260 					high=delta;
   261 				tot+=delta;
   262 				
   263 				oldPagedTrashCount=PagedTrashCount;
   264 				changeCount++;
   265 				}
   266 
   267 			if ((pos > 0) && (pos % checkChangePos) == 0)
   268 				{
   269 				totChangeCount+=changeCount;
   270 				if(CtrlIoGetDeferStatsSupported)
   271 					{
   272 					test.Printf(_L("\nHigh%4d Avg%2d %d%% CC=%4d \n"), high, (TInt) (tot/cCount), (TInt)(changeCount*100)/cCount, totChangeCount);
   273 				
   274 					TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   275 					Drive.ControlIO(KNandGetDeferStats,statsBuf,0);
   276 					test.Printf(_L("PG %d PO %d(%d%%) NG %d NO %d\n"),stats.iPageGarbage,  stats.iPageOther, (TInt) ((stats.iPageOther*100)/cCount), stats.iNormalGarbage,  stats.iNormalOther);
   277 
   278 					test(stats.iPageOther>0);
   279 				 	pageGarbageCount+=stats.iPageGarbage; 
   280 				 	pageOtherCount+=stats.iPageOther;			 	
   281 				 	normalGarbageCount+=stats.iNormalGarbage; 
   282 				 	normalOtherCount+=stats.iNormalOther;			 	
   283 					}
   284 
   285 				high=0;
   286 				
   287 				fullTot+=tot;
   288 				tot=0;
   289 				
   290 				fullcCount+=cCount;
   291 				cCount=0;
   292 				changeCount=0;
   293 				}
   294 
   295 			test(r==KErrNone);
   296 			}	// for loop
   297 
   298 		if (CtrlIoGetDeferStatsSupported)
   299 			{
   300 			test.Printf(_L("\nTotals: Avg %2d %d%% CC=%4d \n"), fullTot/fullcCount, (TInt)(totChangeCount*100)/fullcCount, totChangeCount);
   301 			test.Printf(_L("PG %d PO %d(%d%%) NG %d NO %d\n"),pageGarbageCount,  pageOtherCount,(TInt) (pageOtherCount*100/fullcCount), normalGarbageCount,  normalOtherCount );
   302 			}
   303 
   304 		// If totChangeCount does not change, nand maybe busy waiting.
   305 		test(totChangeCount>0);
   306 		}	// while ()
   307 
   308 	if (GlobError!=KErrNone)
   309 		{
   310 		test.Printf(_L("\nPaging failed with %x\n"), GlobError);
   311 		test(0);
   312 		}
   313 	else
   314 		test.Printf(_L("\ndone\n"));
   315 	}
   316 
   317 
   318 TUint8 ReadByte(volatile TUint8* aPtr)
   319 	{
   320 	return *aPtr;
   321 	}
   322 
   323 #define READ(a) ReadByte((volatile TUint8*)(a))
   324 
   325 TUint32 RandomNo =0;
   326 
   327 TUint32 Random()
   328 	{
   329 	RandomNo = RandomNo*69069+1;
   330 	return RandomNo;
   331 	}
   332 
   333 
   334 // Many instances of this run while testWriteMain runs,
   335 // to cause random background paging.
   336 
   337 LOCAL_C TInt RepeatedPagingThread(TAny* aUseTb)
   338 	{
   339 	TBool trashBurst = EFalse;
   340 	// This makes the paging system continually page stuff.
   341 	// get info about a paged ROM...
   342 	
   343 	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   344 	TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
   345 	TUint size = romHeader->iPageableRomSize;
   346 	TInt pageSize = 0;
   347 	PagedTrashCount=1;
   348 
   349 	UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&pageSize,0);
   350 	RandomNo=123;
   351 	PagedTrashCount++;
   352 
   353 	while (Testing)
   354 		{
   355 		TInt r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   356 		if (Random() & 1)
   357 			User::AfterHighRes(500+Random() & 2047);
   358 
   359 		if (r<0)
   360 			{
   361 			GlobError=r;
   362 			PagedTrashCount=99;
   363 			return (KErrNone);
   364 			}
   365 		if (trashBurst)
   366 			{
   367 			if ((Random() & 0xf) == 0xf)
   368 				trashBurst=EFalse;
   369 			PagedTrashCount++;
   370 			}
   371 		else 
   372 			{
   373 			
   374 			for(TInt i=size/(pageSize); (i>0) && !trashBurst; --i)
   375 				{
   376 				READ(start+((TInt64(Random())*TInt64(size))>>32));
   377 				if ((RandomNo & 0x3f) == 0x3f)
   378 					{
   379 					trashBurst= (TBool) aUseTb;
   380 					}
   381 				PagedTrashCount++;
   382 				if (RandomNo & 1)
   383 					User::AfterHighRes(500+Random() & 2047);
   384 				}
   385 			}
   386 	
   387 		}
   388 	return(KErrNone);
   389 	}
   390 	
   391 
   392 // This starts up multiple instances of repeatedPagingThread, and runs testWriteMain.
   393 // After its done, it calls format, to clean up the drive.
   394 
   395 void TestNandAccuratcy()
   396 	{
   397 	RThread thisThread;
   398 	const TInt KNoThreads=10;
   399 	TInt i;
   400 	test.Printf(_L("Reset concurrency stats\n"));
   401 
   402 	i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetConcurrencyInfo,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
   403 	test(i==KErrNone || i==KErrNotSupported);
   404 	if(i==KErrNotSupported)
   405 		test.Printf(_L("Concurrency stats not supported on this build\n"));
   406 	i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetPagingBenchmark,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
   407 	test(i==KErrNone || i==KErrNotSupported);
   408 	if(i==KErrNotSupported)
   409 		test.Printf(_L("Benchmark stats not supported on this build\n"));
   410 
   411 	if (Maxloops>0)
   412 		{
   413 		TRequestStatus stat[KNoThreads];
   414 		// Start Read Test
   415 		RThread repeatedPagingThread[KNoThreads];
   416 		
   417 		test.Next(_L("Read/Write and Page test"));
   418 
   419 		Testing=ETrue;
   420 		for (i=0; i<KNoThreads; i++)
   421 			{
   422 			
   423 			test(repeatedPagingThread[i].Create(_L(""),RepeatedPagingThread,KDefaultStackSize,NULL,(TAny*) ETrue)==KErrNone);
   424 			repeatedPagingThread[i].Logon(stat[i]);
   425 			test(stat[i]==KRequestPending);	
   426 			repeatedPagingThread[i].Resume();
   427 			}
   428 		// Start repeated paging.
   429 		thisThread.SetPriority(EPriorityMore);
   430 		testWriteMain();
   431 		Testing = 0;
   432 		thisThread.SetPriority(EPriorityNormal);
   433 		for (i=0; i<KNoThreads; i++)
   434 	 		User::WaitForRequest(stat[i]);
   435 
   436 		test.Printf(_L("Collect concurrency stats\n"));
   437 		SMediaROMPagingConcurrencyInfo info;
   438 		SPagingBenchmarkInfo infoBench;
   439 		i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMConcurrencyInfo,(TAny*)locDriveNumber,&info);
   440 		test(i==KErrNone || i==KErrNotSupported);
   441 		TInt r=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMPagingBenchmark,(TAny*)locDriveNumber,&infoBench);
   442 		test(r==KErrNone || r==KErrNotSupported);
   443 		if(i==KErrNone)
   444 			{
   445 			test.Printf(_L("Media concurrency stats:\n\n"));
   446 			test.Printf(_L("The total number of page in requests issued whilst processing other page in requests: %d\n"),info.iTotalConcurrentReqs);
   447 			test.Printf(_L("The total number of page in requests issued with at least one queue not empty: %d\n"),info.iTotalReqIssuedNonEmptyQ);
   448 			test.Printf(_L("The maximum number of pending page in requests in the main queue any time during this session: %d\n"),info.iMaxReqsInPending);
   449 			test.Printf(_L("The maximum number of pending page in requests in the deferred queue any time during this session: %d\n"),info.iMaxReqsInDeferred);
   450 			test.Printf(_L("The total number of page in requests first-time deferred during this session: %d\n"),info.iTotalFirstTimeDeferrals);
   451 			test.Printf(_L("The total number of page in requests re-deferred during this session: %d\n"),info.iTotalReDeferrals);
   452 			test.Printf(_L("The maximum number of deferrals of any single page in request during this session: %d\n"),info.iMaxDeferrals);
   453 			test.Printf(_L("The total number of times the main queue was emptied when completing an asynchronous request during this session: %d\n"),info.iTotalSynchEmptiedMainQ);
   454 			test.Printf(_L("The total number of page in requests serviced from main queue when completing an asynchronous request: %d\n"),info.iTotalSynchServicedFromMainQ);
   455 			test.Printf(_L("The total number of page in requests deferred after being picked out of main queue when completing an asynchronous request: %d\n"),info.iTotalSynchDeferredFromMainQ);
   456 			test.Printf(_L("The total number of times the page in DFC run with an empty main queue during this session: %d\n"),info.iTotalRunDry);
   457 			test.Printf(_L("The total number of dry runs of paging DFC avoided during this session: %d\n"),info.iTotalDryRunsAvoided);
   458 			}
   459 
   460 		if(r==KErrNone)
   461 			{
   462 			TInt freq = 0;
   463 			r = HAL::Get(HAL::EFastCounterFrequency, freq);
   464 			if (r==KErrNone)
   465 				{
   466 				TReal mult = 1000000.0 / freq;
   467 				TReal min = 0.0;
   468 				TReal max = 0.0;
   469 				TReal avg = 0.0;
   470 				if (infoBench.iCount != 0)
   471 					{
   472 					min = infoBench.iMinTime * mult;
   473 					max = infoBench.iMaxTime * mult;
   474 					avg = (infoBench.iTotalTime * mult) / infoBench.iCount;
   475 					}
   476 				test.Printf(_L("Media benchmarks:\n\n"));
   477 				test.Printf(_L("The total number of page in requests issued: %d\n"),infoBench.iCount);
   478 				test.Printf(_L("The average latency of any page in request in the Media subsystem: %9.1f(us)\n"),avg);
   479 				test.Printf(_L("The maximum latency of any page in request in the Media subsystem: %9.1f(us)\n"),max);
   480 				test.Printf(_L("The minimum latency of any page in request in the Media subsystem: %9.1f(us)\n"),min);
   481 				}
   482 			}
   483 
   484 		test.Printf(_L("Formatting...\n"));
   485 		silentFormat(DriveNumber);
   486 		}
   487 		else
   488 			test.Next(_L("Read/Write test - Skipped!"));
   489 
   490 	}
   491 	
   492 
   493 // ************************************************************************************
   494 	
   495 	
   496 // This code causes a flush
   497 // It is done in a second thread to see if you really do get just
   498 // one deferral, with the other page requests just waiting in line.
   499 // (Paging is not re-entrant)
   500 
   501 TInt PagesBeingPaged=0;
   502 RMutex PageMutex;
   503 RSemaphore PageSemaphore;
   504 RSemaphore PageDoneSemaphore;
   505  
   506 LOCAL_C TInt CausePage(TAny*)
   507 	{	
   508 	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   509 	TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
   510 	TUint size = romHeader->iPageableRomSize;
   511 	TUint8* addr=NULL;
   512 	TBool flush;
   513 	while (Testing)
   514 		{
   515 			PageSemaphore.Wait(); // wait for main thread to want paging.
   516 			flush = (PagesBeingPaged==0);
   517 			addr=start+((TInt64(Random())*TInt64(size))>>32);	
   518 			PageDoneSemaphore.Signal(); // Acknolage request.
   519 
   520 			PageMutex.Wait();
   521 			PagesBeingPaged++;
   522 			PageMutex.Signal();
   523 
   524 			if (flush)
   525 				UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   526 			READ(addr);
   527 
   528 			PageMutex.Wait();
   529 			PagesBeingPaged--;
   530 			PageMutex.Signal();
   531 		}
   532 	return 0;
   533 	}
   534 
   535 
   536 // TestDefered causes garbage collection, and then triggers paging to happen, which should be defered.
   537 // One would only expect one defered request, as the paging system is not reentrant, but this is checked.
   538 
   539 void TestDefered()
   540 	{
   541 	if (MaxDeferLoops==0)
   542 		{
   543 		test.Next(_L("Defering test - Skipped!"));
   544 		return;
   545 		}
   546 		
   547 	TInt timeout;
   548 	TInt writesNeeded=100;
   549 	TInt r = KErrNone;
   550 	RFile tempFile;
   551 	TInt i;
   552 	TInt ii;
   553 	TInt runs=0;
   554 
   555 	SDeferStats stats;
   556 	TInt pageGarbageCount=0;
   557 	TInt pageOtherCount=0;	
   558 	TInt normalGarbageCount=0;
   559 	TInt normalOtherCount=0;
   560 
   561 
   562 	// Set up thread sync
   563 	test(PageMutex.CreateLocal()==KErrNone);
   564 	test(PageSemaphore.CreateLocal(0)==KErrNone);
   565 	test(PageDoneSemaphore.CreateLocal(0)==KErrNone);
   566 
   567 		
   568 
   569 	const TInt KMaxPageThreads = 2;
   570 	UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   571 	// Set up threads
   572 	RThread pageThread[KMaxPageThreads];
   573 	TRequestStatus stat[KMaxPageThreads];
   574 	Testing=ETrue;
   575 	for (i=0; i<KMaxPageThreads; i++)
   576 		{
   577 		test(pageThread[i].Create(_L(""),CausePage,KDefaultStackSize,NULL,NULL)==KErrNone);
   578 		pageThread[i].Logon(stat[i]);
   579 		test(stat[i]==KRequestPending);
   580 		pageThread[i].SetPriority(EPriorityMore);
   581 		pageThread[i].Resume();
   582 		}
   583 		
   584 		
   585 	test.Next(_L("Defering test"));
   586 
   587 	// clear counters
   588 	TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   589 	test(Drive.ControlIO(KNandGetDeferStats,statsBuf,0)==KErrNone);
   590 	
   591 	CreateFile(tempFile,_L("nandpage.txt"));
   592 		
   593 	 	
   594 	for (ii=0; ii<MaxDeferLoops; ii++)  // Repeat the test, 'MaxDeferLoops' number of times.  This can be set on cammand line.
   595 		{
   596 		timeout=20;
   597 		do  // while ((pageGarbageCount==0) && (timeout>0));
   598 			// ie, while garbage collection hasn't happened, or timed out
   599 			{
   600 			timeout--;
   601 			pageGarbageCount=0;
   602 			pageOtherCount=0;
   603 			normalGarbageCount=0;
   604 			normalOtherCount=0;
   605 			
   606 			// Give somethng for garbage collector to collect	
   607 			for (i=0; i<writesNeeded; i++)
   608 		 		test(WriteNumber(tempFile)==KErrNone);
   609 			 
   610 			// Force Collection.  (Normally only happens in Idle) 		
   611 		 	r = Drive.ControlIO(KNandCollectGarbage,NULL,NULL);
   612 		 	test(r==KErrNone);
   613 			 	
   614 		 	// Since garbage Colleciton should be going now, watch it, until its finished. 
   615 			do
   616 		 		{
   617 				runs = PagesBeingPaged;
   618 				for (i=runs; i<KMaxPageThreads; i++)
   619 		 			PageSemaphore.Signal(); // Trigger Paging.
   620 
   621 				for (i=runs; i<KMaxPageThreads; i++)
   622 		 			PageDoneSemaphore.Wait();
   623 					
   624 				TInt tries = 10;
   625 				do { // If we get zero hits, maybe the page hasnt hit yet.
   626 					tries--;
   627 					User::AfterHighRes(1000+Random() & 2047);  // Throw some uncertainly into things
   628 				
   629 					TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   630 			 		r = Drive.ControlIO(KNandGetDeferStats,statsBuf,0);
   631 					test (r == KErrNone);
   632 			 		pageGarbageCount+=stats.iPageGarbage; 
   633 			 		pageOtherCount+=stats.iPageOther;			 	
   634 			 		normalGarbageCount+=stats.iNormalGarbage; 
   635 			 		normalOtherCount+=stats.iNormalOther;
   636 					} while ((pageGarbageCount==0) && (tries>0)); // If we get zero hits, maybe the page hasnt hit yet
   637 		 		}
   638 		 	while (stats.iPageGarbage>0); // Keep going until collection seems to have finished.
   639 		 	
   640 		 	// The paging system is not reentrant, so should never get more then one.
   641 		 	test(stats.iPageGarbage<2);
   642 		 	
   643 			test.Printf(_L("%d: PG %d PO %d NG %d NO %d\n"),ii,pageGarbageCount,  pageOtherCount, normalGarbageCount,  normalOtherCount );
   644 			// if no collection, probebly didnt write enough to trigger it, so up the quantity.
   645 			if (pageGarbageCount==0)
   646 				{		
   647 				writesNeeded+=writesNeeded/2;
   648 				test.Printf(_L("Writes needed = %d\n"),writesNeeded);
   649 				}
   650 				
   651 			}
   652 		while ((pageGarbageCount==0) && (timeout>0));
   653 		test(timeout>0);
   654 			
   655 		} // end for MaxDeferLoops.
   656 
   657 	// Clean up. . . . .
   658 
   659 	Testing=EFalse;  // Setting this causes the CausePage threads to exit.
   660 
   661 	// Wait for threads to exit, signaling the semaphore in case they where waiting on it.
   662 	for (i=0; i<KMaxPageThreads; i++)
   663 		PageSemaphore.Signal();
   664 	for (i=0; i<KMaxPageThreads; i++)
   665 		User::WaitForRequest(stat[i]);
   666 
   667 	PageMutex.Close();	
   668 	PageSemaphore.Close();
   669 	PageDoneSemaphore.Close();
   670 	CloseAndDestroy(tempFile);
   671 	}
   672 
   673 
   674 // ************************************************************************************
   675 
   676 //
   677 // The gubbins that starts all the tests
   678 //
   679 // ParseCommandLine reads the arguments and sets globals accordingly.
   680 //
   681 
   682 void ParseCommandLine()
   683 	{
   684 	TBuf<32> args;
   685 	User::CommandLine(args);
   686 	TLex lex(args);
   687 	
   688 	FOREVER
   689 		{
   690 		
   691 		TPtrC token=lex.NextToken();
   692 		if(token.Length()!=0)
   693 			{
   694 			if ((token.Length()==2) && (token[1]==':'))
   695 				DriveNumber=User::UpperCase(token[0])-'A';
   696 			else if (token.Length()==1)
   697 				{
   698 				TChar driveLetter = User::UpperCase(token[0]); 
   699 				if ((driveLetter>='A') && (driveLetter<='Z'))
   700 					DriveNumber=driveLetter - (TChar) 'A';
   701 				else 
   702 					test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
   703 				}
   704 			else if ((token==_L("help")) || (token==_L("-h")) || (token==_L("-?")))
   705 				{
   706 				test.Printf(_L("\nUsage:  t_nandpaging <driveletter> [rwsoak <cc>] [defer <c>]\n'-' indicated infinity.\n\n"));
   707 				test.Getch();
   708 				Maxloops=0;
   709 				}
   710 			else if (token==_L("rwsoak"))
   711 				{
   712 				TPtrC val=lex.NextToken();
   713 				TLex lexv(val);
   714 				TInt v;
   715 
   716 				if (val==_L("-"))
   717 					Forever=ETrue;
   718 				else
   719 					if (lexv.Val(v)==KErrNone)
   720 						Maxloops=v;
   721 					else
   722 						test.Printf(_L("Bad value for rwsoak '%S' was ignored.\n"), &val);
   723 				}
   724 			else if (token==_L("defer"))
   725 				{
   726 				TPtrC val=lex.NextToken();
   727 				TLex lexv(val);
   728 				TInt v;
   729 
   730 				if (val==_L("-"))
   731 					MaxDeferLoops=KMaxTInt;
   732 				else
   733 					if (lexv.Val(v)==KErrNone)
   734 						MaxDeferLoops=v;
   735 					else
   736 						test.Printf(_L("Bad value for defer '%S' was ignored.\n"), &val);
   737 				}	
   738 			else
   739 				test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
   740 			}
   741 		else
   742 			break;
   743 		
   744 		}
   745 	}
   746 
   747 //
   748 // E32Main
   749 //
   750 
   751 TInt E32Main()
   752 	{
   753 	TInt r;
   754 	test.Title();
   755 
   756 	test.Printf(_L("key\n---\n"));	
   757 	test.Printf(_L("PG: Paging requests defered due to Garbage\n"));
   758 	test.Printf(_L("PO: Paging requests defered due to other operations\n"));
   759 	test.Printf(_L("NG: Normal requests defered due to Garbage\n"));
   760 	test.Printf(_L("NO: Normal requests defered due to other operations\n\n"));
   761 
   762 	
   763 	test.Start(_L("Check that the rom is paged"));
   764 	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   765 
   766 	if (romHeader->iPageableRomStart==NULL)
   767 		test.Printf(_L("Test ROM is not paged - test skipped!\r\n"));
   768 	else
   769 		{
   770 		ParseCommandLine();	
   771 		test(TheFs.Connect()==KErrNone);
   772 
   773 		r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   774 		if(r<0)
   775 			{
   776 			test.Printf(_L("DemandPagingFlushPages Error = %d\n"),r);
   777 			test(0);
   778 			}
   779 
   780 		DriveNumber = FindFsNANDDrive();	
   781 		
   782 		if(DriveNumber<0)
   783 			test.Printf(_L("NAND Flash not found - test skipped!\r\n"));
   784 		else
   785 			{
   786 			RFile file;
   787 			TBuf<256> fileName;	
   788 			fileName.Append((TChar)('A'+DriveNumber));
   789 			fileName+=_L(":\\f32-tst\\");
   790 			TInt r=TheFs.MkDirAll(fileName);
   791 			test(r==KErrNone || r== KErrAlreadyExists);
   792 			fileName += _L("annoyingflies.txt");
   793 			r=file.Replace(TheFs,fileName,EFileWrite);
   794 			if (r!=KErrNone)
   795 				test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
   796 			test(r==KErrNone);
   797 			r=file.Write(_L8("Flies as big as sparrows indoletly buzzing in the warm air, heavy with the stench of rotting carcasses"));
   798 			if (r!=KErrNone)
   799 				test.Printf(_L("Error %d: could not write to file\n"),r);
   800 			test(r==KErrNone);
   801 
   802 			test(file.Flush() == KErrNone);
   803 
   804 			SBlockMapInfo info;
   805 			TInt64 start=0;
   806 			r=file.BlockMap(info,start, -1,ETestDebug);
   807 			if (r!=KErrNone && r!=KErrCompletion)
   808 				test.Printf(_L("Error %d: could not obtain block map\n"),r);
   809 			test(r==KErrNone || r==KErrCompletion);
   810 			locDriveNumber=info.iLocalDriveNumber;
   811 			test.Printf(_L("Found drive: %c (NAND drive %d)\r\n"), DriveNumber+'A',locDriveNumber);
   812 			file.Close();
   813 			
   814 			// Connect to device driver
   815 			TBool changeFlag = EFalse;
   816 			r = Drive.Connect(locDriveNumber,changeFlag);
   817 			TPckg<TLocalDriveCapsV4>	capsPack(DriveCaps);
   818 			Drive.Caps(capsPack);
   819 			test(r == KErrNone);
   820 
   821 			r = Drive.ControlIO(KNandCollectGarbage,NULL,NULL);
   822 			if (r!=KErrNone)
   823 				{
   824 				test.Printf(_L("LocalDrive does not support the KNandCollectGarbage ControlIO request\n"));
   825 				CtrlIoCollectGarbageSupported = EFalse;
   826 				}
   827 
   828 			SDeferStats stats;
   829 			TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   830 	 		r = Drive.ControlIO(KNandGetDeferStats,statsBuf,0);
   831 			if (r == KErrNone)
   832 				{
   833 				if (stats.iSynchronousMediaDriver)
   834 					{
   835 					test.Printf(_L("Media drive is synchronous - test skipped!\r\n"));
   836 					test.End();
   837 					return 0;
   838 					}
   839 				}
   840 			else
   841 				{
   842 				test.Printf(_L("LocalDrive does not support the KNandGetDeferStats ControlIO request\n"));
   843 				CtrlIoGetDeferStatsSupported = EFalse;
   844 				}
   845 
   846 
   847 			test.Printf(_L("LocalDrive Connected\n"));
   848 			//
   849 			// Run tests	
   850 			//
   851 			TestNandAccuratcy();
   852 			if(CtrlIoCollectGarbageSupported && CtrlIoGetDeferStatsSupported)
   853 				TestDefered();
   854 			// 
   855 			// Free device and end test program
   856 			//
   857 			Drive.Disconnect();
   858 			}	
   859 		}
   860 		
   861 	test.End();
   862 	return 0;
   863 	}
   864 
   865