kerneltest/f32test/demandpaging/t_mmcpaging.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 6 0173bcd7697c
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32test\mmu\t_mmcpaging.cpp
// Suite of tests specifically to test the demand paging subsystem when
// booted from MMC rather than NAND.
// 002 Read/Write and Page test
// 
//

//! @SYMTestCaseID			KBASE-T_MMCPAGING-0331
//! @SYMTestType			UT
//! @SYMPREQ				PREQ1110
//! @SYMTestCaseDesc		Demand Paging MMC Paging tests.
//! @SYMTestActions			001 Check that the rom is paged
//! @SYMTestExpectedResults All tests should pass.
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented

#include <e32test.h>
RTest test(_L("T_MMCPAGING"));

#include <e32rom.h>
#include <e32svr.h>
#include <u32hal.h>
#include <f32file.h>
#include <f32dbg.h>
#include <d32locd.h>
#include <hal.h>
#define __TEST_PAGING_MEDIA_DRIVER__
#include "mmcdp.h"



TInt DriveNumber=-1;   // Parameter - Which drive?  -1 = autodetect.
TInt locDriveNumber;

TInt MaxDeferLoops=40; // Parameter - Defer test, for how long?
TInt Maxloops=400;	   // Parameter - RW Soak, for how long?
TBool Forever=EFalse;  // Parameter - RW Soak forever?

TBool Testing=ETrue;	// Used to communicate when testing has finished between threads.

RFs TheFs;
TBusLocalDrive Drive;
TLocalDriveCapsV4 DriveCaps;

TInt PagedTrashCount=0; // Incremented by threads, is used to detect preemption.
TInt GlobError=KErrNone; // To communicate an error between threads.
TBool CtrlIOSupported=ETrue;


//const TInt KDiskSectorShift = 9;
const TInt KBufSizeInBytes = (32 * 1024);

LOCAL_D TBuf8<KBufSizeInBytes> Buffer;



// Three functions for the garbage test.
// CreateFile creates a file, and sets up the buffer for WriteNumber.
// After the code has finished writing numbers to the start,
// CloseAndDestroy cleans up.

void CreateFile(RFile &aFile,const TDesC& aFileName)
	{
	TBuf<256> fileName;	
	fileName.Append((TChar)('A'+DriveNumber));
	fileName+=_L(":\\f32-tst\\");
	TInt r=TheFs.MkDirAll(fileName);
	test(r==KErrNone || r== KErrAlreadyExists);

	fileName += aFileName;	

	r=aFile.Replace(TheFs,fileName,EFileWrite);
	if (r!=KErrNone)
		test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
	test(r==KErrNone);	
	Buffer.SetLength(4);
	}
	
void CloseAndDestroy(RFile &aFile)
	{
	TBuf<256> fileName;	
	aFile.FullName(fileName);
	aFile.Close();
	TheFs.Delete(fileName);
	}

TInt WriteNumber(RFile &aFile)
	{
	TInt r;
	Buffer[0]++;
	r = aFile.Write(0,Buffer);
	if (r==KErrNone)
		return aFile.Flush();
	else
		return r; 
	}



// Finds the 1st MMC drive, or checks the specified one fits requirements  

static TInt FindFsMMCDrive()
	{
	TDriveList driveList;
	TDriveInfo driveInfo;
	TInt r=TheFs.DriveList(driveList);
    test(r == KErrNone);

	TInt drvNum = DriveNumber;
	if (drvNum<0)
		drvNum = 0;
	do
		{
	    if(!driveList[drvNum])
	        continue;   //-- skip unexisting drive

	    test(TheFs.Drive(driveInfo, drvNum) == KErrNone);

		if(driveInfo.iMediaAtt&KMediaAttPageable)
			{
			// Internal MMC ?
			if (driveInfo.iType == EMediaHardDisk && 
				(driveInfo.iDriveAtt & KDriveAttInternal) &&
				(!(driveInfo.iDriveAtt & KDriveAttRemovable)))
				return (drvNum);
			}
		}
	while(DriveNumber<0 && ++drvNum<KMaxDrives);

	return (-1);
	}


