Update contrib.
1 // Copyright (c) 1998-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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32\include\memmodel\epoc\mmubase\demand_paging.h
22 #define __CONCURRENT_PAGING_INSTRUMENTATION__
25 class DDemandPagingLock;
28 Maximum number of paging devices supported.
31 const TInt KMaxPagingDevices = 1 + KMaxLocalDrives;
34 Multiplier for number of request objects in pool per drive that supports code paging.
37 const TInt KPagingRequestsPerDevice = 2;
40 Maximum number of paging request objects supported.
43 const TInt KMaxPagingRequests = KMaxPagingDevices * KPagingRequestsPerDevice;
46 Base class for the demand paging object.
48 The main functionality provided by this is:
49 - Management of the live page list.
50 - Interface to paging devices (media drivers).
52 The 'live page list' contains those pages which are currently present or 'paged in'.
53 This is a doubly linked list of SPageInfo objects which each represent a page of physical RAM.
55 The list is split into two parts; 'young' pages (iYoungList) which are marked as accessible
56 by the MMU and 'old' pages (iOldList) which are which are not marked as accessible.
57 The page at the head of iYoungList is the 'youngest' page, and that at the tail
58 of iOldList is the 'oldest' page.
60 This arrangement enables a pseudo Most Recently Used (MRU) algorithm to be implemented
61 where access to an old page will cause a data abort and the fault handler will then
62 move this page to the head of the 'young' list; the last young page will then be made
65 When a data abort occurs because of an access to a page which is not live, then
66 a new RAM page is obtained from the systems free pool, this is then filled with the
67 correct data and added to the start of the live page list. (It is made the 'youngest'
68 page.) If there are no RAM pages left in the systems free pool, or the live page list is
69 at its maximum size, (iMinimumPageCount), then the oldest page from the live page list
70 is recycled and used for the new page.
72 If the OS requests RAM pages from the systems free pool and there are not enough then
73 pages are removed from the live page list to satisfy this request - as long as this does
74 not make the live page list smaller than the limit specified by iMinimumPageCount.
78 class DemandPaging : public RamCacheBase
86 EInitialiseFailed = 0, /**< Error occured during initialisation */
87 EInitialiseBadArgs = 1, /**< Arguments used during initialisation were bad */
88 ERamPageLocked = 2, /**< A page in the live page list was found to be locked */
89 EUnexpectedPageType = 3, /**< A page in the live page list had an unexpected type (SPageInfo::Attribs) */
90 EPageInFailed = 4, /**< An error occured whilst reading data for a 'page in' operation */
91 ELockTwice = 5, /**< DDemandPagingLock::Lock was used twice without an intervening DDemandPagingLock::Unlock. */
92 ELockTooBig = 6, /**< DDemandPagingLock::Lock was used with a size greater than that reserved with DDemandPagingLock::Alloc. */
93 EInvalidPagingDevice = 7, /**< Raised by InstallPagingDevice when the diven device is found to have invalid parameters */
94 EDeviceAlreadyExists = 8, /**< Atempt to install a paging device when one already exists for the ROM or the specified drive. */
95 EDeviceMissing = 9, /**< A Paging Fault occured and the device required to service it was found to be missing. */
96 EPageFaultWhilstFMHeld = 10,/**< A Paging Fault occured whilst the current thread held a fast mutex. */
97 EPageFreeContiguousPages = 11,/**< An error occured when finding pages to free to make contiguous memory blocks.*/
100 class DPagingRequest;
103 // Configuration and initialisation...
107 Tests whether rom paging has been requested.
109 static TBool RomPagingRequested();
112 Tests whether code paging has been requested.
114 static TBool CodePagingRequested();
116 static DemandPaging* New();
120 virtual ~DemandPaging();
123 Intialisation called during MmuBase:Init2.
125 virtual void Init2();
128 Intialisation called from M::DemandPagingInit.
130 virtual TInt Init3();
133 // Live list management...
137 Called by a thread whenever it takes an exception.
139 If this function returns KErrNone then the thread will continue execution at the
140 instruction which caused the exception. Any other return value will cause the normal
141 thread exception handling to continue.
143 The implementation of this function should determine if the exception was casued
144 by access to pagable memory, if it wasn't then it should return KErrUnknown.
146 Otherwise it should perform the actions necessary to make the memory accessible
147 and return KErrNone. The implementation should also call the threads
148 iPagingExcTrap->PagingException
150 virtual TInt Fault(TAny* aExceptionInfo)=0;
153 Make a page in the live page list not accessible. (By changing it's page table entries.)
155 @pre System Lock held
156 @post System Lock held (but may have been released by this function)
158 virtual void SetOld(SPageInfo* aPageInfo)=0;
161 Make a page in the live page list free to use for other purposes.
162 I.e. Unmap it from all of the page tables which map it flush the cache.
164 @pre RamAlloc mutex held
165 @pre System Lock held
166 @post System Lock held (but may have been released by this function)
168 virtual void SetFree(SPageInfo* aPageInfo)=0;
171 If the number of young pages excedes that specified by iYoungOldRatio then a
172 single page is made 'old'. Call this after adding a new 'young' page.
174 @pre System Lock held
175 @post System Lock left unchanged.
180 Add a page to the head of the live page list. I.e. make it the 'youngest' page.
182 @pre System Lock held
183 @post System Lock left unchanged.
185 void AddAsYoungest(SPageInfo* aPageInfo);
188 Mark a page as usused (EPagedFree) and add it to the end of the live page list.
189 I.e. make it the 'oldest' page, so that it is the first page to be reused.
191 @pre System Lock held
192 @post System Lock left unchanged.
194 void AddAsFreePage(SPageInfo* aPageInfo);
197 Remove a page from live page list.
198 It is set to the state EStatePagedDead.
200 @pre System Lock held
201 @post System Lock left unchanged.
203 void RemovePage(SPageInfo* aPageInfo);
206 Remove the oldest page from the live page list.
207 The returned page is no longer mapped by any page table and is marked as unused.
209 @pre System Lock held
210 @post System Lock left unchanged.
212 SPageInfo* GetOldestPage();
215 Remove pages from the live page list and return them to the system's free pool. (Free them.)
217 @param aNumPages The number of pages to free up.
218 @return True if all pages could be freed, false otherwise
219 @pre RamAlloc mutex held.
221 TBool GetFreePages(TInt aNumPages);
224 Give a RAM cache page to the paging system for managing.
225 This page of RAM may be reused for any purpose.
226 If the page has already been donated then no action is taken.
228 @param aPageInfo The page info for the donated page.
230 @see ReclaimRamCachePage.
232 @pre System Lock held
233 @post System Lock left unchanged.
235 void DonateRamCachePage(SPageInfo* aPageInfo);
238 Attempt to reclaim a RAM cache page given to the paging system with #DonateRamCachePage.
239 If the RAM page has not been reused for other purposes then the page is
240 removed from the paging system's management.
241 If the page has not previousely been donated then no action is taken.
243 @param aPageInfo The page info for the page to reclaim.
245 @return True if page successfully reclaimed, false otherwise.
247 @pre System Lock held
248 @post System Lock left unchanged.
250 TBool ReclaimRamCachePage(SPageInfo* aPageInfo);
254 Check whether the specified page can be discarded by the RAM cache.
256 @param aPageInfo The page info of the page being queried.
257 @return ETrue when the page can be discarded, EFalse otherwise.
258 @pre System lock held.
259 @post System lock held.
261 TBool IsPageDiscardable(SPageInfo& aPageInfo);
264 Discard the specified page.
265 Should only be called on a page if a previous call to IsPageDiscardable()
266 returned ETrue and the system lock hasn't been released between the calls.
267 The cache will not be reduced beyond the minimum size as a new page will
268 be allocated if necessary.
270 @param aPageInfo The page info of the page to be discarded
271 @param aBlockZoneId The ID of the RAM zone that shouldn't be allocated into.
272 @param aBlockRest Set to ETrue to stop allocation as soon as aBlockedZoneId is reached
273 in preference ordering. EFalse otherwise.
274 @return ETrue if the page could be discarded, EFalse otherwise.
276 @pre System lock held.
277 @post System lock held.
279 TBool DoDiscardPage(SPageInfo& aPageInfo, TUint aBlockedZoneId, TBool aBlockRest);
282 First stage in discarding a list of pages.
284 Must ensure that the pages will still be discardable even if system lock
285 is released after this method has completed.
286 To be used in conjunction with DemandPaging::DoDiscardPages1().
288 @param aPageList A NULL terminated list of the pages to be discarded
289 @return KErrNone on success.
291 @pre System lock held
292 @post System lock held
294 TInt DoDiscardPages0(SPageInfo** aPageList);
298 Final stage in discarding a list of page
299 Finish discarding the pages previously removed by DemandPaging::DoDiscardPages0().
301 @param aPageList A NULL terminated list of the pages to be discarded
302 @return KErrNone on success.
304 @pre System lock held
305 @post System lock held
307 TInt DoDiscardPages1(SPageInfo** aPageList);
310 Get a RAM page for use by a new page to be added to the live page list.
311 This tries to obtain a RAM page from the following places:
312 1. An unused page in the live page list.
313 2. The systems free pool.
314 3. The oldest page from the live page list.
316 @pre Calling thread must be in a critical section.
317 @pre System Lock held
318 @post System Lock held
320 SPageInfo* AllocateNewPage();
323 Move an old page the new youngest page. I.e. move it the the head of the live page list
324 and use the MMU to mark it accessible.
326 void Rejuvenate(SPageInfo* aPageInfo);
329 Reserve one page for locking.
330 Increments the reserved page count. May increse the size of the live list, and the minimum and
331 maximum page counts. To unreserve a page, simply decrement the reserved page count.
332 @return Whether the operation was sucessful.
337 Ensure all pages in the given region are present and 'lock' them so that they will not
339 To enable the pages to be paged out again, call UnlockRegion.
340 @param aProcess The process to which the linear addresses refer, or NULL for global memory
341 @pre Paging mutex held
343 TInt LockRegion(TLinAddr aStart,TInt aSize,DProcess* aProcess);
346 Mark in all pages in the given region as no longer locked.
347 @param aProcess The process to which the linear addresses refer, or NULL for global memory
348 This reverses the action of LockRegion.
350 TInt UnlockRegion(TLinAddr aStart,TInt aSize,DProcess* aProcess);
353 Flush (unmap) all memory which is demand paged.
354 This reduces the live page list to a minimum.
359 Page in the specified pages and 'lock' it so it will not be paged out.
360 To enable the page to be paged out again, call UnlockPage.
361 @param aPage The linear address of the page to be locked.
362 @param aProcess The process which the page is mapped in.
363 @param aPhysAddr The physical address of the page which was locked.
364 @pre System Lock held
365 @post System Lock held (but may have been released by this function)
367 TInt LockPage(TLinAddr aPage, DProcess* aProcess, TPhysAddr& aPhysAddr);
370 Mark the specified page as no longer locked.
371 This reverses the action of LockPage.
372 @param aPage The linear address of the page to be unlocked.
373 @param aProcess The process which the page is mapped in.
374 @param aPhysAddr The physical address of the page which was originally locked. (Or KPhysAddrInvalid.)
375 @pre System Lock held
376 @post System Lock held
378 TInt UnlockPage(TLinAddr aPage, DProcess* aProcess, TPhysAddr aPhysAddr);
381 Implementation of DDemandPagingLock::Alloc
383 TInt ReserveAlloc(TInt aSize, DDemandPagingLock& aLock);
386 Implementation of DDemandPagingLock::Free
388 void ReserveFree(DDemandPagingLock& aLock);
391 Implementation of DDemandPagingLock::Lock
393 TBool ReserveLock(DThread* aThread, TLinAddr aStart, TInt aSize, DDemandPagingLock& aLock);
396 Implementation of DDemandPagingLock::Unlock
398 void ReserveUnlock(DDemandPagingLock& aLock);
401 Ensure a page is present, paging it in if necessary. Used in the implementation of LockPage.
402 @param aPage The linear address of the page.
403 @param aProcess The process the page is mapped in.
405 virtual TInt EnsurePagePresent(TLinAddr aPage, DProcess* aProcess)=0;
408 Get the physical address of a page. Used in the implementation of LockPage and UnlockPage.
409 @param aPage The linear address of the page.
410 @param aProcess The process the page is mapped in.
412 virtual TPhysAddr LinearToPhysical(TLinAddr aPage, DProcess* aProcess)=0;
415 Install the specified paging device.
416 @param aDevice The device.
417 @return KErrNone or standard error code.
418 @post The devices DPagingDevice::iDeviceId has been set.
420 TInt InstallPagingDevice(DPagingDevice* aDevice);
423 Pure virutal function to allocate the virtual address space for temporary page mapping, for a
424 paging request object. This is called by DoInstallPagingDevice after the object is created.
425 @param aReq The paging request object
426 @param aReqId An small integer unique to the supplied paging request object
428 virtual void AllocLoadAddress(DPagingRequest& aReq, TInt aReqId)=0;
431 Notify the paging system that a page of physical RAM that was used for demand paging is no
432 longer mapped into any processes and is about to be freed. This is called on the multiple
433 memory model when a code segment is unloaded. It is not implemented on the moving memory model.
435 virtual void NotifyPageFree(TPhysAddr aPage)=0;
438 Called when a realtime thread takes a paging fault.
439 Checks whether it's ok for the thread to take to fault.
440 @return KErrNone if the paging fault should be further processed
442 TInt CheckRealtimeThreadFault(DThread* aThread, TAny* aContext);
445 Memory-model specific method to indicate if an address range might contain paged memory.
447 Implementations may return false positives but not false negatives - in other words this method
448 may say the range contains paged memory when it does not, but not the other way around.
450 This is used when pinning to determine whether memory could actally be paged.
452 virtual TBool MayBePaged(TLinAddr aStartAddr, TUint aLength);
455 TUint iMinimumPageCount; /**< Minimum size for the live page list, including locked pages */
456 TUint iMaximumPageCount; /**< Maximum size for the live page list, including locked pages */
457 TUint16 iYoungOldRatio; /**< Ratio of young to old pages in the live page list */
458 SDblQue iYoungList; /**< Head of 'young' page list. */
459 TUint iYoungCount; /**< Number of young pages */
460 SDblQue iOldList; /**< Head of 'old' page list. */
461 TUint iOldCount; /**< Number of young pages */
462 TUint iReservePageCount; /**< Number of pages reserved for locking */
463 TUint iMinimumPageLimit; /**< Minimum size for iMinimumPageCount, not including locked pages.
464 iMinimumPageCount >= iMinimumPageLimit + iReservePageCount */
466 TLinAddr iTempPages; /**< Uncached memory location in kernel memory which may be used
467 to map RAM pages whilst they are being paged in. */
469 static DemandPaging* ThePager; /**< Pointer to the single instance of this class */
471 TUint iInitMinimumPageCount; /**< Initial value for iMinimumPageCount */
472 TUint iInitMaximumPageCount; /**< Initial value for iMaximumPageCount */
474 TLinAddr iRomLinearBase; /**< Linear address of ROM start. */
475 TUint iRomSize; /**< ROM size in bytes. */
476 TLinAddr iRomPagedLinearBase; /**< Linear address for the start of pagable ROM. */
477 TUint iRomPagedSize; /**< The size of pagable ROM in bytes.
478 (Zero indicates ROM is not pagable.) */
479 SRomPageInfo* iRomPageIndex; /**< Pointer to ROM page index. */
481 TLinAddr iCodeLinearBase; /**< Linear adderss of start of user code area. */
482 TUint iCodeSize; /**< Size of user code area in bytes. */
486 // Paging device management...
490 Information for a paging device.
494 TBool iInstalled; /**< True, if this device has been installed. */
495 DPagingDevice* iDevice; /**< Pointer to device object */
498 TInt DoInstallPagingDevice(DPagingDevice* aDevice, TInt aId);
499 TInt ReadRomPage(const DPagingRequest* aReq, TLinAddr aRomAddress);
500 TInt ReadCodePage(const DPagingRequest* aReq, DMmuCodeSegMemory* aCodeSegMemory, TLinAddr aCodeAddress);
501 TInt Decompress(TInt aCompressionType,TLinAddr aDst,TLinAddr aSrc,TUint aSrcSize);
503 inline SPagingDevice& RomPagingDevice()
504 { return iPagingDevices[0]; }
506 inline SPagingDevice& CodePagingDevice(TInt aLocalDriveNumber)
507 { return iPagingDevices[aLocalDriveNumber + 1]; }
510 SPagingDevice iPagingDevices[KMaxPagingDevices]; /**< Array of paging devices. The first device is used for ROM paging. */
511 DChunk* iDeviceBuffersChunk; /**< Shared Chunk used to contain buffers for paging devices */
512 TLinAddr iDeviceBuffers; /**< Address for start of iDeviceBuffersChunk */
513 TUint iDeviceBufferSize; /**< Size of each individual buffer within iDeviceBuffers */
517 // Paging request management...
521 Resources needed to service a paging request.
523 class DPagingRequest : public SDblQueLink
528 TThreadMessage iMessage; /**< Used by the media driver to queue requests */
529 DMutex* iMutex; /**< A mutex for synchronisation and priority inheritance. */
530 TInt iUsageCount;/**< How many threads are using or waiting for this object. */
531 TLinAddr iBuffer; /**< A two-page buffer to read compressed data into. */
532 TLinAddr iLoadAddr; /**< Virtual address to map page at while it's being loaded. */
533 TPte* iLoadPte; /**< PTE corresponding to iLoadAddr. */
537 Creates a new DPagingRequest object and adds it to the list and free pool.
538 Called from DoInstallPagingDevice.
540 TInt CreateRequestObject();
543 Get a paging request object, waiting if necessary for one to become available.
544 @pre The system lock must be held.
546 DPagingRequest* AcquireRequestObject();
549 Release a previously acquired paging request object.
550 @pre The system lock must be held.
552 void ReleaseRequestObject(DPagingRequest* aReq);
555 /** Count of number of paging requests created. */
556 TUint iPagingRequestCount;
558 /** Array of paging request objects. */
559 DPagingRequest* iPagingRequests[KMaxPagingRequests];
561 /** Pool of unused paging request objects. */
562 SDblQue iFreeRequestPool;
565 Count of number of paging requests created or currently being created. Used to allocate request
566 object IDs and communicate eventual paging request count to ResizeLiveList.
568 TInt iNextPagingRequestCount;
577 Resize the live page list.
579 TInt ResizeLiveList(TUint aMinimumPageCount,TUint aMaximumPageCount);
582 Return state information about a page of memory ad the given address in the current process.
584 virtual TInt PageState(TLinAddr aAddr)=0;
587 Debug check to see if current thread can safely acquire the PagingMutex.
588 Use this check in locations where paged memory may be accessed. It will detect code which
589 would fault if paged memory were accessed.
590 @return The held mutex that prohibits acquiring the PagingMutex, or NULL.
592 DMutex* CheckMutexOrder();
595 Memory-model specific method to indicate if a read from an address range requires a mutex order
596 check. Used in the implementation of CheckMutexOrder.
598 virtual TBool NeedsMutexOrderCheck(TLinAddr aStartAddr, TUint aLength)=0;
603 static void Panic(TFault aFault);
608 Non-cryptographically secure linear congruential pseudo random number generator -
609 developed for speed with the use of a single multiply accumulate instruction.
610 Derived from section "7.8 RANDOM NUMBER GENERATION" in ARM System Developer's
613 static TUint32 FastPseudoRand()
615 // Make sure the seed has been lazily initialised.
616 FastPseudoRandomise();
621 // Keep trying to generate the next value in the pseudo random sequence until we
622 // are sure no race has been caused by another thread which has entered the same
626 oldX = PseudoRandSeed;
627 newX = 69069 * oldX + 41; // should compile to a single multiply accumulate instruction under ARM
629 while(!__e32_atomic_cas_acq32(&PseudoRandSeed, &oldX, newX));
635 Initialises the seed value for the pseudo random number generator.
637 static void FastPseudoRandomise()
639 // Create the initial seed value for the pseudo random number generator using
640 // the current system time.
641 if(!PseudoRandInitialised) // race-prone but harmless - worst that can happen is that the seed is initialised more than once until PseudoRandInitialised is set to true
643 Int64 t = Kern::SystemTime();
644 PseudoRandSeed = (TUint32)t ^ (TUint32)(t >> 32); // combine the two words for maximum entropy
646 PseudoRandInitialised = ETrue;
651 #ifdef __SUPPORT_DEMAND_PAGING_EMULATION__
652 TInt iOriginalRomPageCount;
653 TPhysAddr* iOriginalRomPages;
656 SVMEventInfo iEventInfo;
658 #ifdef __CONCURRENT_PAGING_INSTRUMENTATION__
659 TInt iWaitingCount; ///< number of threads waiting to acquire request object
660 TInt iPagingCount; ///< number of threads holding request object
661 TInt iMaxWaitingCount; ///< maximum historical value of iWaitingCount
662 TInt iMaxPagingCount; ///< maximum historical value of iPagingCount
665 #ifdef __DEMAND_PAGING_BENCHMARKS__
666 void RecordBenchmarkData(TPagingBenchmark aBm, TUint32 aStartTime, TUint32 aEndTime);
667 void ResetBenchmarkData(TPagingBenchmark aBm);
668 SPagingBenchmarkInfo iBenchmarkInfo[EMaxPagingBm];
672 static TBool PseudoRandInitialised; // flag to check whether FastPseudoRand has been lazily initialised yet
673 static volatile TUint32 PseudoRandSeed; // current random seed for FastPseudoRand()
676 #ifdef __DEMAND_PAGING_BENCHMARKS__
678 #define START_PAGING_BENCHMARK TUint32 _bmStart = NKern::FastCounter()
679 #define END_PAGING_BENCHMARK(pager, bm) pager->RecordBenchmarkData(bm, _bmStart, NKern::FastCounter())
683 #define START_PAGING_BENCHMARK
684 #define END_PAGING_BENCHMARK(pager, bm)