--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/demandpaging/t_fragmentdp.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,549 @@
+// Copyright (c) 1996-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:
+// f32test\demandpaging\t_fragment.cpp
+// This test exercises the fragmentation of write requests carried out
+// by the Local Media subsystem, when the request is for a partition
+// driven by a Media driver that supports paging.
+// 002 Check if LFFS drive (Mount LFFS if required)
+// 003 Testing Fragmentation of writes to writable drives in paging media
+// 004 Testing concurrent Fragmentation of writes on the same media
+// 005 Check Disk
+//
+//
+
+//! @SYMTestCaseID KBASE-T_FRAGMENTDP-0333
+//! @SYMTestType UT
+//! @SYMPREQ PREQ1110
+//! @SYMTestCaseDesc Demand Paging Page cache fragmentation tests.
+//! @SYMTestActions 001 Starting tests...
+//! @SYMTestExpectedResults All tests should pass.
+//! @SYMTestPriority High
+//! @SYMTestStatus Implemented
+
+#include <f32file.h>
+#include <e32test.h>
+#include "t_server.h"
+#include <u32hal.h>
+#include <e32rom.h>
+#include <f32dbg.h>
+#include "testdefs.h"
+
+#ifdef __VC32__
+ // Solve compilation problem caused by non-English locale
+ #pragma setlocale("english")
+#endif
+
+const TInt KMuliplySize=10;
+const TInt KFileSizeInBytes=302498;
+
+LOCAL_D TBuf8<KMuliplySize*KFileSizeInBytes> Buffer;
+LOCAL_D RSemaphore WriteSemaphore;
+
+GLDEF_D RTest test(_L("T_FRAGMENTDP"));
+
+void DoTestF(TInt aDrvNum, TBool aNand); // may want to do something weird on NAND later (e.g. trigger Garbage Collection)
+void DoTestC(TInt aDrvNum, TInt aNotherDrvNum, TBool aNand); // may want to do something weird on NAND later (e.g. trigger Garbage Collection)
+TInt CreateEmptyFile(RFs& aFs, const TDesC& aFileName, TUint aFileSize);
+TInt GetLocDrvNumber(TInt aDrvNo);
+
+/*
+ This plain looking test exercises the fragmentation of write requests carried out by the Local
+ Media subsystem, when the request is for a partition driven by a Media driver that supports
+ paging.
+ It indirectly tests that the ELOCD fragmentation and EKERN locking mechanisms work as specified
+ to prevent deadlocks. It also causes an awful lot of paging activity.
+*/
+
+LOCAL_C TBool TestSimpleFragmentation()
+//
+// Find ROM address of file and write from it to another file in writable partition in the same media as the backing store for ROM
+//
+
+ {
+ TDriveInfo driveInfo;
+ TBool tested=EFalse;
+
+ TFileName path;
+ TInt r=TheFs.SessionPath(path);
+ test(r==KErrNone);
+ TInt drv;
+ r=RFs::CharToDrive(path[0],drv);
+ test(r==KErrNone);
+
+ test(TheFs.Drive(driveInfo, drv) == KErrNone);
+
+ //-- select a suitable drive for the testing. It shall be a writable drive on a media that services paging
+ if(driveInfo.iMediaAtt&KMediaAttPageable)
+ {
+ TBool readOnly = driveInfo.iMediaAtt & KMediaAttWriteProtected; // skip ROFS partitions
+ if(!readOnly)
+ {
+ DoTestF(drv, (driveInfo.iType==EMediaNANDFlash)?(TBool)ETrue:(TBool)EFalse);
+ tested=ETrue;
+ }
+ }
+ if(!tested)
+ test.Printf(_L("Skipped T_FRAGMENTDP on drive %c\n"), path[0]);
+ return tested;
+ }
+
+
+void DoTestF(TInt aDrvNum, TBool aNand)
+ {
+ TInt pos=0;
+ TInt size, size1;
+ TInt r;
+ TFileName fileName;
+
+ test.Next(_L("Testing Fragmentation of writes to writable drives in paging media"));
+ if(aNand)
+ test.Printf(_L("Testing on NAND\n"));
+
+ fileName.Format(_L("Testing drive %c:\n"), 'A'+aDrvNum);
+ test.Printf(fileName);
+
+ _LIT(KTPagedCpp, "Z:\\test\\TEST_PAGED.cpp");
+ _LIT(KTUnpagedCpp, "Z:\\test\\TEST_UNPAGED.CPP");
+
+ if(TheFs.IsFileInRom(KTPagedCpp) != NULL && TheFs.IsFileInRom(KTUnpagedCpp) != NULL) // .oby must include these files
+ {
+ RFile f;
+ r=f.Open(TheFs,KTUnpagedCpp,EFileStream);
+ test(r==KErrNone);
+ r=f.Seek(ESeekAddress,pos);
+ test(r==KErrNone);
+ TText8* ptrPos=*(TText8**)&pos; // start address of unpaged file in ROM
+ test.Printf(_L("Start address of section to copy 0x%x\n"), ptrPos);
+
+ r=f.Size(size); // size of unpaged file
+ test(r==KErrNone);
+ size+=((~(size&0xf)&0xf)+1); // adjust for ROM alignement (EABI, 8 bytes)
+ f.Close();
+
+ r=f.Open(TheFs,KTPagedCpp,EFileStream);
+ test(r==KErrNone);
+ r=f.Size(size1); // size of paged file
+ test(r==KErrNone);
+ size1+=((~(size1&0xf)&0xf)+1); // adjust for ROM alignement (EABI, 8 bytes)
+ f.Close();
+
+ size+=size1;
+ test.Printf(_L("Set descriptor with size %d (paged+unpaged sections+ROMFS padding)\n"), size);
+ TPtrC8 ptr(ptrPos,size);
+
+ fileName.Format(_L("%c:\\TestFragFile.bin"), aDrvNum+'A');
+ TheFs.Delete(fileName); //-- just in case
+
+ test.Printf(_L("Create and open destination file\n"));
+ r = CreateEmptyFile(TheFs, fileName, (size)); // create file to hold both sizes
+ test(r == KErrNone);
+ r=f.Open(TheFs,fileName,EFileRead|EFileWrite);
+ test(r == KErrNone);
+
+ test.Printf(_L("Attempt to flush paged section\n"));
+ TInt r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+ if(r==KErrNotSupported)
+ test.Printf(_L("Not Supported\n"));
+
+ test.Printf(_L("Write paged and unpaged sections, synchronoulsy\n"));
+ r=f.Write(ptr);
+ test(r==KErrNone);
+
+ test.Printf(_L("Read back and compare\n"));
+ pos=0;
+ r=f.Seek(ESeekStart,pos);
+ test(r==KErrNone);
+ TUint end=(TUint)ptrPos+(size);
+ TBuf8<1024> readBuf;
+ TPtrC8 memBuf(ptrPos,1024);
+
+ while((TUint)ptrPos+1024<end)
+ {
+ r=f.Read(readBuf);
+ test(r==KErrNone);
+ test(readBuf.Length()==readBuf.MaxLength());
+ if(memBuf!=readBuf)
+ {
+ test.Printf(_L("Failed on descriptor starting at address %x\n"), ptrPos);
+ test(0);
+ }
+ ptrPos+=1024;
+ memBuf.Set(ptrPos,1024);
+ }
+ r=f.Read(readBuf);
+ test(r==KErrNone);
+ test(readBuf.Length()==(TInt)(end-(TUint)ptrPos));
+ memBuf.Set(ptrPos,(end-(TUint)ptrPos));
+ if(memBuf!=readBuf)
+ {
+ test.Printf(_L("Failed on descriptor starting at address %x\n"), ptrPos);
+ test(0);
+ }
+ f.Close();
+ }
+ else
+ {
+ test.Printf(_L("Required test files not present\n"));
+ test(0);
+ }
+ }
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+LOCAL_C TInt ConcurrThread(TAny* aArg);
+/*
+ This equally unimpressive looking test further exercises the fragmentation of write requests
+ carried out by the Local Media subsystem. This time write requests where the request source is
+ in paged out ROM are issued concurrently.
+ By having concurrent writes it indirectly tests both page in and fragment deferral mechaninsms.
+*/
+
+LOCAL_C void TestConcurrentFragmentation()
+ {
+ // concurrently write from paged out ROM addresses to either files in separate writebla partitions or different locations in the same partition
+ TDriveList driveList;
+ TDriveInfo driveInfo;
+ TBool concurr=EFalse;
+
+ TFileName path;
+ TInt r=TheFs.SessionPath(path);
+ test(r==KErrNone);
+ TInt drvNum;
+ r=RFs::CharToDrive(path[0],drvNum);
+ test(r==KErrNone);
+
+ r=TheFs.DriveList(driveList);
+ test(r == KErrNone);
+
+ test(TheFs.Drive(driveInfo, drvNum) == KErrNone);
+
+ //-- select suitable drives for the testing. They shall be writable drives on a media that services paging
+ if((driveInfo.iMediaAtt&KMediaAttPageable) && !(driveInfo.iMediaAtt&KMediaAttWriteProtected))
+ {
+ for (TInt drvNum1=drvNum+1; drvNum1<KMaxDrives; drvNum1++) // if yes search for more drives suitable for concurrent fragmentation
+ {
+ if(!driveList[drvNum1])
+ continue; //-- skip unexisting drive
+
+ TDriveInfo driveInfo2; // for second drive
+ test(TheFs.Drive(driveInfo2, drvNum1) == KErrNone);
+ if ((driveInfo2.iMediaAtt&KMediaAttPageable) &&
+ !(driveInfo2.iMediaAtt&KMediaAttWriteProtected) &&
+ (driveInfo.iType == driveInfo2.iType))
+ {
+ DoTestC(drvNum, drvNum1, (driveInfo.iType==EMediaNANDFlash)?(TBool)ETrue:(TBool)EFalse); // test concurrent
+ concurr=ETrue;
+ }
+ }
+ }
+ if(!concurr)
+ test.Printf(_L("Skipped concurrent test\n"));
+ }
+
+
+void silentFormat(TInt driveNo)
+ {
+ TBuf<4> driveBuf=_L("?:\\");
+ RFormat format;
+ TInt count;
+
+ driveBuf[0] = (TText)(driveNo + 'A');
+
+ TInt r = format.Open(TheFs, driveBuf, EHighDensity, count);
+ test(r == KErrNone);
+
+ while(count)
+ {
+ r=format.Next(count);
+ test(r == KErrNone);
+ }
+
+ format.Close();
+ }
+
+
+void DoTestC(TInt aDrvNum, TInt aNotherDrvNum, TBool aNand)
+ {
+ TInt pos=0;
+ TInt size=0;
+ TInt r;
+ TRequestStatus logonStat;
+ RThread concurrThread;
+ TInt locDriveNumber;
+ TBusLocalDrive drive;
+ TLocalDriveCapsV4 driveCaps;
+ SDeferStats stats;
+
+ test.Next(_L("Testing concurrent Fragmentation of writes on the same media"));
+ if(aNand)
+ test.Printf(_L("Testing on NAND\n"));
+ test.Printf(_L("Testing on writable drives %c and %c\n"), 'A'+aDrvNum,'A'+aNotherDrvNum);
+
+ _LIT(KTPagedCpp, "Z:\\test\\TEST_PAGED.cpp");
+ _LIT(KTPaged1Cpp, "Z:\\test\\TEST_PAGED1.cpp");
+
+ if(TheFs.IsFileInRom(KTPagedCpp) != NULL && TheFs.IsFileInRom(KTPaged1Cpp) != NULL) // .oby must include these files
+ {
+ RFile f;
+ r=f.Open(TheFs,KTPagedCpp,EFileStream); // source 1
+ test(r==KErrNone);
+ r=f.Seek(ESeekAddress,pos);
+ test(r==KErrNone);
+ TText8* ptrPos=*(TText8**)&pos; // start address of paged file 1 in ROM
+ test.Printf(_L("Main thread->Start address of paged out file 1 0x%x\n"), ptrPos);
+ r=f.Size(size); // size of paged file 1
+ test(r==KErrNone);
+ f.Close();
+
+ TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+ TUint fsize=Min(KMuliplySize*size,romHeader->iPageableRomSize);
+
+ test.Printf(_L("Main thread->Set descriptor with size %d to point to paged out file 1 +...\n"), fsize);
+ TPtrC8 ptr(ptrPos,fsize);
+
+ Buffer.SetLength(fsize);
+ TPtr8 readBuf(&Buffer[0],fsize,fsize);
+
+ test.Printf(_L("Create and resume concurrent thread\n"));
+ const TInt KHeapSize=0x2000;
+
+ locDriveNumber = GetLocDrvNumber(aNotherDrvNum);
+ TInt r = concurrThread.Create(_L("ConcurrentWriteThread"),ConcurrThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)locDriveNumber);
+ test(r==KErrNone);
+ concurrThread.Logon(logonStat);
+
+ locDriveNumber = GetLocDrvNumber(aDrvNum);
+ test.Printf(_L("Connect to local drive %d\n"),locDriveNumber);
+ TBool changeFlag = EFalse;
+ r = drive.Connect(locDriveNumber,changeFlag);
+ TPckg<TLocalDriveCapsV4> capsPack(driveCaps);
+ drive.Caps(capsPack);
+ test(r == KErrNone);
+
+ test(WriteSemaphore.CreateLocal(0)==KErrNone);
+
+ // try to ensure there is no other thread activity as this may prevent the
+ // large write from being pre-empted by the ConcurrentWriteThread
+ test.Printf(_L("Waiting 2 secs for file server threads to quieten down...."));
+ User::After(2000000);
+
+ concurrThread.Resume();
+
+ WriteSemaphore.Wait();
+ WriteSemaphore.Signal();
+
+ // long write...
+// test.Printf(_L("Starting file 1 write\n"));
+ r = drive.Write(0,ptr);
+ test(r==KErrNone);
+
+ test.Printf(_L("Main thread->Write 1 completed\n"));
+
+ if(aNand)
+ {
+ test.Printf(_L("Read stats\n"));
+ TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
+ test(drive.ControlIO(KNandGetDeferStats, statsBuf, 0)==KErrNone);
+ test.Printf(_L("Fragmentation clashes %d Fragmentation deferrals %d Page In deferrals %d Other deferrals %d\n"),stats.iClashFragmenting, stats.iNormalFragmenting, stats.iPageOther, stats.iNormalOther);
+ }
+
+ test.Printf(_L("Read back file 1 and compare\n"));
+ r = drive.Read(0,fsize,readBuf);
+ test(r==KErrNone);
+ test(ptr==readBuf);
+ test.Printf(_L("Verify file 1 OK\n"));
+ drive.Disconnect();
+
+ WriteSemaphore.Signal();
+ User::WaitForRequest(logonStat);
+ test(logonStat==KErrNone);
+ concurrThread.Close();
+ WriteSemaphore.Close();
+
+ silentFormat(aDrvNum);
+ silentFormat(aNotherDrvNum);
+ }
+ else
+ {
+ test.Printf(_L("Required test files not present\n"));
+ test(0);
+ }
+ }
+
+
+GLDEF_D RFs TheFsT;
+GLDEF_D RTest testT(_L("T_CONCURRENT_WRITE_THREAD"));
+
+LOCAL_C TInt ConcurrThread(TAny* aArg)
+ {
+ // the whole test is dodgy and hangs if this thread fails an assert,
+ // so at least make sure thread panic takes out whole test process...
+ User::SetCritical(User::EProcessCritical);
+
+ RFile f;
+ TInt pos=0;
+ TInt size=0;
+ TInt locDriveNumber;
+ TBusLocalDrive drive;
+ TLocalDriveCapsV4 driveCaps;
+ SDeferStats stats;
+ RThread thisThread;
+
+ TInt r = TheFsT.Connect();
+ _LIT(KTPaged1Cpp, "Z:\\test\\TEST_PAGED1.cpp");
+ r=f.Open(TheFsT,KTPaged1Cpp,EFileStream); // source 2
+ testT(r==KErrNone);
+ r=f.Seek(ESeekAddress,pos);
+ testT(r==KErrNone);
+ TText8* ptrPos=*(TText8**)&pos; // start address of paged file 2 in ROM
+ testT.Printf(_L("ConcurrThread->Start address of paged out file 2 0x%x\n"), ptrPos);
+ r=f.Size(size); // size of paged file 2
+ testT(r==KErrNone);
+ f.Close();
+
+ testT.Printf(_L("ConcurrThread->Set descriptor with size %d to point to paged out file 2\n"), size);
+ TPtrC8 ptr(ptrPos,size);
+
+ TPtr8 readBuf(&Buffer[0],size,size);
+
+ locDriveNumber = (TInt)aArg;
+ testT.Printf(_L("Connect to local drive %d\n"),locDriveNumber);
+ TBool changeFlag = EFalse;
+ r = drive.Connect(locDriveNumber,changeFlag);
+ TPckg<TLocalDriveCapsV4> capsPack(driveCaps);
+ drive.Caps(capsPack);
+ testT(r == KErrNone);
+
+ if (driveCaps.iType == EMediaNANDFlash)
+ {
+ testT.Printf(_L("Zero stats\n"));
+ TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
+ testT(drive.ControlIO(KNandGetDeferStats, statsBuf, 0)==KErrNone);
+ }
+
+ r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+ if(r==KErrNotSupported)
+ testT.Printf(_L("ConcurrThread->Flushing of paging not Supported\n"));
+
+ // pause one second to make sure main thread has executed WriteSemaphore.Wait();
+ User::After(1000000);
+
+ WriteSemaphore.Signal();
+ WriteSemaphore.Wait();
+ // up our priority
+ thisThread.SetPriority(EPriorityMore);
+
+ // wait a very short time to give the other thread a better chance to initiate the write
+ User::After(1);
+// testT.Printf(_L("Starting file 2 write\n"));
+
+ // write
+ r = drive.Write(0,ptr);
+ testT(r==KErrNone);
+ // read back
+ r = drive.Read(0,size,readBuf);
+ testT(r==KErrNone);
+ // erase
+ r=drive.Format(0,size);
+ testT(r==KErrNone);
+
+ testT.Printf(_L("ConcurrThread->Write of file 2 completed\n"));
+
+ WriteSemaphore.Wait();
+ testT.Printf(_L("Read back file 2 and compare\n"));
+ testT(ptr==readBuf);
+ testT.Printf(_L("Verify file 2 OK\n"));
+
+ drive.Disconnect();
+ TheFsT.Close();
+ return KErrNone;
+ }
+
+#endif // #if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+//--------------------------------------------------------
+
+/**
+ Create an empty file of specified size.
+ @param aFs ref. to the FS
+ @param aFileName name of the file
+ @param aFileSize size of the file to be created
+ @return KErrNone on success, system-wide error code otherwise
+*/
+TInt CreateEmptyFile(RFs& aFs, const TDesC& aFileName, TUint aFileSize)
+ {
+ RFile file;
+ TInt nRes;
+
+ nRes = file.Create(aFs, aFileName, EFileWrite);
+ if(nRes != KErrNone)
+ return nRes;
+
+ nRes = file.SetSize(aFileSize);
+ if(nRes != KErrNone)
+ return nRes;
+
+ file.Close();
+
+ return KErrNone;
+ }
+
+TInt GetLocDrvNumber(TInt aDrvNo)
+ {
+ test.Printf(_L("GetLocDrvNumber\r\n"));
+ TInt locDriveNumber;
+ RFile file;
+ TBuf<256> fileName;
+ fileName.Append((TChar)('A'+aDrvNo));
+ fileName+=_L(":\\f32-tst\\");
+ TInt r=TheFs.MkDirAll(fileName);
+ test(r==KErrNone || r== KErrAlreadyExists);
+ fileName += _L("maggots.txt");
+ r=file.Replace(TheFs,fileName,EFileWrite|EFileWriteDirectIO);
+ if (r!=KErrNone)
+ test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
+ test(r==KErrNone);
+ r=file.Write(_L8("Writhing bundles of maggots, this was truly their finest hour"));
+ if (r!=KErrNone)
+ test.Printf(_L("Error %d: could not write to file\n"),r);
+ test(r==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("From drive: %c to Local drive %d\r\n"), aDrvNo+'A',locDriveNumber);
+ file.Close();
+ return locDriveNumber;
+ }
+
+GLDEF_C void CallTestsL()
+ {
+ TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
+ if(!romHeader->iPageableRomStart)
+ {
+ test.Printf(_L("Test not supported (not a paged ROM)\n"));
+ return; // Not a paged ROM, skip test
+ }
+ test.Title();
+
+ TBool r=TestSimpleFragmentation();
+ if(!r)
+ return;
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ TestConcurrentFragmentation();
+#endif
+ }