//
// Writes to main area for the entire disk and reads back to verify.
// The function is called from TestMmcAccuratcy, which will have also
// started the background RepeatedPagingThread
//
void testWriteMain()
	{
	TInt i;
	TInt r;
	TInt changeCount=0;
	TInt totChangeCount=0;
	TInt cCount=0;
	TInt fullcCount=0;
	TInt oldPagedTrashCount=0;
	TInt delta=0;
	TInt high=0;
	TInt tot=0;
	TInt fullTot=0;
	TInt blockNo;
	
	SMmcStats stats;
	TInt reqPageCount=0;	
	TInt reqNormalCount=0;

	
	TInt readSize = KBufSizeInBytes/2;
	TInt writeSize = KBufSizeInBytes/2;

	Buffer.SetLength(2*readSize);

	TPtr8 subBuf1(&Buffer[0],readSize);
	TPtrC8 subBuf2(&Buffer[readSize], readSize);
	
	test.Printf(_L("writeSize = %d\n"), writeSize);

//	TInt64 size = DriveCaps.iSize - (DriveCaps.iSize % readSize);
	
	for(i = 0; i<readSize; i++)
		Buffer[readSize+i] = (char)(i%100);

	// Zero Stats
	if(CtrlIOSupported)
		{
		TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
	 	test(Drive.ControlIO(KMmcGetStats,statsBuf,0) == KErrNone);
		}


	TFileName fileName = _L("?:\\f32-tst\\mmcpage.txt");
	fileName[0] = (TText) ('A'+DriveNumber);


	r = TheFs.MkDirAll(fileName);
	test(r==KErrNone || r== KErrAlreadyExists);
//	fileName += KTempFileName;	
	RFile tempFile;
	r=tempFile.Replace(TheFs,fileName,EFileWrite);
	if (r!=KErrNone)
		test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
	test(r==KErrNone);	

	TVolumeInfo volInfo;
	r = TheFs.Volume(volInfo, DriveNumber);
	test (r == KErrNone);

	
	TInt64 size = volInfo.iFree - (volInfo.iFree % readSize);
	TInt maxFileSize = (size > KMaxTInt) ? KMaxTInt : (TInt) size;
	
	test.Printf(_L("Volume size %ld, free %ld maxFileSize %d file '%S'\n"), volInfo.iSize, volInfo.iFree, maxFileSize, &fileName);
	
	while (((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone))
		{
		
		for(TInt pos=0;
			((pos+writeSize) < maxFileSize) && ((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone);
			pos+=(TUint)(readSize))
			{
			blockNo=I64LOW(pos / writeSize);
			if (pos % (writeSize) == 0)
				test.Printf(_L("Block %d at %u \r"), blockNo, I64LOW(pos));

			//write the pattern
			r = tempFile.Write(pos,subBuf2);
			if (r != KErrNone)
				test.Printf(_L("Write failed %d"), r);
			test(r==KErrNone);

			//read back and verify
			r = tempFile.Read(pos,subBuf1,readSize);
			test(r==KErrNone);

			for(i=0;i<readSize;i++)
				if(Buffer[i]!=Buffer[readSize+i])
					{
					r = KErrCorrupt;
					break;
					}
			delta = PagedTrashCount-oldPagedTrashCount;
			cCount++;
			if (delta)
				{	
				if (delta>high)
					high=delta;
				tot+=delta;
				
				oldPagedTrashCount=PagedTrashCount;
				changeCount++;
				}
			if (pos % (writeSize) == 0)
				{				

				if ((blockNo%80==0) && (blockNo!=0))
					{
					totChangeCount+=changeCount;
					if(CtrlIOSupported)
						{
						test.Printf(_L("High%4d Avg%2d %d%% CC=%4d \n"), high, (TInt) (tot/cCount), (TInt)(changeCount*100)/cCount, totChangeCount);
						
						TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
						Drive.ControlIO(KMmcGetStats,statsBuf,0);
						test.Printf(_L("PR %d(%d%%) NR %d\n"), stats.iReqPage, (TInt) ((stats.iReqPage*100)/cCount), stats.iReqNormal);

						test(stats.iReqPage>0);
				 		reqPageCount+=stats.iReqPage;			 	
				 		reqNormalCount+=stats.iReqNormal;			 	
						}

					high=0;
					
					fullTot+=tot;
					tot=0;
					
					fullcCount+=cCount;
					cCount=0;
					changeCount=0;
					}
						
				}
			test(r==KErrNone);
			}
		if(CtrlIOSupported)
			{
			test.Printf(_L("Totals: Avg %2d %d%% CC=%4d \n"), fullTot/fullcCount, (TInt)(totChangeCount*100)/fullcCount, totChangeCount);
			test.Printf(_L("PR %d(%d%%) NR %d\n"), reqPageCount,(TInt) (reqPageCount*100/fullcCount), reqNormalCount );
			}

		// If totChangeCount does not change, mmc maybe busy waiting.
		test(totChangeCount>0);
		}


	tempFile.Close();
	r = TheFs.Delete(fileName);
	test (r == KErrNone);
	
	if (GlobError!=KErrNone)
		{
		test.Printf(_L("\nPageing failed with %x\n"), GlobError);
		test(0);
		}
	else
		test.Printf(_L("\ndone\n"));
	}


TUint8 ReadByte(volatile TUint8* aPtr)
	{
	return *aPtr;
	}

#define READ(a) ReadByte((volatile TUint8*)(a))

TUint32 RandomNo =0;

TUint32 Random()
	{
	RandomNo = RandomNo*69069+1;
	return RandomNo;
	}


// Many instances of this run while testWriteMain runs,
// to cause random background paging.

LOCAL_C TInt RepeatedPagingThread(TAny* aUseTb)
	{
//	RTest test(_L("RepeatedPagingThread"));
	TBool trashBurst = EFalse;
	// This makes the paging system continually page stuff.
	// get info about a paged ROM...
	
	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
	TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
	TUint size = romHeader->iPageableRomSize;
	TInt pageSize = 0;
	PagedTrashCount=1;

	UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&pageSize,0);
	RandomNo=123;
	PagedTrashCount++;

	while (Testing)
		{
		TInt r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
		if (Random() & 1)
			User::AfterHighRes(500+Random() & 2047);

		if (r<0)
			{
			GlobError=r;
			PagedTrashCount=99;
			return (KErrNone);
			}
		if (trashBurst)
			{
			if ((Random() & 0xf) == 0xf)
				trashBurst=EFalse;
			PagedTrashCount++;
			}
		else 
			{
			
			for(TInt i=size/(pageSize); (i>0) && !trashBurst; --i)
				{
				READ(start+((TInt64(Random())*TInt64(size))>>32));
				if ((RandomNo & 0x3f) == 0x3f)
					{
					trashBurst= (TBool) aUseTb;
					}
				PagedTrashCount++;
				if (RandomNo & 1)
					User::AfterHighRes(500+Random() & 2047);
				}
			}
	
		}
	return(KErrNone);
	}
	

// This starts up multiple instances of repeatedPagingThread, and runs testWriteMain.
// After its done, it calls format, to clean up the drive.

void TestMmcAccuratcy()
	{
	RThread thisThread;
	const TInt KNoThreads=10;
	TInt i;
	test.Printf(_L("Reset stats\n"));

	i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetConcurrencyInfo,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
	test(i==KErrNone || i==KErrNotSupported);
	if(i==KErrNotSupported)
		test.Printf(_L("Concurrency stats not supported on this build\n"));
	i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetPagingBenchmark,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
	test(i==KErrNone || i==KErrNotSupported);
	if(i==KErrNotSupported)
		test.Printf(_L("Benchmark stats not supported on this build\n"));

	if (Maxloops>0)
		{
		TRequestStatus stat[KNoThreads];
		// Start Read Test
		RThread repeatedPagingThread[KNoThreads];
		
		test.Next(_L("Read/Write and Page test"));

		for (i=0; i<KNoThreads; i++)
			{
			test(repeatedPagingThread[i].Create(_L(""),RepeatedPagingThread,KDefaultStackSize,NULL,(TAny*) ETrue)==KErrNone);
			repeatedPagingThread[i].Logon(stat[i]);
			test(stat[i]==KRequestPending);	
			repeatedPagingThread[i].Resume();
			}
		// Start repeated paging.
		thisThread.SetPriority(EPriorityMore);
		Testing=ETrue;
		testWriteMain();
		Testing = 0;
		thisThread.SetPriority(EPriorityNormal);
		for (i=0; i<KNoThreads; i++)
	 		User::WaitForRequest(stat[i]);
	 		
		test.Printf(_L("Collect concurrency stats\n"));
		SMediaROMPagingConcurrencyInfo info;
		SPagingBenchmarkInfo infoBench;
		i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMConcurrencyInfo,(TAny*)locDriveNumber,&info);
		test(i==KErrNone || i==KErrNotSupported);
		TInt r=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMPagingBenchmark,(TAny*)locDriveNumber,&infoBench);
		test(r==KErrNone || r==KErrNotSupported);
		if(i==KErrNone)
			{
			test.Printf(_L("Media concurrency stats:\n\n"));
			test.Printf(_L("The total number of page in requests issued whilst processing other page in requests: %d\n"),info.iTotalConcurrentReqs);
			test.Printf(_L("The total number of page in requests issued with at least one queue not empty: %d\n"),info.iTotalReqIssuedNonEmptyQ);
			test.Printf(_L("The maximum number of pending page in requests in the main queue any time during this session: %d\n"),info.iMaxReqsInPending);
			test.Printf(_L("The maximum number of pending page in requests in the deferred queue any time during this session: %d\n"),info.iMaxReqsInDeferred);
			test.Printf(_L("The total number of page in requests first-time deferred during this session: %d\n"),info.iTotalFirstTimeDeferrals);
			test.Printf(_L("The total number of page in requests re-deferred during this session: %d\n"),info.iTotalReDeferrals);
			test.Printf(_L("The maximum number of deferrals of any single page in request during this session: %d\n"),info.iMaxDeferrals);
			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);
			test.Printf(_L("The total number of page in requests serviced from main queue when completing an asynchronous request: %d\n"),info.iTotalSynchServicedFromMainQ);
			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);
			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);
			test.Printf(_L("The total number of dry runs of paging DFC avoided during this session: %d\n"),info.iTotalDryRunsAvoided);
			}

		if(r==KErrNone)
			{
			TInt freq = 0;
			r = HAL::Get(HAL::EFastCounterFrequency, freq);
			if (r==KErrNone)
				{
				TReal mult = 1000000.0 / freq;
				TReal min = 0.0;
				TReal max = 0.0;
				TReal avg = 0.0;
				if (infoBench.iCount != 0)
					{
					min = infoBench.iMinTime * mult;
					max = infoBench.iMaxTime * mult;
					avg = (infoBench.iTotalTime * mult) / infoBench.iCount;
					}
				test.Printf(_L("Media benchmarks:\n\n"));
				test.Printf(_L("The total number of page in requests issued: %d\n"),infoBench.iCount);
				test.Printf(_L("The average latency of any page in request in the Media subsystem: %9.1f(us)\n"),avg);
				test.Printf(_L("The maximum latency of any page in request in the Media subsystem: %9.1f(us)\n"),max);
				test.Printf(_L("The minimum latency of any page in request in the Media subsystem: %9.1f(us)\n"),min);
				}
			}
		}
		else
			test.Next(_L("Read/Write test - Skipped!"));

	}
	

