--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpagecleaner.cpp Tue May 11 17:28:22 2010 +0300
@@ -0,0 +1,244 @@
+// Copyright (c) 2007-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:
+//
+
+#include <kernel.h>
+#include "mpagecleaner.h"
+#include "mm.h"
+#include "mmu.h"
+#include "mpager.h"
+
+#ifdef __PAGING_PRE_CLEAN_DIRTY_PAGES
+
+inline void IgnorePrintf(...) { }
+
+#define PAGE_CLEANER_TRACE IgnorePrintf
+//#define PAGE_CLEANER_TRACE Kern::Printf
+
+_LIT(KThreadName, "PageCleaner");
+
+const TInt KThreadPriority = 25;
+
+/// The length of time the paging device is idle before we decide to use it for cleaning dirty
+/// pages, in milliseconds.
+const TInt KIdleDelayInMillis = 2;
+
+class DPageCleaner
+ {
+public:
+ DPageCleaner();
+ void Start();
+ void NotifyPagingDeviceIdle();
+ void NotifyPagingDeviceBusy();
+ void NotifyPagesToClean();
+
+private:
+ inline TBool IsRunning();
+ void UpdateBusyCount(TInt aChange);
+ void IdleTimerExpired(TUint aInitialNotificationCount);
+ void TryToClean();
+
+private:
+ static void TimerDfcFn(TAny*);
+ static void CleanerDfcFn(TAny*);
+
+private:
+ TInt iIdleDelayInTicks;
+ NTimer iDelayTimer;
+ TDfcQue iDfcQue;
+ TDfc iTimerDfc;
+ TDfc iCleanerDfc;
+ TBool iRunning;
+
+ // All state below is accessed with the MmuLock held.
+
+ /// Whether the paging device is currently idle.
+ TBool iPagingDeviceIdle;
+
+ /// Whether the paging device has been idle for longer than the wait period.
+ TBool iIdleForAWhile;
+
+ /// Whether the page cleaner is currently running.
+ TBool iCleaningInProgress;
+ };
+
+DPageCleaner ThePageCleaner;
+
+DPageCleaner::DPageCleaner() :
+ iTimerDfc(TimerDfcFn, NULL, 1),
+ iCleanerDfc(CleanerDfcFn, NULL, 1),
+ iRunning(EFalse),
+ iPagingDeviceIdle(ETrue),
+ iIdleForAWhile(ETrue),
+ iCleaningInProgress(EFalse)
+ {
+ }
+
+void DPageCleaner::Start()
+ {
+ TBool alreadyRunning = __e32_atomic_swp_ord32(&iRunning, ETrue);
+ if (alreadyRunning)
+ return;
+
+ iIdleDelayInTicks = NKern::TimerTicks(KIdleDelayInMillis);
+
+ TInt r = Kern::DfcQInit(&iDfcQue, KThreadPriority, &KThreadName);
+ __NK_ASSERT_ALWAYS(r == KErrNone);
+ iTimerDfc.SetDfcQ(&iDfcQue);
+ iCleanerDfc.SetDfcQ(&iDfcQue);
+
+ PAGE_CLEANER_TRACE("PageCleaner started");
+ }
+
+FORCE_INLINE TBool DPageCleaner::IsRunning()
+ {
+ return __e32_atomic_load_acq32(&iRunning);
+ }
+
+void DPageCleaner::NotifyPagingDeviceIdle()
+ {
+ __NK_ASSERT_DEBUG(MmuLock::IsHeld());
+ if (IsRunning())
+ {
+ iPagingDeviceIdle = ETrue;
+ if (!iDelayTimer.IsPending())
+ iDelayTimer.OneShot(iIdleDelayInTicks, iTimerDfc);
+ }
+ }
+
+void DPageCleaner::NotifyPagingDeviceBusy()
+ {
+ __NK_ASSERT_DEBUG(MmuLock::IsHeld());
+ if (IsRunning())
+ {
+ iDelayTimer.Cancel();
+ iPagingDeviceIdle = EFalse;
+ iIdleForAWhile = EFalse;
+ }
+ }
+
+void DPageCleaner::NotifyPagesToClean()
+ {
+ __NK_ASSERT_DEBUG(MmuLock::IsHeld());
+ if (IsRunning())
+ {
+ if (!iCleaningInProgress && iIdleForAWhile)
+ iCleanerDfc.Enque();
+ }
+ }
+
+void DPageCleaner::TimerDfcFn(TAny* aPtr)
+ {
+ ThePageCleaner.IdleTimerExpired((TUint)aPtr);
+ }
+
+void DPageCleaner::IdleTimerExpired(TUint aInitialNotificationCount)
+ {
+ MmuLock::Lock();
+ if (iPagingDeviceIdle)
+ {
+ iIdleForAWhile = ETrue;
+ if (!iCleaningInProgress && ThePager.HasPagesToClean())
+ iCleanerDfc.Enque();
+ }
+ MmuLock::Unlock();
+ }
+
+void DPageCleaner::CleanerDfcFn(TAny*)
+ {
+ ThePageCleaner.TryToClean();
+ }
+
+void DPageCleaner::TryToClean()
+ {
+ MmuLock::Lock();
+ TBool workToDo = iIdleForAWhile && ThePager.HasPagesToClean();
+ iCleaningInProgress = workToDo;
+ MmuLock::Unlock();
+
+ if (!workToDo)
+ {
+ PAGE_CLEANER_TRACE("PageCleaner - started but no work to do");
+ return;
+ }
+
+ for (;;)
+ {
+ PageCleaningLock::Lock();
+ MmuLock::Lock();
+ if (!iIdleForAWhile)
+ break;
+ TInt attempted = ThePager.CleanSomePages(ETrue);
+ if (attempted == 0)
+ break;
+ PAGE_CLEANER_TRACE("PageCleaner - attempted to clean %d pages", attempted);
+ MmuLock::Unlock();
+ PageCleaningLock::Unlock();
+ }
+
+ if (iIdleForAWhile)
+ PAGE_CLEANER_TRACE("PageCleaner - no more pages to clean");
+ else
+ PAGE_CLEANER_TRACE("PageCleaner - device now busy");
+
+ iCleaningInProgress = EFalse;
+ MmuLock::Unlock();
+ PageCleaningLock::Unlock();
+ }
+
+void PageCleaner::Start()
+ {
+ ThePageCleaner.Start();
+ }
+
+void PageCleaner::NotifyPagesToClean()
+ {
+ ThePageCleaner.NotifyPagesToClean();
+ }
+
+EXPORT_C void DPagingDevice::NotifyIdle()
+ {
+ ThePageCleaner.NotifyPagingDeviceIdle();
+ }
+
+EXPORT_C void DPagingDevice::NotifyBusy()
+ {
+ ThePageCleaner.NotifyPagingDeviceBusy();
+ }
+
+#else // __PAGING_PRE_CLEAN_DIRTY_PAGES not defined
+
+void PageCleaner::Start()
+ {
+ }
+
+void PageCleaner::NotifyPagesToClean()
+ {
+ }
+
+EXPORT_C void DPagingDevice::NotifyIdle()
+ {
+ }
+
+EXPORT_C void DPagingDevice::NotifyBusy()
+ {
+ }
+
+#endif
+
+EXPORT_C NFastMutex* DPagingDevice::NotificationLock()
+ {
+ // use the MmuLock
+ return &MmuLock::iLock;
+ }