diff -r 000000000000 -r bde4ae8d615e os/kernelhwsrv/kerneltest/e32test/lffs/tf_suspendsoak.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/kernelhwsrv/kerneltest/e32test/lffs/tf_suspendsoak.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,582 @@ +// Copyright (c) 2003-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: +// Tests erasing of Flash while forcing suspend-resume cycles +// This is a soak-test version that runs continously +// +// + +#include +#include +#include +#include +#include "randgen.h" +#include "user_config.h" + +RTest test( _L("TF_SUSPENDSOAK") ); + + + + +class CEraser + { + public: + enum TFunction + { + EIdle, + EEraseBlock + }; + + public: + ~CEraser(); + void CreateL(); + void Stop(); + void WaitForReady(); + inline TBool CheckDone() const + { + return (EIdle == iRequestedFunction); + } + + inline void WaitForDone() + { + WaitForReady(); + iWaitingSignal.Signal(); // resignal, ready for next Start() + }; + + void EraseBlock( TUint32 aOffset, TUint aLength ); + + private: + void Panic( TInt aPanicNum ); + void Start( TFunction aFunction ); + + static TInt EraserThread( TAny* aParam ); + + void DoEraseBlock(); + + private: + RThread iThread; + + // + // Shared between main & eraser thread + // + TFunction iRequestedFunction; + RSemaphore iGoSignal; + RSemaphore iWaitingSignal; + TBool iStop; + + // + // These are local to the eraser thread + // + TUint iOffset; + TUint iLength; + TBusLocalDrive iDrive; + }; + + + +CEraser::~CEraser() + { + iThread.Terminate( KErrNone ); + iThread.Close(); + iGoSignal.Close(); + iWaitingSignal.Close(); + } + +void CEraser::Panic( TInt aPanicNum ) + { + _LIT( KPanicCat, "ERASE-T" ); + User::Panic( KPanicCat, aPanicNum ); + RProcess().Panic( KPanicCat, aPanicNum ); + } + + +void CEraser::CreateL() + // + // Create new thread and wait for it to become ready + // + { + iGoSignal.CreateLocal( 0 ); // initially blocked + iWaitingSignal.CreateLocal( 0 ); // initially blocked + iStop = EFalse; + User::LeaveIfError( iThread.Create( _L("ERASER"), EraserThread, 2048, 2048, 65536, this ) ); + test.Printf( _L("Eraser thread created\n") ); + + iThread.Resume(); + + test.Printf( _L("Waiting for thread to become ready\n") ); + WaitForReady(); + iWaitingSignal.Signal(); + } + +void CEraser::Start( TFunction aFunction ) + // + // Start the suspender thread executing function aFunction + // + { + iStop = EFalse; + WaitForReady(); + iRequestedFunction = aFunction; + iGoSignal.Signal(); + } + +void CEraser::Stop() + // + // Stop the thread + // + { + iStop = ETrue; + } + +void CEraser::WaitForReady() + { + iWaitingSignal.Wait(); + } + +void CEraser::EraseBlock( TUint32 aOffset, TUint aLength ) + // + // Execute a single read immediately to cause a suspend + // + { + iOffset = aOffset; + iLength = aLength; + Start( EEraseBlock ); + } + + +TInt CEraser::EraserThread( TAny* aParam ) + // + // The thread which executes suspend functions + // + { + RDebug::Print( _L("Eraser thread starts") ); + + CEraser& self = *reinterpret_cast(aParam); + + // + // Open our own TBusLogicalDevice channel + // + TBool changedFlag; + if( KErrNone != self.iDrive.Connect( KDriveNumber, changedFlag ) ) + { + self.Panic( 1 ); + } + + RDebug::Print( _L("Eraser thread connected to drive") ); + + while( !self.iStop ) + { + // + // Signal that we are ready for a request + // + _LIT( KWaitMsg, "Eraser thread waiting..." ); + RDebug::Print( KWaitMsg ); + self.iWaitingSignal.Signal(); + + // + // Wait for a request + // + self.iGoSignal.Wait(); + _LIT( KGoMsg, "Eraser thread go (%d)" ); + RDebug::Print( KGoMsg, self.iRequestedFunction ); + + switch( self.iRequestedFunction ) + { + case EEraseBlock: + self.DoEraseBlock(); + break; + + case EIdle: + default: + self.Panic( 0 ); + } + + self.iRequestedFunction = EIdle; + } + + self.iDrive.Disconnect(); + return KErrNone; + } + +void CEraser::DoEraseBlock() + // + // Issue an erase + // + { + _LIT( KEraseStartMsg, "Eraser starting erase..." ); + RDebug::Print( KEraseStartMsg ); + + TInt r = iDrive.Format( TInt64(iOffset), iLength ); + + if( KErrNone != r ) + { + RDebug::Print( _L("Eraser: FAIL: erase request returns %d"), r ); + Panic( 2 ); + } + } + + + +class CSuspendTest : public CBase + { + public: + ~CSuspendTest(); + + void CreateL(); + + void DoTest(); + + private: + + TInt EraseOneBlock( TInt aBlockNumber ); + TInt ZeroFillBlock( TInt aBlockNumber ); + TBool ValidateBlock( TInt aBlockNumber, TUint32 aFillWord ); + TInt ZeroAllBlocks(); + TBool ValidateAllBlocks( TUint32 aFillWord ); + void TimeSinceStart() const; + + void DoRandomReadSoak(); + + private: + TBusLocalDrive iDrive; + TBool iDriveOpened; + + CEraser* iEraser; + + TInt iFlashSize; + TInt iBlockSize; + TInt iBlockCount; + + TTime iStartTime; + + TBuf8<512> iReadBuffer; + + }; + + +CSuspendTest::~CSuspendTest() + { + if( iDriveOpened ) + { + iDrive.Disconnect(); + } + + delete iEraser; + } + + + +void CSuspendTest::CreateL() + { + // + // Create the eraser thread + // + iEraser = new(ELeave) CEraser; + iEraser->CreateL(); + + // + // Load the device drivers + // + TInt r; +#ifndef SKIP_PDD_LOAD + test.Printf( _L("Loading %S\n"), &KLfsDriverName ); + r = User::LoadPhysicalDevice( KLfsDriverName ); + test( KErrNone == r || KErrAlreadyExists == r ); +#endif + +#ifdef UNMOUNT_DRIVE + RFs fs; + test( KErrNone == fs.Connect() ); + test( KErrNone == fs.SetSessionPath( _L("Z:\\") ) ); + TFullName name; + fs.FileSystemName( name, KLffsLogicalDriveNumber ); + if( name.Length() > 0 ) + { + test.Printf( _L("Unmounting drive") ); + test( KErrNone == fs.DismountFileSystem( _L("Lffs"), KLffsLogicalDriveNumber) ); + User::After( 2000000 ); + test.Printf( _L("Drive unmounted") ); + } + fs.Close(); +#endif + + // + // Open a TBusLogicalDevice to it + // + test.Printf( _L("Opening media channel\n") ); + TBool changedFlag = EFalse; + r = iDrive.Connect( KDriveNumber, changedFlag ); + User::LeaveIfError( r ); + iDriveOpened = ETrue; + + // + // Get size of Flash drive, block size, block count + // + TLocalDriveCapsV2Buf info; + iDrive.Caps(info); + iFlashSize = I64LOW(info().iSize); + iBlockSize = info().iEraseBlockSize; + iBlockCount = iFlashSize / iBlockSize; + + test.Printf( _L("Flash size is 0x%x bytes\n"), iFlashSize ); + test.Printf( _L("Block size is 0x%x bytes\n"), iBlockSize ); + test.Printf( _L("Block count is %d\n"), iBlockCount ); + + test.Printf( _L("CreateL complete\n") ); + } + + +void CSuspendTest::DoTest() + // + // Main test dispatcher + // + { + DoRandomReadSoak(); + } + + +TInt CSuspendTest::EraseOneBlock( TInt aBlockNumber ) + // + // Erases block aBlockNumber on Flash + // + { + TInt blockBaseOffset = aBlockNumber * iBlockSize; + + test.Printf( _L("Erasing block %d (offs=0x%x)\n"), aBlockNumber, blockBaseOffset ); + + TInt r = iDrive.Format( blockBaseOffset, iBlockSize ); + + test.Printf( _L("... block erased, rv=%d\n"), r ); + return r; + } + + +TBool CSuspendTest::ValidateBlock( TInt aBlockNumber, TUint32 aFillWord ) + // + // Checks that every word in block aBlockNumber has the value aFillWord + // + { + TUint offset = aBlockNumber * iBlockSize; + test.Printf( _L("Validating block %d (offs=0x%x)\n"), aBlockNumber, offset ); + + TBool failed = EFalse; + const TInt readBufLen = iReadBuffer.MaxLength(); + + for( TInt len = iBlockSize; len > 0 && !failed ;) + { + TInt r = iDrive.Read( offset, readBufLen, iReadBuffer ); + if( r != KErrNone ) + { + test.Printf( _L("... FAIL: read failed (%d) at offset 0x%x\n"), r, offset ); + test( KErrNone == r ); + } + test( iReadBuffer.Length() == readBufLen ); + + TUint32* p = (TUint32*)iReadBuffer.Ptr(); + for( TInt i = 0; i < readBufLen; i += 4 ) + { + if( aFillWord != *p ) + { + failed = ETrue; + test.Printf( _L("... FAILED: word @ offs=0x%x, read=0x%x, expected=0x%x\n"), + offset+i, p[0], aFillWord ); + break; + } + ++p; + } + offset += readBufLen; + len -= readBufLen; + } + + return !failed; + } + + +TInt CSuspendTest::ZeroFillBlock( TInt aBlockNumber ) + // + // Zero-fills and entire block + // The requires that writing works + // + { + test.Printf( _L("Zero-filling block %d\n"), aBlockNumber ); + + // + // Create a buffer full of zeros + // + const TInt KZeroBufSize = 512; + + TBuf8 buf; + buf.FillZ( buf.MaxLength() ); + + // + // Write the data out to the Flash + // + TInt writeCount = iBlockSize / KZeroBufSize; + TInt r = KErrNone; + TUint blockBaseOffset = aBlockNumber * iBlockSize; + TInt pos = blockBaseOffset; + for( ; (writeCount > 0) && (KErrNone == r); writeCount-- ) + { + r = iDrive.Write( pos, buf ); + if( r != KErrNone ) + { + test.Printf( _L("... FAIL: write failed (%d) at offset 0x%x\n"), pos ); + } + pos += KZeroBufSize; + } + + return r; + } + + +TInt CSuspendTest::ZeroAllBlocks() + // + // Writes zeros to all blocks + // + { + test.Printf( _L("Zeroing all blocks\n") ); + + TInt r = KErrNone; + for( TInt i = 0; (i < iBlockCount) && (KErrNone == r); i++ ) + { + r = ZeroFillBlock( i ); + } + + return r; + } + +TBool CSuspendTest::ValidateAllBlocks( TUint32 aFillWord ) + // + // Checks that all blocks contain aFillWord + // + { + test.Printf( _L("Validating all blocks\n") ); + + TBool failed = EFalse; + for( TInt i = 0; (i < iBlockCount) && (!failed); i++ ) + { + failed = !ValidateBlock( i, aFillWord ); + } + + return !failed; + } + + +void CSuspendTest::TimeSinceStart() const + { + TTimeIntervalSeconds timeTaken; + TTime time; + time.HomeTime(); + TInt r = time.SecondsFrom(iStartTime, timeTaken); + test(r == KErrNone); + TInt totalTime = timeTaken.Int(); + + TInt seconds = totalTime % 60; + TInt minutes = (totalTime / 60) % 60; + TInt hours = totalTime / 3600; + + test.Printf( _L("Time since test started = %d:%d:%d\n"), hours, minutes, seconds ); + } + + + +void CSuspendTest::DoRandomReadSoak() + // + // For each block issues an erase and then + // starts issuing read requests. The intervals + // between read requests are derived from the + // pseudo-random number generator. + // Each block is checked after is has been erased + // + { + + TRandomGenerator random; + random.SetSeed( MAKE_TINT64(0xA05BE111,0x00101111) ); + + test.Next( _L("Random read soak test") ); + + iStartTime.HomeTime(); + + // + // We repeat the test for each block, erasing block n and reading from + // block (n+1) modulo iBlockCount + // + for(;;) + { + TimeSinceStart(); + + for( TInt eraseBlock = 0; eraseBlock < iBlockCount; eraseBlock++ ) + { + TUint32 readBlock = (eraseBlock + 1) % iBlockCount; + TUint32 erasePos = eraseBlock * iBlockSize; + TInt readPos = readBlock * iBlockSize; + + // + // Zero the block we are about to erase + // + test( KErrNone == ZeroFillBlock( eraseBlock ) ); + test( ValidateBlock( eraseBlock, 0 ) ); + + TBuf8<32> buf; + + // + // Start the erase + // + _LIT( KEraseNotify, "Main thread starting erase\n" ); + test.Printf( KEraseNotify ); + iEraser->EraseBlock( erasePos, iBlockSize ); + + // + // Now we loop, waiting for random intervals, issuing + // reads, until the erase completes + // + + while( !iEraser->CheckDone() ) + { + // + // Get a pseudo-random interval between 0 and 524.288 milliseconds + // + TInt delayInMicroseconds = random.Next() % 0x80000; + User::After( delayInMicroseconds ); + + test( KErrNone == iDrive.Read( readPos, buf.MaxLength(), buf ) ); + _LIT( KReadNotify, "Done Read" ); + test.Printf( KReadNotify ); + } + + // + // Now check that the block was erased + // + test( ValidateBlock( eraseBlock, 0xFFFFFFFF ) ); + + } + } + + } + + + + +void E32Main() + { + test.Title(); + test.Start(_L("Testing media erase+suspend operations")); + + CSuspendTest suspendTest; + TRAPD( ret, suspendTest.CreateL() ); + if( KErrNone == ret ) + { + suspendTest.DoTest(); + } + + test.End(); + }