// ************************************************************************************
	
	
/*
// This code causes a flush
// It is done in a second thread to see if you really do get just
// one deferral, with the other page requests just waiting in line.
// (Paging is not re-entrant)

TInt PagesBeingPaged=0;
RMutex PageMutex;
RSemaphore PageSemaphore;
RSemaphore PageDoneSemaphore;
 
LOCAL_C TInt CausePage(TAny*)
	{	
	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
	TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
	TUint size = romHeader->iPageableRomSize;
	TUint8* addr=NULL;
	TBool flush;
	while (Testing)
		{
		// Wait on semaphore
		PageSemaphore.Wait();
		flush = (PagesBeingPaged==0);
		PagesBeingPaged++;
		addr=start+((TInt64(Random())*TInt64(size))>>32);	
		PageDoneSemaphore.Signal();
		if (flush)
			UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
		READ(addr);
		PageMutex.Wait();
		PagesBeingPaged--;
		PageMutex.Signal();
		}
	return 0;
	}
*/

// ************************************************************************************

//
// The gubbins that starts all the tests
//
// ParseCommandLine reads the arguments and sets globals accordingly.
//

void ParseCommandLine()
	{
	TBuf<32> args;
	User::CommandLine(args);
	TLex lex(args);
	
	FOREVER
		{
		
		TPtrC token=lex.NextToken();
		if(token.Length()!=0)
			{
			if ((token.Length()==2) && (token[1]==':'))
				DriveNumber=User::UpperCase(token[0])-'A';
			else if (token.Length()==1)
				{
				TChar driveLetter = User::UpperCase(token[0]); 
				if ((driveLetter>='A') && (driveLetter<='Z'))
					DriveNumber=driveLetter - (TChar) 'A';
				else 
					test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
				}
			else if ((token==_L("help")) || (token==_L("-h")) || (token==_L("-?")))
				{
				test.Printf(_L("\nUsage:  t_mmcpaging <driveletter> [rwsoak <cc>] [defer <c>]\n'-' indicated infinity.\n\n"));
				test.Getch();
				Maxloops=0;
				}
			else if (token==_L("rwsoak"))
				{
				TPtrC val=lex.NextToken();
				TLex lexv(val);
				TInt v;

				if (val==_L("-"))
					Forever=ETrue;
				else
					if (lexv.Val(v)==KErrNone)
						Maxloops=v;
					else
						test.Printf(_L("Bad value for rwsoak '%S' was ignored.\n"), &val);
				}
			else if (token==_L("defer"))
				{
				TPtrC val=lex.NextToken();
				TLex lexv(val);
				TInt v;

				if (val==_L("-"))
					MaxDeferLoops=KMaxTInt;
				else
					if (lexv.Val(v)==KErrNone)
						MaxDeferLoops=v;
					else
						test.Printf(_L("Bad value for defer '%S' was ignored.\n"), &val);
				}	
			else
				test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
			}
		else
			break;
		
		}
	}

