--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/lffs/tf_suspendsoak.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -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 <e32std.h>
+#include <e32std_private.h>
+#include <e32svr.h>
+#include <e32test.h>
+#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<CEraser*>(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<KZeroBufSize> 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();
+ }