os/kernelhwsrv/kerneltest/e32test/defrag/d_pagemove.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\defrag\d_pagemove.cpp
    15 // LDD for testing defrag page moving
    16 // 
    17 //
    18 
    19 #include <kernel/kern_priv.h>
    20 #include "platform.h"
    21 #include "d_pagemove.h"
    22 #include "nk_priv.h"
    23 
    24 // debug tracing for this test driver is very noisy - off by default
    25 #undef DEBUG_PAGEMOVE
    26 #ifdef DEBUG_PAGEMOVE
    27 #define DBG(a) a
    28 #else
    29 #define DBG(a)
    30 #endif
    31 
    32 const TInt KArbitraryNumber = 4;
    33 
    34 // This driver is ram loaded (see mmp file) so this function will do fine
    35 // as a test of RAM-loaded code.
    36 TInt RamLoadedFunction()
    37 	{
    38 	return KArbitraryNumber;
    39 	}
    40 
    41 const TInt KMajorVersionNumber=0;
    42 const TInt KMinorVersionNumber=1;
    43 const TInt KBuildVersionNumber=1;
    44 
    45 class DPageMove;
    46 
    47 class DPageMoveFactory : public DLogicalDevice
    48 //
    49 // Page move LDD factory
    50 //
    51 	{
    52 public:
    53 	DPageMoveFactory();
    54 	virtual TInt Install();						//overriding pure virtual
    55 	virtual void GetCaps(TDes8& aDes) const;	//overriding pure virtual
    56 	virtual TInt Create(DLogicalChannelBase*& aChannel);	//overriding pure virtual
    57 	};
    58 
    59 class DPageMove : public DLogicalChannelBase
    60 //
    61 // Page move logical channel
    62 //
    63 	{
    64 public:
    65 	DPageMove();
    66 	~DPageMove();
    67 protected:
    68 	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
    69 	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
    70 	TInt DoPageMove(TLinAddr aAddr, TBool aEchoOff=EFalse);
    71 	TInt KernelDataMovePerformance(void);
    72 	TInt iPageSize;
    73 	};
    74 
    75 DECLARE_STANDARD_LDD()
    76 	{
    77     return new DPageMoveFactory;
    78     }
    79 
    80 DPageMoveFactory::DPageMoveFactory()
    81 //
    82 // Constructor
    83 //
    84     {
    85     iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
    86     //iParseMask=0;//No units, no info, no PDD
    87     //iUnitsMask=0;//Only one thing
    88     }
    89 
    90 TInt DPageMoveFactory::Create(DLogicalChannelBase*& aChannel)
    91 //
    92 // Create a new DPageMove on this logical device
    93 //
    94     {
    95 	aChannel=new DPageMove;
    96 	return aChannel?KErrNone:KErrNoMemory;
    97     }
    98 
    99 TInt DPageMoveFactory::Install()
   100 //
   101 // Install the LDD - overriding pure virtual
   102 //
   103     {
   104     return SetName(&KPageMoveLddName);
   105     }
   106 
   107 void DPageMoveFactory::GetCaps(TDes8& aDes) const
   108 //
   109 // Get capabilities - overriding pure virtual
   110 //
   111     {
   112     TCapsPageMoveV01 b;
   113     b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
   114     Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
   115     }
   116 
   117 DPageMove::DPageMove()
   118 //
   119 // Constructor
   120 //
   121     {
   122     }
   123 
   124 TInt DPageMove::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
   125 //
   126 // Create channel
   127 //
   128     {
   129     if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
   130     	return KErrNotSupported;
   131 	iPageSize=Kern::RoundToPageSize(1);
   132 	return KErrNone;
   133 	}
   134 
   135 DPageMove::~DPageMove()
   136 //
   137 // Destructor
   138 //
   139     {
   140     }
   141 
   142 TInt DPageMove::Request(TInt aFunction, TAny* a1, TAny* a2)
   143 	{
   144 	TInt r=KErrNone;
   145 	DBG(Kern::Printf("DPageMove::Request func=%d a1=%08x a2=%08x", aFunction, a1, a2));
   146 	NKern::ThreadEnterCS();
   147 	switch (aFunction)
   148 		{
   149 		case RPageMove::EControlTryMovingKHeap:
   150 			// Allocate a large array on the kernel heap and try moving it around.
   151 			{
   152 			const TInt size=16384;
   153 			TUint8* array = new TUint8[size];
   154 			if (array == NULL)
   155 				r=KErrNoMemory;
   156 			else
   157 				{
   158 				for (TInt i=0; i<size; i++) array[i] = i*i;
   159 
   160 				TUint8* firstpage=(TUint8*)_ALIGN_DOWN((TLinAddr)array, iPageSize);
   161 				for (TUint8* page=firstpage; page<array+size; page+=iPageSize)
   162 					{
   163 					r=DoPageMove((TLinAddr)page);
   164 					if (r!=KErrNone)
   165 						{
   166 						Kern::Printf("Move returned %d", r);
   167 						break;
   168 						}
   169 					}
   170 
   171 				if (r==KErrNone)
   172 					{
   173 					for (TInt i=0; i<size; i++)
   174 						{
   175 						if (array[i] != (TUint8)(i*i))
   176 							{
   177 							r=KErrGeneral;
   178 							Kern::Printf("Data differs at index %d address %08x, expected %02x got %02x", i, &array[i], (TUint8)(i*i), array[i]);
   179 							}
   180 						}
   181 					}
   182 
   183 				Kern::ValidateHeap();
   184 
   185 				delete [] array;
   186 				}
   187 			}
   188 			break;
   189 
   190 		case RPageMove::EControlTryMovingKStack:
   191 			// Stick a not-too-large array on the current thread's kernel stack and try moving it around.
   192 			{
   193 			const TInt size=1024;
   194 			TUint8 array[size];
   195 			for (TInt i=0; i<size; i++) array[i] = i*i;
   196 
   197 			TUint8* firstpage=(TUint8*)_ALIGN_DOWN((TLinAddr)array, iPageSize);
   198 			for (TUint8* page=firstpage; page<array+size; page+=iPageSize)
   199 				{
   200 				r=DoPageMove((TLinAddr)page);
   201 				if (r!=KErrNone)
   202 					{
   203 					Kern::Printf("Move returned %d", r);
   204 					break;
   205 					}
   206 				}
   207 
   208 			if (r==KErrNone)
   209 				{
   210 				for (TInt i=0; i<size; i++)
   211 					{
   212 					if (array[i] != (TUint8)(i*i))
   213 						{
   214 						r=KErrGeneral;
   215 						Kern::Printf("Data differs at index %d address %08x, expected %02x got %02x", i, &array[i], (TUint8)(i*i), array[i]);
   216 						}
   217 					}
   218 				}
   219 			}
   220 			break;
   221 
   222 		case RPageMove::EControlTryMovingUserPage:
   223 		case RPageMove::EControlTryMovingLocale:
   224 			// Try moving the page that the user part of the test told us to.
   225 			r=DoPageMove((TLinAddr)a1, (TBool)a2);
   226 			if (r!=KErrNone && !a2)
   227 				Kern::Printf("Move returned %d", r);
   228 			break;
   229 
   230 		case RPageMove::EControlTryMovingKCode:
   231 			{
   232 			r=DoPageMove((TLinAddr)&RamLoadedFunction);
   233 			if (r==KErrNone)
   234 				{
   235 				if (RamLoadedFunction()!=KArbitraryNumber)
   236 					r=KErrGeneral;
   237 				}	
   238 			else
   239 				Kern::Printf("Move returned %d", r);
   240 			}
   241 			break;
   242 
   243 		case RPageMove::EControlPerfMovingKData:
   244 			r = KernelDataMovePerformance();
   245 			break;
   246 
   247 		case RPageMove::EControlGetPhysAddr:
   248 			TPhysAddr addr;
   249 			addr = (TUint)Epoc::LinearToPhysical((TLinAddr)a1);
   250 			Kern::ThreadRawWrite(&Kern::CurrentThread(),a2, &addr, sizeof(TPhysAddr));
   251 			break;
   252 
   253 		case RPageMove::EControlTryMovingPhysAddr:
   254 			{
   255 			TPhysAddr newAddr;
   256 			r = Epoc::MovePhysicalPage((TPhysAddr)a1, newAddr);
   257 			Kern::ThreadRawWrite(&Kern::CurrentThread(),a2, &newAddr, sizeof(TPhysAddr));
   258 			break;
   259 			}
   260 
   261 		case RPageMove::EControlTryMovingPageTable:
   262 			{
   263 			TPhysAddr newAddr;
   264 			r = Epoc::MovePhysicalPage((TPhysAddr) a1, newAddr, Epoc::ERamDefragPage_PageTable);
   265 			if (newAddr != KPhysAddrInvalid)
   266 				r = KErrGeneral;
   267 			break;
   268 			}
   269 
   270 		case RPageMove::EControlTryMovingPageTableInfo:
   271 			{
   272 			TPhysAddr newAddr;
   273 			r = Epoc::MovePhysicalPage((TPhysAddr) a1, newAddr, Epoc::ERamDefragPage_PageTableInfo);
   274 			if (newAddr != KPhysAddrInvalid)
   275 				r = KErrGeneral;
   276 			break;
   277 			}
   278 
   279 		case RPageMove::EControlNumberOfCpus:
   280 			r = NKern::NumberOfCpus();
   281 			break;
   282 		default:
   283 			r=KErrNotSupported;
   284 			break;
   285 		}
   286 	NKern::ThreadLeaveCS();
   287 	if (r!=KErrNone)
   288 		DBG(Kern::Printf("DPageMove::Request returns %d", r));
   289 	return r;
   290 	}
   291 
   292 TInt DPageMove::DoPageMove(TLinAddr aAddr, TBool aEchoOff)
   293 	{
   294 	DBG(Kern::Printf("DPageMove::DoPageMove() addr=%08x",aAddr));
   295 	aAddr = _ALIGN_DOWN(aAddr, iPageSize);
   296 
   297 	TPhysAddr aOld = Epoc::LinearToPhysical(aAddr);
   298 	TInt r;
   299 	if (aOld == KPhysAddrInvalid)
   300 		r=KErrArgument;
   301 	else
   302 		{
   303 		TPhysAddr aNew;
   304 		r=Epoc::MovePhysicalPage(aOld, aNew);
   305 		if (r==KErrNone)
   306 			{
   307 			TPhysAddr aNewCheck = Epoc::LinearToPhysical(aAddr);
   308 			if (aNewCheck != aNew)
   309 				{
   310 				if (!aEchoOff)
   311 					Kern::Printf("Address mismatch: expected %08x actual %08x\n",aNew,aNewCheck);
   312 				if (aNew != KPhysAddrInvalid && aNewCheck != KPhysAddrInvalid)
   313 					{// The page was not paged out by the moving so don't allow 
   314 					// addresses to differ.  If is was paged out then it may have 
   315 					// been paged back in again but to any free page so the addresses will differ.
   316 					r=KErrGeneral;
   317 					}
   318 				}
   319 			}
   320 		}
   321 	if (r!=KErrNone)
   322 		DBG(Kern::Printf("DPageMove::DoPageMove() returns %d", r));
   323 	return r;
   324 	}
   325 
   326 
   327 #ifndef __MSVC6__ 	// VC6 can't cope with variable arguments in macros.
   328 #define KERN_PRINTF(x...) Kern::Printf(x)
   329 #endif
   330 
   331 //#define EXTRA_TRACE
   332 #ifdef EXTRA_TRACE
   333 #define PRINTF(x)	x
   334 #else
   335 #define PRINTF(x)
   336 #endif
   337 
   338 
   339 TInt DPageMove::KernelDataMovePerformance(void)
   340 {
   341 	const TInt KHeapPagesToMove = 2000;
   342 	const TInt KMoveAttempts = 50;
   343 	const TInt KStackSize=1024;
   344 	enum TKMoveMode
   345 		{
   346 		EKMoveHeap,
   347 		EKMoveStack,
   348 		EKMoveModes,
   349 		};
   350 
   351 	TInt r = KErrNone;
   352 
   353 	// Create some kernel stack pages
   354 	TUint8 stackArray[KStackSize];
   355 
   356 	/// Create some kernel heap pages
   357 	TUint actualHeapPages = KHeapPagesToMove;
   358 	TInt heapArraySize = iPageSize * KHeapPagesToMove;
   359 	TUint8* heapArray = new TUint8[heapArraySize];
   360 	if (heapArray == NULL)
   361 		return KErrNoMemory;
   362 	
   363 	TInt i = 0;
   364 	for (; i < heapArraySize; i++)
   365 		{
   366 		heapArray[i] = i;
   367 		}
   368 
   369 	PRINTF(KERN_PRINTF("Testing Performance of Moving Kernel Data Pages"));
   370 
   371 	TInt moveMode = EKMoveStack;
   372 	for (; moveMode < EKMoveModes; moveMode++)
   373 		{
   374 		TLinAddr pageAddr = NULL;
   375 		TLinAddr endAddr = NULL;
   376 		TLinAddr baseAddr = NULL;
   377 		switch (moveMode)
   378 			{
   379 			case EKMoveHeap:
   380 				pageAddr = _ALIGN_DOWN((TLinAddr)heapArray, iPageSize);
   381 				baseAddr = pageAddr;
   382 				endAddr = _ALIGN_UP((TLinAddr)heapArray + heapArraySize, iPageSize);
   383 				actualHeapPages = (endAddr - baseAddr) / iPageSize;
   384 				PRINTF(KERN_PRINTF("heap baseAddr %x endAddr %x", baseAddr, endAddr));
   385 				break;
   386 
   387 			case EKMoveStack:
   388 				pageAddr = _ALIGN_DOWN((TLinAddr)stackArray, iPageSize);
   389 				baseAddr = pageAddr;
   390 				endAddr = _ALIGN_UP((TLinAddr)stackArray + KStackSize, iPageSize);
   391 				PRINTF(KERN_PRINTF("stack baseAddr %x endAddr %x", baseAddr, endAddr));
   392 				break;
   393 			}
   394 
   395 		TUint32 minTime = KMaxTUint32;
   396 		TUint32 maxTime = 0; 
   397 		TUint32 cummulative = 0;
   398 		TInt iterations = KMoveAttempts;
   399 		TInt tot = iterations;
   400 		TUint pagesMoved = (endAddr - baseAddr) / iPageSize;
   401 		while (iterations--)
   402 			{
   403 			TUint32 diff;
   404 			TUint32 startTime=0;
   405 			TUint32 endTime=0;
   406 			switch (moveMode)
   407 				{
   408 				case EKMoveHeap:
   409 					startTime = NKern::FastCounter(); 
   410 				
   411 					while (pageAddr < endAddr) 
   412 						{
   413 						r = DoPageMove(pageAddr);
   414 
   415 						if (r != KErrNone)
   416 							{
   417 							goto exit;
   418 							}
   419 						pageAddr += iPageSize;
   420 						}
   421 					endTime = NKern::FastCounter();
   422 					break;
   423 			
   424 				case EKMoveStack:
   425 					// Normally will move same number of pages as heap to make comparison easier
   426 					TUint i = actualHeapPages;
   427 					startTime = NKern::FastCounter();
   428 					for (; i > 0; i--)
   429 						{
   430 						while (pageAddr < endAddr) 
   431 							{
   432 							r = DoPageMove(pageAddr);
   433 
   434 							if (r != KErrNone)
   435 								{
   436 								goto exit;
   437 								}
   438 							pageAddr += iPageSize;
   439 							}
   440 						pageAddr = baseAddr;
   441 						}
   442 					endTime = NKern::FastCounter();
   443 					break;
   444 				}
   445 			diff = endTime - startTime;
   446 			if (endTime < startTime) 
   447 				{
   448 				Kern::Printf("WARNING - fast counter overflowed. Assuming only once and continuing");
   449 				diff = (KMaxTUint32 - startTime) + endTime;
   450 				}
   451 
   452 			if (diff == 0)
   453 				{
   454 				Kern::Printf("difference 0!");
   455 				tot--;
   456 				}
   457 
   458 			if (diff > maxTime)
   459 				maxTime = diff;
   460 			if (diff < minTime)
   461 				minTime = diff;
   462 
   463 			if (cummulative + diff < cummulative)
   464 				{
   465 				Kern::Printf("OverFlow!!!");
   466 				r = KErrOverflow;
   467 				goto exit;
   468 				}
   469 			pageAddr = baseAddr;
   470 			cummulative += diff;
   471 			}
   472 		switch (moveMode)
   473 			{
   474 			case EKMoveHeap:				
   475 				if (tot != 0)
   476 					{
   477 					TUint average = (cummulative / tot);
   478 					Kern::Printf("Fast counter ticks to move %d kernel heap pages: Av %d Min %d Max %d (non zero iterations = %d out of %d)", pagesMoved, average, minTime, maxTime, tot, KMoveAttempts);
   479 					Kern::Printf("Average of %d ticks to move one page\n", average / pagesMoved);
   480 					}
   481 				else
   482 					Kern::Printf("WARNING - all kernel heap page moves took 0 fast counter ticks");
   483 				break;
   484 			
   485 			case EKMoveStack:
   486 				if (tot != 0)
   487 					{
   488 					TUint average = (cummulative / tot);
   489 					Kern::Printf("Fast counter ticks to move %d kernel stack pages %d times: Av %d Min %d Max %d (non zero iterations = %d out of %d)", pagesMoved, actualHeapPages, average, minTime, maxTime, tot, KMoveAttempts);
   490 					Kern::Printf("Average of %d ticks to move one page\n", average / (pagesMoved * actualHeapPages));
   491 					}
   492 				else
   493 					Kern::Printf("WARNING - all kernel stack page moves took 0 fast counter ticks");
   494 				break;
   495 			}
   496 		}
   497 
   498 	r = KErrNone;
   499 exit:
   500 	delete [] heapArray;
   501 	return r;
   502 	}
   503 	
   504