//
// E32Main
//

TInt E32Main()
	{
	TInt r;
	test.Title();

	test.Printf(_L("key\n---\n"));	
	test.Printf(_L("PR: Paging requests\n"));
	test.Printf(_L("NR: Normal requests\n\n"));

	
	test.Start(_L("Check that the rom is paged"));
	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
	if (romHeader->iPageableRomStart==NULL)
		test.Printf(_L("Test ROM is not paged - test skipped!\r\n"));
	else
		{
		ParseCommandLine();	
		test(TheFs.Connect()==KErrNone);

		r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
		if(r<0)
			{
			test.Printf(_L("DemandPagingFlushPages Error = %d\n"),r);
			test(0);
			}
			
		DriveNumber = FindFsMMCDrive();	
		
		if(DriveNumber<0)
			test.Printf(_L("MMC Flash not found - test skipped!\r\n"));
		else
			{
			RFile file;
			TBuf<256> fileName;	
			fileName.Append((TChar)('A'+DriveNumber));
			fileName+=_L(":\\f32-tst\\");
			TInt r=TheFs.MkDirAll(fileName);
			test(r==KErrNone || r== KErrAlreadyExists);
			fileName += _L("redglare.txt");
			r=file.Replace(TheFs,fileName,EFileWrite);
			if (r!=KErrNone)
				test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
			test(r==KErrNone);
			r=file.Write(_L8("The red glare of an ancient sun reflecting on the leaden surface of a primeval soup of decomposing matter"));
			if (r!=KErrNone)
				test.Printf(_L("Error %d: could not write to file\n"),r);
			test(r==KErrNone);

			test(file.Flush() == KErrNone);

			SBlockMapInfo info;
			TInt64 start=0;
			r=file.BlockMap(info,start, -1,ETestDebug);
			if (r!=KErrNone && r!=KErrCompletion)
				test.Printf(_L("Error %d: could not obtain block map\n"),r);
			test(r==KErrNone || r==KErrCompletion);
			locDriveNumber=info.iLocalDriveNumber;
			test.Printf(_L("Found drive: %c (MMC drive %d)\r\n"), DriveNumber+'A',locDriveNumber);
			file.Close();
			
			TDriveInfo driveInfo;
			test(TheFs.Drive(driveInfo, DriveNumber) == KErrNone);

			// Connect to device driver
			TBool changeFlag = EFalse;
			r = Drive.Connect(locDriveNumber,changeFlag);
			TPckg<TLocalDriveCapsV4>	capsPack(DriveCaps);
			Drive.Caps(capsPack);
			test(r == KErrNone);

			SMmcStats stats;
			TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
			r = Drive.ControlIO(KMmcGetStats,statsBuf,0);


			if (r!=KErrNone)
				{
				test.Printf(_L("LocalDrive does not support testing IO Requests\n"));
				CtrlIOSupported=EFalse;
				}
			test.Printf(_L("LocalDrive Connected\n"));
			//
			// Run tests	
			//
			TestMmcAccuratcy();
			// 
			// Free device and end test program
			//
			Drive.Disconnect();
			}	
		}
		
	test.End();
	return 0;
	}