# HG changeset patch # User Dremov Kirill (Nokia-D-MSW/Tampere) # Date 1273588102 -10800 # Node ID c734af59ce989bd867e12b65a3edb3014aca6083 # Parent 41f0cfe18c8063e220ab83262e29fba049071b27 Revision: 201019 Kit: 201019 diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bmarm/ekernsmp.def --- a/kernel/eka/bmarm/ekernsmp.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bmarm/ekernsmp.def Tue May 11 17:28:22 2010 +0300 @@ -1046,4 +1046,13 @@ RandomSalt__4KernUxUi @ 1045 NONAME ; Kern::RandomSalt(unsigned long long, unsigned int) RandomSalt__4KernPCUcUiUi @ 1046 NONAME R3UNUSED ; Kern::RandomSalt(unsigned char const *, unsigned int, unsigned int) SecureRandom__4KernR5TDes8 @ 1047 NONAME R3UNUSED ; Kern::SecureRandom(TDes8 &) + NotificationLock__13DPagingDevice @ 1048 NONAME R3UNUSED ; DPagingDevice::NotificationLock(void) + Configure__13KernCoreStatsUi @ 1049 NONAME R3UNUSED ABSENT ; KernCoreStats::Configure(unsigned int) + Engage__13KernCoreStatsi @ 1050 NONAME R3UNUSED ABSENT ; KernCoreStats::Engage(int) + Retire__13KernCoreStatsii @ 1051 NONAME R3UNUSED ABSENT ; KernCoreStats::Retire(int, int) + Stats__13KernCoreStatsPv @ 1052 NONAME R3UNUSED ABSENT ; KernCoreStats::Stats(void *) + SetNumberOfActiveCpus__5NKerni @ 1053 NONAME ABSENT + SetIdleHandler__3ArmPFPvUlPVv_vPv @ 1054 NONAME R3UNUSED ABSENT ; Arm::SetIdleHandler(void (*)(void *, unsigned long, void volatile *), void *) + FreeRamZone__4EpocUi @ 1055 NONAME R3UNUSED ABSENT ; Epoc::FreeRamZone(unsigned int) + SelectiveAlloc__16TBitMapAllocatorii @ 1056 NONAME R3UNUSED ; TBitMapAllocator::SelectiveAlloc(int, int) diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bmarm/ekernu.def --- a/kernel/eka/bmarm/ekernu.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bmarm/ekernu.def Tue May 11 17:28:22 2010 +0300 @@ -1042,4 +1042,14 @@ RandomSalt__4KernUxUi @ 1041 NONAME ; Kern::RandomSalt(unsigned long long, unsigned int) RandomSalt__4KernPCUcUiUi @ 1042 NONAME R3UNUSED ; Kern::RandomSalt(unsigned char const *, unsigned int, unsigned int) SecureRandom__4KernR5TDes8 @ 1043 NONAME R3UNUSED ; Kern::SecureRandom(TDes8 &) + NotificationLock__13DPagingDevice @ 1044 NONAME R3UNUSED ; DPagingDevice::NotificationLock(void) + Configure__13KernCoreStatsUi @ 1045 NONAME R3UNUSED ABSENT ; KernCoreStats::Configure(unsigned int) + Engage__13KernCoreStatsi @ 1046 NONAME R3UNUSED ABSENT ; KernCoreStats::Engage(int) + EnterIdle__13KernCoreStats @ 1047 NONAME R3UNUSED ABSENT ; KernCoreStats::EnterIdle() + LeaveIdle__13KernCoreStatsUi @ 1048 NONAME R3UNUSED ABSENT ; KernCoreStats::LeaveIdle(unsigned int) + Retire__13KernCoreStatsii @ 1049 NONAME R3UNUSED ABSENT ; KernCoreStats::Retire(int, int) + Stats__13KernCoreStatsPv @ 1050 NONAME R3UNUSED ABSENT ; KernCoreStats::Stats(void *) + SetIdleHandler__3ArmPFPvUl_vPv @ 1051 NONAME R3UNUSED ABSENT ; Arm::SetIdleHandler(void (*)(void *, unsigned long), void *) + FreeRamZone__4EpocUi @ 1052 NONAME R3UNUSED ABSENT ; Epoc::FreeRamZone(unsigned int) + SelectiveAlloc__16TBitMapAllocatorii @ 1053 NONAME R3UNUSED ; TBitMapAllocator::SelectiveAlloc(int, int) diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bwins/ekernu.def --- a/kernel/eka/bwins/ekernu.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bwins/ekernu.def Tue May 11 17:28:22 2010 +0300 @@ -929,4 +929,10 @@ ?RandomSalt@Kern@@SAX_KI@Z @ 928 NONAME ; public: static void __cdecl Kern::RandomSalt(unsigned __int64,unsigned int) ?RandomSalt@Kern@@SAXPBEII@Z @ 929 NONAME ; void Kern::RandomSalt(unsigned char const *, unsigned int, unsigned int) ?SecureRandom@Kern@@SAHAAVTDes8@@@Z @ 930 NONAME ; int Kern::SecureRandom(class TDes8 &) + ?NotificationLock@DPagingDevice@@QAEPAVNFastMutex@@XZ @ 931 NONAME ; public: class NFastMutex * __thiscall DPagingDevice::NotificationLock(void) + ?Configure@KernCoreStats@@SAHI@Z @ 932 NONAME ABSENT ; public: static int KernCoreStats::Configure(unsigned int) + ?Engage@KernCoreStats@@SAHH@Z @ 933 NONAME ABSENT ; public: static int KernCoreStats::Engage(int) + ?Retire@KernCoreStats@@SAHHH@Z @ 934 NONAME ABSENT ; public: static int KernCoreStats::Retire(int, int) + ?Stats@KernCoreStats@@SAHPAX@Z @ 935 NONAME ABSENT ; public: static int KernCoreStats::Stats(void *) + ?SelectiveAlloc@TBitMapAllocator@@QAEIHH@Z @ 936 NONAME ; public: unsigned int __thiscall TBitMapAllocator::SelectiveAlloc(int,int) diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bx86/ekernsmp.def --- a/kernel/eka/bx86/ekernsmp.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bx86/ekernsmp.def Tue May 11 17:28:22 2010 +0300 @@ -1004,4 +1004,12 @@ ?RandomSalt@Kern@@SAX_KI@Z @ 1003 NONAME ; public: static void __cdecl Kern::RandomSalt(unsigned __int64,unsigned int) ?RandomSalt@Kern@@SAXPBEII@Z @ 1004 NONAME ; void Kern::RandomSalt(unsigned char const *, unsigned int, unsigned int) ?SecureRandom@Kern@@SAHAAVTDes8@@@Z @ 1005 NONAME ; int Kern::SecureRandom(class TDes8 &) + ?NotificationLock@DPagingDevice@@QAEPAVNFastMutex@@XZ @ 1006 NONAME ; public: class NFastMutex * __thiscall DPagingDevice::NotificationLock(void) + ?Configure@KernCoreStats@@SAHI@Z @ 1007 NONAME ABSENT ; public: static int KernCoreStats::Configure(unsigned int) + ?Engage@KernCoreStats@@SAHH@Z @ 1008 NONAME ABSENT ; public: static int KernCoreStats::Engage(int) + ?Retire@KernCoreStats@@SAHHH@Z @ 1009 NONAME ABSENT ; public: static int KernCoreStats::Retire(int, int) + ?Stats@KernCoreStats@@SAHPAX@Z @ 1010 NONAME ABSENT ; public: static int KernCoreStats::Stats(void *) + ?SetNumberOfActiveCpus@NKern@@SAXH@Z @ 1011 NONAME ABSENT ; public: static void __cdecl NKern::SetNumberOfActiveCpus(int) + ?FreeRamZone@Epoc@@SAHI@Z @ 1012 NONAME ABSENT ; public: static int Epoc::FreeRamZone(unsigned int) + ?SelectiveAlloc@TBitMapAllocator@@QAEIHH@Z @ 1013 NONAME ; public: unsigned int __thiscall TBitMapAllocator::SelectiveAlloc(int,int) diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bx86/ekernu.def --- a/kernel/eka/bx86/ekernu.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bx86/ekernu.def Tue May 11 17:28:22 2010 +0300 @@ -948,4 +948,13 @@ ?RandomSalt@Kern@@SAX_KI@Z @ 947 NONAME ; public: static void __cdecl Kern::RandomSalt(unsigned __int64,unsigned int) ?RandomSalt@Kern@@SAXPBEII@Z @ 948 NONAME ; void Kern::RandomSalt(unsigned char const *, unsigned int, unsigned int) ?SecureRandom@Kern@@SAHAAVTDes8@@@Z @ 949 NONAME ; int Kern::SecureRandom(class TDes8 &) + ?NotificationLock@DPagingDevice@@QAEPAVNFastMutex@@XZ @ 950 NONAME ; public: class NFastMutex * __thiscall DPagingDevice::NotificationLock(void) + ?Configure@KernCoreStats@@SAHI@Z @ 951 NONAME ABSENT ; public: static int KernCoreStats::Configure(unsigned int) + ?Engage@KernCoreStats@@SAHH@Z @ 952 NONAME ABSENT ; public: static int KernCoreStats::Engage(int) + ?EnterIdle@KernCoreStats@@SAIXZ @ 953 NONAME ABSENT ; public: static unsigned int KernCoreStats::EnterIdle() + ?LeaveIdle@KernCoreStats@@SAXI@Z @ 954 NONAME ABSENT ; public: static void KernCoreStats::LeaveIdle(unsigned int) + ?Retire@KernCoreStats@@SAHHH@Z @ 955 NONAME ABSENT ; public: static int KernCoreStats::Retire(int, int) + ?Stats@KernCoreStats@@SAHPAX@Z @ 956 NONAME ABSENT ; public: static int KernCoreStats::Stats(void *) + ?FreeRamZone@Epoc@@SAHI@Z @ 957 NONAME ABSENT ; public: static int Epoc::FreeRamZone(unsigned int) + ?SelectiveAlloc@TBitMapAllocator@@QAEIHH@Z @ 958 NONAME ; public: unsigned int __thiscall TBitMapAllocator::SelectiveAlloc(int,int) diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bx86gcc/ekernsmp.def --- a/kernel/eka/bx86gcc/ekernsmp.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bx86gcc/ekernsmp.def Tue May 11 17:28:22 2010 +0300 @@ -1092,4 +1092,12 @@ _ZN4Kern10RandomSaltEyj @ 1091 NONAME _ZN4Kern10RandomSaltEPKhjj @ 1092 NONAME _ZN4Kern12SecureRandomER5TDes8 @ 1093 NONAME + _ZN13DPagingDevice16NotificationLockEv @ 1094 NONAME + _ZN13KernCoreStats5StatsEPv @ 1095 NONAME ABSENT + _ZN13KernCoreStats6EngageEi @ 1096 NONAME ABSENT + _ZN13KernCoreStats6RetireEii @ 1097 NONAME ABSENT + _ZN13KernCoreStats9ConfigureEj @ 1098 NONAME ABSENT + _ZN5NKern21SetNumberOfActiveCpusEi @ 1099 NONAME ABSENT + _ZN4Epoc11FreeRamZoneEj @ 1100 NONAME ABSENT + _ZN16TBitMapAllocator14SelectiveAllocEii @ 1101 NONAME diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/bx86gcc/ekernu.def --- a/kernel/eka/bx86gcc/ekernu.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/bx86gcc/ekernu.def Tue May 11 17:28:22 2010 +0300 @@ -1031,4 +1031,13 @@ _ZN4Kern10RandomSaltEyj @ 1030 NONAME _ZN4Kern10RandomSaltEPKhjj @ 1031 NONAME _ZN4Kern12SecureRandomER5TDes8 @ 1032 NONAME + _ZN13DPagingDevice16NotificationLockEv @ 1033 NONAME + _ZN13KernCoreStats5StatsEPv @ 1034 NONAME ABSENT + _ZN13KernCoreStats6EngageEi @ 1035 NONAME ABSENT + _ZN13KernCoreStats6RetireEii @ 1036 NONAME ABSENT + _ZN13KernCoreStats9ConfigureEj @ 1037 NONAME ABSENT + _ZN13KernCoreStats9EnterIdleEv @ 1038 NONAME ABSENT + _ZN13KernCoreStats9LeaveIdleEj @ 1039 NONAME ABSENT + _ZN4Epoc11FreeRamZoneEj @ 1040 NONAME ABSENT + _ZN16TBitMapAllocator14SelectiveAllocEii @ 1041 NONAME diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/drivers/locmedia/locmedia.cpp --- a/kernel/eka/drivers/locmedia/locmedia.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/drivers/locmedia/locmedia.cpp Tue May 11 17:28:22 2010 +0300 @@ -3615,15 +3615,19 @@ void DPrimaryMediaBase::RequestCountInc() { __ASSERT_DEBUG(iBody, LOCM_FAULT()); - TInt oldVal = (TInt) __e32_atomic_add_ord32(&iBody->iRequestCount, (TUint) 1); -//Kern::Printf("RCINC: this %x cnt %d, old %d", this, iBody->iRequestCount, oldVal); - - OstTraceDefExt2( OST_TRACE_CATEGORY_RND, TRACE_DEMANDPAGING, DPRIMARYMEDIABASE_REQUESTCOUNTINC, "new count=%d; old count=%d", iBody->iRequestCount, oldVal ); - - if (oldVal == 0 && iBody->iPagingDevice) + if (iBody->iPagingDevice) { -//Kern::Printf("RCINC: NotifyBusy()"); - iBody->iPagingDevice->NotifyBusy(); + NFastMutex* lock = iBody->iPagingDevice->NotificationLock(); + NKern::FMWait(lock); + TInt oldVal = iBody->iRequestCount++; + //Kern::Printf("RCINC: this %x cnt %d, old %d", this, iBody->iRequestCount, oldVal); + OstTraceDefExt2( OST_TRACE_CATEGORY_RND, TRACE_DEMANDPAGING, DPRIMARYMEDIABASE_REQUESTCOUNTINC, "new count=%d; old count=%d", iBody->iRequestCount, oldVal ); + if (oldVal == 0) + { + //Kern::Printf("RCINC: NotifyBusy()"); + iBody->iPagingDevice->NotifyBusy(); + } + NKern::FMSignal(lock); } } @@ -3635,17 +3639,21 @@ void DPrimaryMediaBase::RequestCountDec() { __ASSERT_DEBUG(iBody, LOCM_FAULT()); - TInt oldVal = (TInt) __e32_atomic_add_ord32(&iBody->iRequestCount, (TUint) -1); -//Kern::Printf("RCDEC: this %x cnt %d, old %d", this, iBody->iRequestCount, oldVal); - - OstTraceDefExt2( OST_TRACE_CATEGORY_RND, TRACE_DEMANDPAGING, DPRIMARYMEDIABASE_REQUESTCOUNTDEC, "new count=%d; old count=%d", iBody->iRequestCount, oldVal ); - - if (oldVal == 1 && iBody->iPagingDevice) + if (iBody->iPagingDevice) { -//Kern::Printf("RCDEC: NotifyIdle()"); - iBody->iPagingDevice->NotifyIdle(); + NFastMutex* lock = iBody->iPagingDevice->NotificationLock(); + NKern::FMWait(lock); + TInt oldVal = iBody->iRequestCount--; + //Kern::Printf("RCDEC: this %x cnt %d, old %d", this, iBody->iRequestCount, oldVal); + OstTraceDefExt2( OST_TRACE_CATEGORY_RND, TRACE_DEMANDPAGING, DPRIMARYMEDIABASE_REQUESTCOUNTDEC, "new count=%d; old count=%d", iBody->iRequestCount, oldVal ); + if (oldVal == 1) + { + //Kern::Printf("RCDEC: NotifyIdle()"); + iBody->iPagingDevice->NotifyIdle(); + } + NKern::FMSignal(lock); + __ASSERT_DEBUG(iBody->iRequestCount >= 0, LOCM_FAULT()); } - __ASSERT_DEBUG(iBody->iRequestCount >= 0, LOCM_FAULT()); } #endif // __DEMAND_PAGING__ diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/drivers/pbus/mmc/stack.cpp --- a/kernel/eka/drivers/pbus/mmc/stack.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/drivers/pbus/mmc/stack.cpp Tue May 11 17:28:22 2010 +0300 @@ -3517,13 +3517,10 @@ OstTrace0( TRACE_INTERNALS, DMMCSTACK_DETERMINEBUSWIDTHANDCLOCKSM3, "EStWritePowerClass" ); // Check the card type is valid - // The only currently valid values for this field are 0x01 or 0x03 - TUint cardType = cardP->iExtendedCSD.CardType(); - if (cardType != (TExtendedCSD::EHighSpeedCard26Mhz) && - cardType != (TExtendedCSD::EHighSpeedCard26Mhz | TExtendedCSD::EHighSpeedCard52Mhz)) - { - __KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardType)); - OstTrace1( TRACE_INTERNALS, DMMCSTACK_DETERMINEBUSWIDTHANDCLOCKSM4, "Unsupported card type=%u", cardType ); + if (!(cardP->iExtendedCSD.IsSupportedCardType())) + { + __KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardP->iExtendedCSD.CardType())); + OstTrace1( TRACE_INTERNALS, DMMCSTACK_DETERMINEBUSWIDTHANDCLOCKSM4, "Unsupported card type=%u", cardP->iExtendedCSD.CardType() ); SMF_GOTOS(EStExit); } @@ -3618,13 +3615,10 @@ cardP->SetHighSpeedClock(0); // Check the card type is valid - // The only currently valid values for this field are 0x01 or 0x03 - TUint cardType = cardP->iExtendedCSD.CardType(); - if (cardType != (TExtendedCSD::EHighSpeedCard26Mhz) && - cardType != (TExtendedCSD::EHighSpeedCard26Mhz | TExtendedCSD::EHighSpeedCard52Mhz)) - { - __KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardType)); - OstTrace1( TRACE_INTERNALS, DMMCSTACK_CONFIGUREHIGHSPEEDSM4, "Unsupported card type=%u", cardType ); + if (!(cardP->iExtendedCSD.IsSupportedCardType())) + { + __KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardP->iExtendedCSD.CardType())); + OstTrace1( TRACE_INTERNALS, DMMCSTACK_CONFIGUREHIGHSPEEDSM4, "Unsupported card type=%u", cardP->iExtendedCSD.CardType() ); SMF_GOTOS(EStExit); } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/drivers/usbcc/chapter9.cpp --- a/kernel/eka/drivers/usbcc/chapter9.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/drivers/usbcc/chapter9.cpp Tue May 11 17:28:22 2010 +0300 @@ -1136,8 +1136,19 @@ // New configuration is the same as the old one (but not 0) if (iCurrentConfig == aValue) { - // no-op __KTRACE_OPT(KUSB, Kern::Printf(" Configuration: New == Old == %d --> exiting", aValue)); + + // From the spec 9.1.1.5, Data toggle is reset to zero here when + // setconfiguration(x->x)(x!=0) received, although we only support + // single configuration currently. + TInt num = 0; + TInt ret = DoForEveryEndpointInUse(&DUsbClientController::ResetDataToggle, num); + if(ret != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf(" Error: Endpoint data toggle reset failed")); + } + __KTRACE_OPT(KUSB, Kern::Printf(" Called ResetDataToggle()for %d endpoints", num)); + return; } // Device is already configured diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/eabi/ekernsmp.def --- a/kernel/eka/eabi/ekernsmp.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/eabi/ekernsmp.def Tue May 11 17:28:22 2010 +0300 @@ -1180,4 +1180,13 @@ _ZN4Kern10RandomSaltEyj @ 1179 NONAME _ZN4Kern10RandomSaltEPKhjj @ 1180 NONAME _ZN4Kern12SecureRandomER5TDes8 @ 1181 NONAME + _ZN13DPagingDevice16NotificationLockEv @ 1182 NONAME + _ZN13KernCoreStats5StatsEPv @ 1183 NONAME ABSENT + _ZN13KernCoreStats6EngageEi @ 1184 NONAME ABSENT + _ZN13KernCoreStats6RetireEii @ 1185 NONAME ABSENT + _ZN13KernCoreStats9ConfigureEj @ 1186 NONAME ABSENT + _ZN5NKern21SetNumberOfActiveCpusEi @ 1187 NONAME ABSENT + _ZN3Arm14SetIdleHandlerEPFvPvmPVvES0_ @ 1188 NONAME ABSENT + _ZN4Epoc11FreeRamZoneEj @ 1189 NONAME ABSENT + _ZN16TBitMapAllocator14SelectiveAllocEii @ 1190 NONAME diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/eabi/ekernu.def --- a/kernel/eka/eabi/ekernu.def Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/eabi/ekernu.def Tue May 11 17:28:22 2010 +0300 @@ -1171,4 +1171,14 @@ _ZN4Kern10RandomSaltEyj @ 1170 NONAME _ZN4Kern10RandomSaltEPKhjj @ 1171 NONAME _ZN4Kern12SecureRandomER5TDes8 @ 1172 NONAME + _ZN13DPagingDevice16NotificationLockEv @ 1173 NONAME + _ZN13KernCoreStats5StatsEPv @ 1174 NONAME ABSENT + _ZN13KernCoreStats6EngageEi @ 1175 NONAME ABSENT + _ZN13KernCoreStats6RetireEii @ 1176 NONAME ABSENT + _ZN13KernCoreStats9ConfigureEj @ 1177 NONAME ABSENT + _ZN13KernCoreStats9EnterIdleEv @ 1178 NONAME ABSENT + _ZN13KernCoreStats9LeaveIdleEj @ 1179 NONAME ABSENT + _ZN3Arm14SetIdleHandlerEPFvPvmES0_ @ 1180 NONAME ABSENT + _ZN4Epoc11FreeRamZoneEj @ 1181 NONAME ABSENT + _ZN16TBitMapAllocator14SelectiveAllocEii @ 1182 NONAME diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/d32usbdi_errors.h --- a/kernel/eka/include/d32usbdi_errors.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/d32usbdi_errors.h Tue May 11 17:28:22 2010 +0300 @@ -201,6 +201,7 @@ EUsbDevMonDeviceAttachDenied = 41, EUsbHubDriverZeroInterfaceTokenProduced = 42, EUsbInterfaceSuccessfulPipeOpenWithNoPipe = 43, + EFailedToLockHostStackInWaitDeviceStateMutex = 44, }; _LIT(KUsbDescFaultCat, "USBDesc-Fault"); diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/drivers/mmc.h --- a/kernel/eka/include/drivers/mmc.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/drivers/mmc.h Tue May 11 17:28:22 2010 +0300 @@ -1540,7 +1540,8 @@ enum TCardTypes { EHighSpeedCard26Mhz = 0x01, - EHighSpeedCard52Mhz = 0x02 + EHighSpeedCard52Mhz = 0x02, + ECardTypeMsk = 0x03 }; /** @@ -1714,6 +1715,9 @@ /** returns the contents of the S_A_TIMEOUT field */ inline TUint SleepAwakeTimeout() const; + + /** returns True if the CARD_TYPE field conatains a valid value **/ + inline TBool IsSupportedCardType() const; private: /** diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/drivers/mmc.inl --- a/kernel/eka/include/drivers/mmc.inl Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/drivers/mmc.inl Tue May 11 17:28:22 2010 +0300 @@ -190,6 +190,19 @@ inline TUint TExtendedCSD::BootBusWidth() const {return iData[EBootBusWidthIndex];} inline TUint TExtendedCSD::EraseGroupDef() const {return iData[EEraseGroupDefIndex];} +/* + * MMC v4.3 specification states the only valid values for CardType are 0x01 or 0x03 + */ +inline TBool TExtendedCSD::IsSupportedCardType() const + { + switch (CardType()&ECardTypeMsk) + { + case 0x01: + case 0x03: return ETrue; + default: return EFalse; + } + } + // -------- class TMMCStatus -------- /** * Constructor for TMMCStatus. diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/e32ver.h --- a/kernel/eka/include/e32ver.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/e32ver.h Tue May 11 17:28:22 2010 +0300 @@ -28,7 +28,7 @@ const TInt KE32MajorVersionNumber=2; const TInt KE32MinorVersionNumber=0; -const TInt KE32BuildVersionNumber=2111; +const TInt KE32BuildVersionNumber=2117; const TInt KMachineConfigurationMajorVersionNumber=1; const TInt KMachineConfigurationMinorVersionNumber=0; diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/kernel/kbma.h --- a/kernel/eka/include/kernel/kbma.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/kernel/kbma.h Tue May 11 17:28:22 2010 +0300 @@ -39,6 +39,7 @@ IMPORT_C void Free(TInt aPos); IMPORT_C void Alloc(TInt aStart, TInt aLength); IMPORT_C void Free(TInt aStart, TInt aLength); + IMPORT_C TUint SelectiveAlloc(TInt aStart, TInt aLength); IMPORT_C void SelectiveFree(TInt aStart, TInt aLength); IMPORT_C TBool NotFree(TInt aStart, TInt aLength) const; IMPORT_C TBool NotAllocated(TInt aStart, TInt aLength) const; diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/kernel/kern_priv.h --- a/kernel/eka/include/kernel/kern_priv.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/kernel/kern_priv.h Tue May 11 17:28:22 2010 +0300 @@ -2804,6 +2804,7 @@ static TUint NumberOfFreeDpPages(); static TUint NumberOfDirtyDpPages(); static TInt MovePage(TPhysAddr aOld, TPhysAddr& aNew, TUint aBlockZoneId, TBool aBlockRest); + static TInt MoveAndAllocPage(TPhysAddr aAddr, TZonePageType aPageType); static TInt DiscardPage(TPhysAddr aAddr, TUint aBlockZoneId, TBool aBlockRest); static void RamZoneClaimed(SZone* aZone); static TInt RamDefragFault(TAny* aExceptionInfo); diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/kernel/kernel.h --- a/kernel/eka/include/kernel/kernel.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/kernel/kernel.h Tue May 11 17:28:22 2010 +0300 @@ -257,8 +257,8 @@ const TUint8 KMutexOrdRamAlloc = 0x04; /**< @internalComponent */ #if defined(__MEMMODEL_FLEXIBLE__) const TUint8 KMutexOrdSyncPhysMem = 0x03; /**< @internalComponent */ +const TUint8 KMutexOrdPageOut = 0x02; /**< @internalComponent */ #endif -const TUint8 KMutexOrdPageOut = 0x02; /**< @internalComponent */ const TUint8 KMutexOrdResourceManager = 0x01; /**< @internalComponent */ @@ -2838,6 +2838,12 @@ inline virtual TInt DeleteNotify(TThreadMessage* aReq,TUint aOffset,TUint aSize); /** + Return the lock that should be used to synchronise calculation of the idle/busy state and + subsequent calls to #NotifyIdle and #NotifyBusy. + */ + IMPORT_C NFastMutex* NotificationLock(); + + /** Called by the paging device to notify the kernel that the device has just become idle and is not currently processing any requests. diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/memmodel/epoc/mmubase/mmubase.h --- a/kernel/eka/include/memmodel/epoc/mmubase/mmubase.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/memmodel/epoc/mmubase/mmubase.h Tue May 11 17:28:22 2010 +0300 @@ -525,8 +525,8 @@ public: TInt AllocRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aPageType, TUint aBlockedZoneId=KRamZoneInvalidId, TBool aBlockRest=EFalse); TInt ZoneAllocRamPages(TUint* aZoneIdList, TUint aZoneIdCount, TPhysAddr* aPageList, TInt aNumPages, TZonePageType aPageType); - TInt AllocContiguousRam(TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aPageType, TInt aAlign, TUint aBlockedZoneId=KRamZoneInvalidId, TBool aBlockRest=EFalse); - TInt ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aPageType, TInt aAlign); + TInt AllocContiguousRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign); + TInt ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign); public: TInt iPageSize; // page size in bytes diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/memmodel/epoc/mmubase/ramalloc.h --- a/kernel/eka/include/memmodel/epoc/mmubase/ramalloc.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/memmodel/epoc/mmubase/ramalloc.h Tue May 11 17:28:22 2010 +0300 @@ -142,8 +142,14 @@ void FreeRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType); TInt AllocRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType, TUint aBlockedZoneId=KRamZoneInvalidId, TBool aBlockRest=EFalse); TInt ZoneAllocRamPages(TUint* aZoneIdList, TUint aZoneIdCount, TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType); - TInt AllocContiguousRam(TUint aNumPages, TPhysAddr& aPhysAddr, TZonePageType aType, TInt aAlign=0, TUint aBlockZoneId=KRamZoneInvalidId, TBool aBlockRest=EFalse); - TInt ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aType, TInt aAlign); + TInt AllocContiguousRam(TUint aNumPages, TPhysAddr& aPhysAddr, TInt aAlign=0); +#if !defined(__MEMMODEL_MULTIPLE__) && !defined(__MEMMODEL_MOVING__) + void BlockContiguousRegion(TPhysAddr aAddrBase, TUint aNumPages); + void UnblockSetAllocRuns(TUint& aOffset1, TUint& aOffset2, TUint aRunLength1, TUint aRunLength2, TUint& aAllocLength, TUint& aAllocStart); + void UnblockContiguousRegion(TPhysAddr aAddrBase, TUint aNumPages); + TBool ClearContiguousRegion(TPhysAddr aAddrBase, TPhysAddr aZoneBase, TUint aNumPages, TInt& aOffset); +#endif + TInt ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign); #ifdef _DEBUG void DebugDump(); #endif @@ -158,6 +164,7 @@ TInt GetZoneAddress(TUint aZoneId, TPhysAddr& aPhysBase, TUint& aNumPages); TInt HalFunction(TInt aFunction, TAny* a1, TAny* a2); TInt NextAllocatedPage(SZone* aZone, TUint& aOffset, TZonePageType aType) const; + TInt NextAllocatedRun(SZone* aZone, TUint& aOffset, TUint aEndOffset, TZonePageType aType) const; TUint GenDefragFreePages(TZonePageType aType) const; SZone* GeneralDefragStart0(TGenDefragStage& aStage, TUint& aRequiredToBeDiscarded); SZone* GeneralDefragNextZone0(); @@ -205,9 +212,7 @@ SDblQueLink* iZoneGeneralPrefLink; /**< Link to the current RAM zone being defragged*/ SDblQueLink* iZoneGeneralTmpLink; /**< Link to the current RAM zone being defragged*/ TUint iZoneGeneralStage; /**< The current stage of any general defrag operation*/ -#ifdef _DEBUG - TBool iAllowBmaVerify; -#endif + TUint iContiguousReserved; /**< The count of the number of separate contiguous allocations that have reserved pages*/ }; #endif diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/include/u32hal.h --- a/kernel/eka/include/u32hal.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/include/u32hal.h Tue May 11 17:28:22 2010 +0300 @@ -2868,6 +2868,7 @@ EPagingBmDeleteNotifyDataPage, EPagingBmReadDataMedia, EPagingBmWriteDataMedia, + EPagingBmRejuvenate, // only implemented on FMM EMaxPagingBm }; diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/kernel/arm/cipc.cia --- a/kernel/eka/kernel/arm/cipc.cia Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/kernel/arm/cipc.cia Tue May 11 17:28:22 2010 +0300 @@ -57,7 +57,7 @@ __NAKED__ void ExecHandler::MessageComplete(RMessageK* /*aMsg*/, TInt /*aReason*/) { - asm("ldr ip, [r0, #%a0]" : : "i" _FOFF(RMessageK, iFunction)); // get iFunction, as per preprocessor + asm("ldr ip, [r0, #%a0]" : : "i" _FOFF(RMessageK, iFunction)); // get iFunction, as per preprocessor // Subroutine MessageComplete // Complete an IPC message @@ -69,65 +69,73 @@ #ifdef BTRACE_CLIENT_SERVER asm("stmfd sp!,{r0,r1,ip,lr}"); - asm("mov r2,r1"); // arg2 = aReason - asm("mov r1,r0"); // arg1 = aMsg - asm("ldr r0,_messageCompleteTraceHeader"); // arg0 = header + asm("mov r2,r1"); // arg2 = aReason + asm("mov r1,r0"); // arg1 = aMsg + asm("ldr r0,_messageCompleteTraceHeader"); // arg0 = header asm("bl " CSM_ZN6BTrace4OutXEmmmm); asm("ldmfd sp!,{r0,r1,ip,lr}"); #endif asm("cmp ip, #%a0" : : "i" (RMessage2::EDisConnect)); asm("ldreq r0, [r0, #%a0]" : : "i" _FOFF(RMessageK,iSession)); - asm("beq " CSM_ZN8DSession19CloseFromDisconnectEv ); // if disconnect, do it in C++ - asm("mov r2, r1 "); // r2=aReason + asm("beq " CSM_ZN8DSession19CloseFromDisconnectEv ); // if disconnect, do it in C++ + + asm("mov r2, r1 "); // r2 = aReason ASM_DEBUG2(Complete,r0,r2); - asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(RMessageK, iSession)); // r3=iSession - - asm("subs r1, ip, #%a0" : : "i" (RMessage2::EConnect)); // (m.iFunction == RMessage2::EConnect)? - asm("streq r1, [r3, #%a0] " : : "i" _FOFF(DSession, iConnectMsgPtr)); // iSession->iConnectMsgPtr = NULL - - asm("ldr r1, [r3, #%a0]" : : "i" _FOFF(DSession,iAccessCount)); // r1=iSession->iAccessCount - asm("cmp r1, #0 "); // iAccessCount = 0? + asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(RMessageK, iSession)); // r3 = iSession + asm("subs r1, ip, #%a0" : : "i" (RMessage2::EConnect)); // (m.iFunction == RMessage2::EConnect)? + asm("streq r1, [r3, #%a0] " : : "i" _FOFF(DSession, iConnectMsgPtr)); // iSession->iConnectMsgPtr = NULL + asm("ldr r1, [r3, #%a0]" : : "i" _FOFF(DSession,iAccessCount)); // r1 = iSession->iAccessCount + asm("cmp r1, #0 "); // iAccessCount = 0? asm("beq 2f "); - // if (!s->IsClosing()) - asm("mov r1, r0"); // r1 = RMessageK ptr - asm("ldr r0, [r0, #%a0] " : : "i" _FOFF(RMessageK,iClient)); // r0=iClient + // !s->IsClosing() + asm("mov r1, r0"); // r1 = RMessageK ptr + asm("ldr r0, [r0, #%a0] " : : "i" _FOFF(RMessageK,iClient)); // r0 = iClient + asm("ldrb ip, [r0, #%a0] " : : "i" _FOFF(DThread,iMState)); // ip = iClient->iMState + asm("cmp ip, #%a0" : : "i" (DThread::EDead)); // (iMState == EDead)? + asm("beq 1f "); + + // if (!s->IsClosing() && m.iClient->iMState != DThread::EDead) asm("mov ip, #1"); - asm("str ip, [r1, #%a0]" : : "i" _FOFF(RMessageK, iServerLink.iNext)); // iServerLink.iNext=1 - asm("b " CSM_ZN4Kern20QueueRequestCompleteEP7DThreadP14TClientRequesti); + asm("str ip, [r1, #%a0]" : : "i" _FOFF(RMessageK, iServerLink.iNext)); // iServerLink.iNext=1 + asm("b " CSM_ZN4Kern20QueueRequestCompleteEP7DThreadP14TClientRequesti); // tail call - // if (s->IsClosing()) + // m.iClient->iMState == DThread::EDead + asm("1: "); // shuffle RMessageK and iFunction back to expected registers + asm("ldr ip, [r1, #%a0]" : : "i" _FOFF(RMessageK, iFunction)); // refetch iFunction + asm("mov r0, r1"); // r0 = RMessageK ptr + + // else (closing or dead) asm("2: "); - asm("cmp ip, #%a0" : : "i" (RMessage2::EConnect)); // (m.iFunction == RMessage2::EConnect)? - asm("beq 4f "); - asm("3: "); - asm("stmfd sp!, {r0,lr} "); - asm("bl " CSM_ZN14TClientRequest5ResetEv); - asm("ldmfd sp!, {r0,lr} "); - asm("b " CSM_ZN9RMessageK8CloseRefEv); + asm("cmp ip, #%a0" : : "i" (RMessage2::EConnect)); // (m.iFunction == RMessage2::EConnect)? + asm("bne 3f "); - asm("4: "); - // if closing & connect msg - asm("ldr r2, [r3, #%a0] " : : "i" _FOFF(DSession, iSessionCookie)); // r2=iSession->iSessionCookie + // (closing or dead) and it's a connect msg + asm("ldr r2, [r3, #%a0] " : : "i" _FOFF(DSession, iSessionCookie)); // r2=iSession->iSessionCookie asm("teq r2, #0"); #ifdef _DEBUG asm("beq nosession "); - asm("ldr r1, [r3, #%a0] " : : "i" _FOFF(DSession, iServer)); // r1=iSession->iServer + asm("ldr r1, [r3, #%a0] " : : "i" _FOFF(DSession, iServer)); // r1=iSession->iServer asm("cmp r1, #0 "); asm("beq noserver "); - asm("ldr r2, [r3, #%a0] " : : "i" (_FOFF(DSession, iDisconnectMsgPtr))); // r2=iSession->iDisconnectMsgPtr + asm("ldr r2, [r3, #%a0] " : : "i" (_FOFF(DSession, iDisconnectMsgPtr))); // r2=iSession->iDisconnectMsgPtr asm("ldr r2, [r2, #%a0] " : : "i" (_FOFF(RMessageK, iServerLink.iNext))); // r2=iDisconnectMsgPtr->iServerLink.iNext asm("cmp r2, #0 "); - asm("beq __FaultMsgCompleteDiscNotSent "); // die if a session has been created and no disc msg sent - asm("ldr r2, [r3, #%a0] " : : "i" _FOFF(DSession, iSessionCookie)); // r2=iSession->iSessionCookie + asm("beq __FaultMsgCompleteDiscNotSent "); // tail call to die if a session has been created and no disc msg sent + asm("ldr r2, [r3, #%a0] " : : "i" _FOFF(DSession, iSessionCookie)); // r2=iSession->iSessionCookie asm("noserver: "); asm("teq r2, #0"); asm("nosession: "); #endif //_DEBUG asm("moveq r0, r3 "); - asm("beq __SendDiscMsg "); // if no session object to clean up, send disc msg in C++ - asm("b 3b "); // return + asm("beq __SendDiscMsg "); // if no session object to clean up, tail call to send disc msg in C++ + + asm("3: "); + asm("stmfd sp!, {r0,lr} "); + asm("bl " CSM_ZN14TClientRequest5ResetEv); + asm("ldmfd sp!, {r0,lr} "); + asm("b " CSM_ZN9RMessageK8CloseRefEv); // tail call #ifdef BTRACE_CLIENT_SERVER asm("_messageCompleteTraceHeader:"); @@ -358,8 +366,8 @@ #endif asm("ldr r1, [r1, #%a0]" : : "i" (_FOFF(DThread,iOwningProcess)-_FOFF(DThread,iNThread))); // r1->process to check asm("bl do_messagek "); + asm("bcc 0f "); // if bad handle, panic asm("ldr ip, [r0, #%a0]" : : "i" _FOFF(RMessageK,iFunction)); // ip = function - asm("bcc 0f "); // if bad handle, panic asm("cmp ip, #%a0" : : "i" ((TInt)RMessage2::EDisConnect)); // check iFunction != RMessage2::EDisConnect asm("ldmnefd sp!, {r4,pc} "); // if not, return OK asm("0: "); diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/klib/bma.cpp --- a/kernel/eka/klib/bma.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/klib/bma.cpp Tue May 11 17:28:22 2010 +0300 @@ -762,6 +762,49 @@ } +/** Allocates a specific range of bit positions. + + The specified range must lie within the total range for this allocator but it is + not necessary that all the positions are currently free. + + @param aStart First position to allocate. + @param aLength Number of consecutive positions to allocate, must be >0. + @return The number of previously free positions that were allocated. + */ +EXPORT_C TUint TBitMapAllocator::SelectiveAlloc(TInt aStart, TInt aLength) + { + __ASSERT_ALWAYS(TUint(aStart) < TUint(iSize), TBMA_FAULT()); + __ASSERT_ALWAYS(TUint(aStart + aLength) >= TUint(aStart), TBMA_FAULT()); + __ASSERT_ALWAYS(TUint(aStart + aLength) <= TUint(iSize), TBMA_FAULT()); + TInt wix = aStart >> 5; + TInt sbit = aStart & 31; + TUint32* pW = iMap + wix; + iAvail -= aLength; // update free count assuming no positions already allocated + TInt ebit = sbit + aLength; + if (ebit < 32) + { + TUint32 b = ((0xffffffffu >> aLength) >> sbit) | ~(0xffffffffu >> sbit); + TUint32 w = *pW; + *pW = w & b; // mark all positions allocated + TUint allocated = __e32_bit_count_32(~w & ~b); + iAvail += allocated; // increase free count by number of positions already allocated + return aLength - allocated; + } + TUint32 b = ~(0xffffffffu >> sbit); + while (ebit > 0) + { + TUint32 w = *pW; + *pW++ = w & b; // mark all positions allocated + TUint allocated = __e32_bit_count_32(~w & ~b); + iAvail += allocated; // increase free count by number of positions already allocated + aLength -= allocated; + ebit -= 32; + b = (ebit >= 32)? 0 : 0xffffffff >> ebit; + } + return aLength; + } + + /** Copies a range from another allocator, mark remainder as occupied. Values of bit positions from aFirst to aFirst+aLen-1 inclusive in allocator diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/emul/win32/mutils.cpp --- a/kernel/eka/memmodel/emul/win32/mutils.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/emul/win32/mutils.cpp Tue May 11 17:28:22 2010 +0300 @@ -412,6 +412,12 @@ // Misc DPagingDevice methods +EXPORT_C NFastMutex* DPagingDevice::NotificationLock() + { + // use the system lock + return &TheScheduler.iLock; + } + EXPORT_C void DPagingDevice::NotifyIdle() { // Not used on this memory model diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/direct/mutils.cpp --- a/kernel/eka/memmodel/epoc/direct/mutils.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/direct/mutils.cpp Tue May 11 17:28:22 2010 +0300 @@ -673,6 +673,12 @@ // Misc DPagingDevice methods +EXPORT_C NFastMutex* DPagingDevice::NotificationLock() + { + // use the system lock + return &TheScheduler.iLock; + } + EXPORT_C void DPagingDevice::NotifyIdle() { // Not used on this memory model diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mcodepaging.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mcodepaging.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mcodepaging.cpp Tue May 11 17:28:22 2010 +0300 @@ -239,7 +239,8 @@ { TRACE2(("DCodePagedMemoryManager::ReadPage(0x%08x,0x%08x,0x%08x,?,?)",aMemory,aIndex,aCount)); - __NK_ASSERT_DEBUG(aRequest->CheckUse(aMemory,aIndex,aCount)); + __NK_ASSERT_DEBUG(aRequest->CheckUseContiguous(aMemory,aIndex,aCount)); + __ASSERT_CRITICAL; START_PAGING_BENCHMARK; @@ -256,6 +257,7 @@ TLinAddr linAddr = aRequest->MapPages(aIndex,aCount,aPages); TInt r = KErrNone; + TThreadMessage message; if(!info.iCodeSize) { @@ -292,7 +294,7 @@ device.iReadUnitShift, ReadFunc, (TAny*)info.iCodeLocalDrive, - (TAny*)&aRequest->iMessage); + (TAny*)&message); if(bufferStart<0) { @@ -332,7 +334,7 @@ pagedCodeInfo->AsyncClose(); - END_PAGING_BENCHMARK(EPagingBmReadCodePage); + END_PAGING_BENCHMARK_N(EPagingBmReadCodePage, aCount); return r; } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mdatapaging.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mdatapaging.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mdatapaging.cpp Tue May 11 17:28:22 2010 +0300 @@ -31,17 +31,26 @@ { public: - enum TSwapFlags + /// The state of swap for a logical page in a memory object. + /// + /// Note that this does not always correspond to the state of the page in RAM - for example a + /// page can be dirty in RAM but blank in swap if it has never been written out. + enum TSwapState { - EAllocated = 1 << 0, - EUninitialised = 1 << 1, - ESaved = 1 << 2, - ESwapFlagsMask = 0x7, - - ESwapIndexShift = 3, - ESwapIndexMask = 0xffffffff << ESwapIndexShift, + EStateUnreserved = 0, ///< swap space not yet reserved, or page is being decommitted + EStateBlank = 1, ///< swap page has never been written + EStateWritten = 2, ///< swap page has been written out at least once + EStateWriting = 3 ///< swap page is in the process of being written out + }; + + enum + { + ESwapIndexShift = 2, + ESwapStateMask = (1 << ESwapIndexShift) - 1, + ESwapIndexMask = 0xffffffff & ~ESwapStateMask }; +public: TInt Create(DPagingDevice* aDevice); TInt ReserveSwap(DMemoryObject* aMemory, TUint aStartIndex, TUint aPageCount); @@ -49,21 +58,32 @@ TBool IsReserved(DMemoryObject* aMemory, TUint aStartIndex, TUint aPageCount); TInt ReadSwapPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TLinAddr aLinAddr, DPageReadRequest* aRequest, TPhysAddr* aPhysAddrs); - TInt WriteSwapPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TLinAddr aLinAddr, DPageWriteRequest* aRequest); - void DoDeleteNotify(TUint aSwapData); + TInt WriteSwapPages(DMemoryObject** aMemory, TUint* aIndex, TUint aCount, TLinAddr aLinAddr, TBool aBackground); void GetSwapInfo(SVMSwapInfo& aInfoOut); TInt SetSwapThresholds(const SVMSwapThresholds& aThresholds); - void CheckSwapThresholds(TUint aInitial, TUint aFinal); + +private: + inline TSwapState SwapState(TUint aSwapData); + inline TInt SwapIndex(TUint aSwapData); + inline TUint SwapData(TSwapState aSwapState, TInt aSwapIndex); + + TInt AllocSwapIndex(TInt aCount); + void FreeSwapIndex(TInt aSwapIndex); + void CheckSwapThresholdsAndUnlock(TUint aInitial); -protected: - DPagingDevice* iDevice; - TBitMapAllocator* iBitMap; - TUint iBitMapFree; - TUint iAllocOffset; + void DoDeleteNotify(TUint aSwapIndex); + TInt DoWriteSwapPages(DMemoryObject** aMemory, TUint* aIndex, TUint aCount, TLinAddr aLinAddr, TInt aSwapIndex, TBool aBackground); + +private: + DPagingDevice* iDevice; ///< Paging device used to read and write swap pages + + NFastMutex iSwapLock; ///< Fast mutex protecting access to all members below + TUint iFreePageCount; ///< Number of swap pages that have not been reserved + TBitMapAllocator* iBitMap; ///< Bitmap of swap pages that have been allocated + TUint iAllocOffset; ///< Next offset to try when allocating a swap page TUint iSwapThesholdLow; TUint iSwapThesholdGood; - TThreadMessage iDelNotifyMsg; }; @@ -81,15 +101,14 @@ virtual TInt Alloc(DMemoryObject* aMemory, TUint aIndex, TUint aCount); virtual void Free(DMemoryObject* aMemory, TUint aIndex, TUint aCount); virtual TInt Wipe(DMemoryObject* aMemory); - virtual TInt CleanPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TPhysAddr*& aPageArrayEntry); + virtual void CleanPages(TUint aPageCount, SPageInfo** aPageInfos, TBool aBackground); // Methods inherited from DPagedMemoryManager virtual void Init3(); virtual TInt InstallPagingDevice(DPagingDevice* aDevice); virtual TInt AcquirePageReadRequest(DPageReadRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount); - virtual TInt AcquirePageWriteRequest(DPageWriteRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount); + virtual TInt AcquirePageWriteRequest(DPageWriteRequest*& aRequest, DMemoryObject** aMemory, TUint* aIndex, TUint aCount); virtual TInt ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest); - virtual TInt WritePages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageWriteRequest* aRequest); virtual TBool IsAllocated(DMemoryObject* aMemory, TUint aIndex, TUint aCount); public: @@ -97,6 +116,9 @@ TInt SetSwapThresholds(const SVMSwapThresholds& aThresholds); private: + TInt WritePages(DMemoryObject** aMemory, TUint* aIndex, TPhysAddr* aPages, TUint aCount, DPageWriteRequest *aRequest, TBool aAnyExecutable, TBool aBackground); + +private: /** The paging device used for accessing the backing store. This is set by #InstallPagingDevice. @@ -127,7 +149,7 @@ */ TInt DSwapManager::Create(DPagingDevice* aDevice) { - __ASSERT_COMPILE(!(ESwapIndexMask & ESwapFlagsMask)); + __ASSERT_COMPILE(!(ESwapIndexMask & ESwapStateMask)); __NK_ASSERT_DEBUG(iDevice == NULL); iDevice = aDevice; @@ -147,12 +169,88 @@ {// Not enough RAM to keep track of the swap. return KErrNoMemory; } - iBitMapFree = swapPages; + iFreePageCount = swapPages; iAllocOffset = 0; return KErrNone; } +inline DSwapManager::TSwapState DSwapManager::SwapState(TUint aSwapData) + { + TSwapState state = (TSwapState)(aSwapData & ESwapStateMask); + __NK_ASSERT_DEBUG(state >= EStateWritten || (aSwapData & ~ESwapStateMask) == 0); + return state; + } + + +inline TInt DSwapManager::SwapIndex(TUint aSwapData) + { + return aSwapData >> ESwapIndexShift; + } + + +inline TUint DSwapManager::SwapData(TSwapState aSwapState, TInt aSwapIndex) + { + return (aSwapIndex << ESwapIndexShift) | aSwapState; + } + + +/** +Allocate one or more page's worth of space within the swap area. + +The location is represented by a page-based index into the swap area. + +@param aCount The number of page's worth of space to allocate. + +@return The swap index of the first location allocated. +*/ +TInt DSwapManager::AllocSwapIndex(TInt aCount) + { + __NK_ASSERT_DEBUG(aCount > 0 && aCount <= KMaxPagesToClean); + NKern::FMWait(&iSwapLock); + + // search for run of aCount from iAllocOffset to end + TInt carry = 0; + TInt l = KMaxTInt; + TInt swapIndex = iBitMap->AllocAligned(aCount, 0, 0, EFalse, carry, l, iAllocOffset); + + // if search failed, retry from beginning + if (swapIndex < 0) + { + iAllocOffset = 0; + carry = 0; + swapIndex = iBitMap->AllocAligned(aCount, 0, 0, EFalse, carry, l, iAllocOffset); + } + + // if we found one then mark it as allocated and update iAllocOffset + if (swapIndex >= 0) + { + __NK_ASSERT_DEBUG(swapIndex <= (iBitMap->iSize - aCount)); + iBitMap->Alloc(swapIndex, aCount); + iAllocOffset = (swapIndex + aCount) % iBitMap->iSize; + } + + NKern::FMSignal(&iSwapLock); + __NK_ASSERT_DEBUG(swapIndex >= 0 || aCount > 1); // can't fail to allocate single page + return swapIndex; + } + + +/** +Free one page's worth of space within the swap area. + +The index must have been previously allocated with AllocSwapIndex(). +*/ +void DSwapManager::FreeSwapIndex(TInt aSwapIndex) + { + __NK_ASSERT_DEBUG(aSwapIndex >= 0 && aSwapIndex < iBitMap->iSize); + DoDeleteNotify(aSwapIndex); + NKern::FMWait(&iSwapLock); + iBitMap->Free(aSwapIndex); + NKern::FMSignal(&iSwapLock); + } + + /** Reserve some swap pages for the requested region of the memory object @@ -167,40 +265,29 @@ TInt DSwapManager::ReserveSwap(DMemoryObject* aMemory, TUint aStartIndex, TUint aPageCount) { __NK_ASSERT_DEBUG(MemoryObjectLock::IsHeld(aMemory)); - __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); - const TUint indexEnd = aStartIndex + aPageCount; - TUint index = aStartIndex; - -#ifdef _DEBUG - for (; index < indexEnd; index++) - {// This page shouldn't already be in use. - MmuLock::Lock(); - __NK_ASSERT_DEBUG(!(aMemory->PagingManagerData(index) & ESwapFlagsMask)); - MmuLock::Unlock(); - } -#endif - - if (iBitMapFree < aPageCount) + NKern::FMWait(&iSwapLock); + TUint initFree = iFreePageCount; + if (iFreePageCount < aPageCount) { + NKern::FMSignal(&iSwapLock); Kern::AsyncNotifyChanges(EChangesOutOfMemory); return KErrNoMemory; } - // Reserve the required swap space and mark each page as allocated and uninitialised. - TUint initFree = iBitMapFree; - iBitMapFree -= aPageCount; - for (index = aStartIndex; index < indexEnd; index++) + iFreePageCount -= aPageCount; + CheckSwapThresholdsAndUnlock(initFree); + + // Mark each page as allocated and uninitialised. + const TUint indexEnd = aStartIndex + aPageCount; + for (TUint index = aStartIndex; index < indexEnd; index++) { // Grab MmuLock to stop manager data being accessed. MmuLock::Lock(); - TUint swapData = aMemory->PagingManagerData(index); - __NK_ASSERT_DEBUG(!(swapData & EAllocated)); - swapData = EAllocated | EUninitialised; - aMemory->SetPagingManagerData(index, swapData); + __NK_ASSERT_DEBUG(SwapState(aMemory->PagingManagerData(index)) == EStateUnreserved); + aMemory->SetPagingManagerData(index, EStateBlank); MmuLock::Unlock(); } - CheckSwapThresholds(initFree, iBitMapFree); return KErrNone; } @@ -219,9 +306,7 @@ TInt DSwapManager::UnreserveSwap(DMemoryObject* aMemory, TUint aStartIndex, TUint aPageCount) { __NK_ASSERT_DEBUG(MemoryObjectLock::IsHeld(aMemory)); - __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); - TUint initFree = iBitMapFree; TUint freedPages = 0; const TUint indexEnd = aStartIndex + aPageCount; for (TUint index = aStartIndex; index < indexEnd; index++) @@ -229,30 +314,35 @@ // Grab MmuLock to stop manager data being accessed. MmuLock::Lock(); TUint swapData = aMemory->PagingManagerData(index); - TUint swapIndex = swapData >> ESwapIndexShift; - TBool notifyDelete = EFalse; - if (swapData & EAllocated) + TSwapState state = SwapState(swapData); + if (state != EStateUnreserved) { - if (swapData & ESaved) - { - notifyDelete = ETrue; - iBitMap->Free(swapIndex); - } freedPages++; - aMemory->SetPagingManagerData(index, 0); + aMemory->SetPagingManagerData(index, EStateUnreserved); } -#ifdef _DEBUG - else - __NK_ASSERT_DEBUG(swapData == 0); -#endif - MmuLock::Unlock(); - if (notifyDelete) - DoDeleteNotify(swapIndex); + if (state == EStateWritten) + FreeSwapIndex(SwapIndex(swapData)); + else if (state == EStateWriting) + { + // Wait for cleaning to finish before deallocating swap space + PageCleaningLock::Lock(); + PageCleaningLock::Unlock(); + +#ifdef _DEBUG + MmuLock::Lock(); + __NK_ASSERT_DEBUG(SwapState(aMemory->PagingManagerData(index)) == EStateUnreserved); + MmuLock::Unlock(); +#endif + } } - iBitMapFree += freedPages; - CheckSwapThresholds(initFree, iBitMapFree); + + NKern::FMWait(&iSwapLock); + TUint initFree = iFreePageCount; + iFreePageCount += freedPages; + CheckSwapThresholdsAndUnlock(initFree); + return freedPages; } @@ -275,7 +365,7 @@ const TUint indexEnd = aStartIndex + aPageCount; for (TUint index = aStartIndex; index < indexEnd; index++) { - if (!(aMemory->PagingManagerData(index) & DSwapManager::EAllocated)) + if (SwapState(aMemory->PagingManagerData(index)) == EStateUnreserved) {// This page is not allocated by swap manager. return EFalse; } @@ -296,15 +386,12 @@ */ TInt DSwapManager::ReadSwapPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TLinAddr aLinAddr, DPageReadRequest* aRequest, TPhysAddr* aPhysAddrs) { + __ASSERT_CRITICAL; + TInt r = KErrNone; const TUint readUnitShift = iDevice->iReadUnitShift; TUint readSize = KPageSize >> readUnitShift; - TThreadMessage* msg = const_cast(&aRequest->iMessage); - - // Determine the wipe byte values for uninitialised pages. - TUint allocFlags = aMemory->RamAllocFlags(); - TBool wipePages = !(allocFlags & Mmu::EAllocNoWipe); - TUint8 wipeByte = (allocFlags & Mmu::EAllocUseCustomWipeByte) ? (allocFlags >> Mmu::EAllocWipeByteShift) & 0xff : 0x03; + TThreadMessage message; const TUint indexEnd = aIndex + aCount; for (TUint index = aIndex; index < indexEnd; index++, aLinAddr += KPageSize, aPhysAddrs++) @@ -313,36 +400,43 @@ MmuLock::Lock(); // MmuLock required for atomic access to manager data. TUint swapData = aMemory->PagingManagerData(index); + TSwapState state = SwapState(swapData); - if (!(swapData & EAllocated)) + if (state == EStateUnreserved) {// This page is not committed to the memory object MmuLock::Unlock(); return KErrNotFound; } - if (swapData & EUninitialised) + else if (state == EStateBlank) {// This page has not been written to yet so don't read from swap // just wipe it if required. + TUint allocFlags = aMemory->RamAllocFlags(); MmuLock::Unlock(); + TBool wipePages = !(allocFlags & Mmu::EAllocNoWipe); if (wipePages) { + TUint8 wipeByte = (allocFlags & Mmu::EAllocUseCustomWipeByte) ? + (allocFlags >> Mmu::EAllocWipeByteShift) & 0xff : + 0x03; memset((TAny*)aLinAddr, wipeByte, KPageSize); } } else { - __NK_ASSERT_DEBUG(swapData & ESaved); - TUint swapIndex = swapData >> ESwapIndexShift; + // It is not possible to get here if the page is in state EStateWriting as if so it must + // be present in RAM, and so will not need to be read in. + __NK_ASSERT_DEBUG(state == EStateWritten); + // OK to release as if the object's data is decommitted the pager // will check that data is still valid before mapping it. MmuLock::Unlock(); - TUint readStart = (swapIndex << KPageShift) >> readUnitShift; + TUint readStart = (SwapIndex(swapData) << KPageShift) >> readUnitShift; START_PAGING_BENCHMARK; - r = iDevice->Read(msg, aLinAddr, readStart, readSize, DPagingDevice::EDriveDataPaging); + r = iDevice->Read(&message, aLinAddr, readStart, readSize, DPagingDevice::EDriveDataPaging); if (r != KErrNone) __KTRACE_OPT(KPANIC, Kern::Printf("DSwapManager::ReadSwapPages: error reading media at %08x + %x: %d", readStart << readUnitShift, readSize << readUnitShift, r)); __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocate memory, therefore can't fail with KErrNoMemory END_PAGING_BENCHMARK(EPagingBmReadDataMedia); - // TODO: Work out what to do if page in fails, unmap all pages???? __NK_ASSERT_ALWAYS(r == KErrNone); } END_PAGING_BENCHMARK(EPagingBmReadDataPage); @@ -359,102 +453,166 @@ @param aIndex The index within the memory object. @param aCount The number of pages to write out. @param aLinAddr The location of the pages to write out. -@param aRequest The demand paging request to use. +@param aBackground Whether this is being called in the background by the page cleaning thread + as opposed to on demand when a free page is required. +@pre Called with page cleaning lock held */ -TInt DSwapManager::WriteSwapPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TLinAddr aLinAddr, DPageWriteRequest* aRequest) - {// The RamAllocLock prevents the object's swap pages being reassigned. - __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); - - // Write the page out to the swap. - TInt r = KErrNone; - const TUint readUnitShift = iDevice->iReadUnitShift; - TUint writeSize = KPageSize >> readUnitShift; - TThreadMessage* msg = const_cast(&aRequest->iMessage); +TInt DSwapManager::WriteSwapPages(DMemoryObject** aMemory, TUint* aIndex, TUint aCount, TLinAddr aLinAddr, TBool aBackground) + { + __ASSERT_CRITICAL; // so we can pass the paging device a stack-allocated TThreadMessage + __NK_ASSERT_DEBUG(PageCleaningLock::IsHeld()); - const TUint indexEnd = aIndex + aCount; - for (TUint index = aIndex; index < indexEnd; index++) + START_PAGING_BENCHMARK; + + TUint i; + TUint swapData[KMaxPagesToClean + 1]; + + MmuLock::Lock(); + for (i = 0 ; i < aCount ; ++i) { - START_PAGING_BENCHMARK; + swapData[i] = aMemory[i]->PagingManagerData(aIndex[i]); + TSwapState s = SwapState(swapData[i]); + // It's not possible to write a page while it's already being written, because we always hold + // the PageCleaning mutex when we clean + __NK_ASSERT_DEBUG(s == EStateUnreserved || s == EStateBlank || s == EStateWritten); + if (s == EStateBlank || s == EStateWritten) + aMemory[i]->SetPagingManagerData(aIndex[i], SwapData(EStateWriting, 0)); + } + MmuLock::Unlock(); - MmuLock::Lock(); - TUint swapData = aMemory->PagingManagerData(index); - // OK to release as ram alloc lock prevents manager data being updated. - MmuLock::Unlock(); - if (!(swapData & EAllocated)) - {// This page is being decommited from aMemory so it is clean/unrequired. - continue; + // By the time we get here, some pages may have been decommitted, so write out only those runs + // of pages which are still committed. + + TInt r = KErrNone; + TInt startIndex = -1; + swapData[aCount] = SwapData(EStateUnreserved, 0); // end of list marker + for (i = 0 ; i < (aCount + 1) ; ++i) + { + if (SwapState(swapData[i]) != EStateUnreserved) + { + if (startIndex == -1) + startIndex = i; + + // Free swap page corresponding to old version of the pages we are going to write + if (SwapState(swapData[i]) == EStateWritten) + FreeSwapIndex(SwapIndex(swapData[i])); } - TInt swapIndex = swapData >> ESwapIndexShift; - if (swapData & ESaved) - {// An old version of this page has been saved to swap so free it now - // as it will be out of date. - iBitMap->Free(swapIndex); - DoDeleteNotify(swapIndex); - } - // Get a new swap location for this page. - swapIndex = iBitMap->AllocFrom(iAllocOffset); - __NK_ASSERT_DEBUG(swapIndex != -1 && swapIndex < iBitMap->iSize); - iAllocOffset = swapIndex + 1; - if (iAllocOffset == (TUint)iBitMap->iSize) - iAllocOffset = 0; + else + { + if (startIndex != -1) + { + // write pages from startIndex to i exclusive + TInt count = i - startIndex; + __NK_ASSERT_DEBUG(count > 0 && count <= KMaxPagesToClean); - TUint writeOffset = (swapIndex << KPageShift) >> readUnitShift; - { - START_PAGING_BENCHMARK; - r = iDevice->Write(msg, aLinAddr, writeOffset, writeSize, EFalse); - if (r != KErrNone) - __KTRACE_OPT(KPANIC, Kern::Printf("DSwapManager::WriteSwapPages: error writing media at %08x + %x: %d", writeOffset << readUnitShift, writeSize << readUnitShift, r)); - __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocate memory, therefore can't fail with KErrNoMemory - END_PAGING_BENCHMARK(EPagingBmWriteDataMedia); + // Get a new swap location for these pages, writing them all together if possible + TInt swapIndex = AllocSwapIndex(count); + if (swapIndex >= 0) + r = DoWriteSwapPages(&aMemory[startIndex], &aIndex[startIndex], count, aLinAddr + (startIndex << KPageShift), swapIndex, aBackground); + else + { + // Otherwise, write them individually + for (TUint j = startIndex ; j < i ; ++j) + { + swapIndex = AllocSwapIndex(1); + __NK_ASSERT_DEBUG(swapIndex >= 0); + r = DoWriteSwapPages(&aMemory[j], &aIndex[j], 1, aLinAddr + (j << KPageShift), swapIndex, aBackground); + if (r != KErrNone) + break; + } + } + + startIndex = -1; + } + } } - // TODO: Work out what to do if page out fails. - __NK_ASSERT_ALWAYS(r == KErrNone); - MmuLock::Lock(); - // The swap data should not have been modified. - __NK_ASSERT_DEBUG(swapData == aMemory->PagingManagerData(index)); - // Store the new swap location and mark the page as saved. - swapData &= ~(EUninitialised | ESwapIndexMask); - swapData |= (swapIndex << ESwapIndexShift) | ESaved; - aMemory->SetPagingManagerData(index, swapData); - MmuLock::Unlock(); - - END_PAGING_BENCHMARK(EPagingBmWriteDataPage); - } + + END_PAGING_BENCHMARK_N(EPagingBmWriteDataPage, aCount); return r; } +TInt DSwapManager::DoWriteSwapPages(DMemoryObject** aMemory, TUint* aIndex, TUint aCount, TLinAddr aLinAddr, TInt aSwapIndex, TBool aBackground) + { + + const TUint readUnitShift = iDevice->iReadUnitShift; + const TUint writeSize = aCount << (KPageShift - readUnitShift); + const TUint writeOffset = aSwapIndex << (KPageShift - readUnitShift); + + TThreadMessage msg; + START_PAGING_BENCHMARK; + TInt r = iDevice->Write(&msg, aLinAddr, writeOffset, writeSize, aBackground); + if (r != KErrNone) + { + __KTRACE_OPT(KPANIC, Kern::Printf("DSwapManager::WriteSwapPages: error writing media from %08x to %08x + %x: %d", aLinAddr, writeOffset << readUnitShift, writeSize << readUnitShift, r)); + } + __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocate memory, therefore can't fail with KErrNoMemory + __NK_ASSERT_ALWAYS(r == KErrNone); + END_PAGING_BENCHMARK(EPagingBmWriteDataMedia); + + TUint i; + TUint swapData[KMaxPagesToClean]; + + MmuLock::Lock(); + for (i = 0 ; i < aCount ; ++i) + { + // Re-check the swap state in case page was decommitted while we were writing + swapData[i] = aMemory[i]->PagingManagerData(aIndex[i]); + TSwapState s = SwapState(swapData[i]); + __NK_ASSERT_DEBUG(s == EStateUnreserved || s == EStateWriting); + if (s == EStateWriting) + { + // Store the new swap location and mark the page as saved. + aMemory[i]->SetPagingManagerData(aIndex[i], SwapData(EStateWritten, aSwapIndex + i)); + } + } + MmuLock::Unlock(); + + for (i = 0 ; i < aCount ; ++i) + { + TSwapState s = SwapState(swapData[i]); + if (s == EStateUnreserved) + { + // The page was decommitted while we were cleaning it, so free the swap page we + // allocated and continue, leaving this page in the unreserved state. + FreeSwapIndex(aSwapIndex + i); + } + } + + return KErrNone; + } + /** Notify the media driver that the page written to swap is no longer required. */ void DSwapManager::DoDeleteNotify(TUint aSwapIndex) { - // Ram Alloc lock prevents the swap location being assigned to another page. - __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); - + __ASSERT_CRITICAL; // so we can pass the paging device a stack-allocated TThreadMessage #ifdef __PAGING_DELETE_NOTIFY_ENABLED const TUint readUnitShift = iDevice->iReadUnitShift; const TUint size = KPageSize >> readUnitShift; TUint offset = (aSwapIndex << KPageShift) >> readUnitShift; + TThreadMessage msg; START_PAGING_BENCHMARK; // Ignore the return value as this is just an optimisation that is not supported on all media. - (void)iDevice->DeleteNotify(&iDelNotifyMsg, offset, size); + (void)iDevice->DeleteNotify(&msg, offset, size); END_PAGING_BENCHMARK(EPagingBmDeleteNotifyDataPage); #endif } // Check swap thresholds and notify (see K::CheckFreeMemoryLevel) -void DSwapManager::CheckSwapThresholds(TUint aInitial, TUint aFinal) +void DSwapManager::CheckSwapThresholdsAndUnlock(TUint aInitial) { TUint changes = 0; - if (aFinal < iSwapThesholdLow && aInitial >= iSwapThesholdLow) + if (iFreePageCount < iSwapThesholdLow && aInitial >= iSwapThesholdLow) changes |= (EChangesFreeMemory | EChangesLowMemory); - if (aFinal >= iSwapThesholdGood && aInitial < iSwapThesholdGood) + if (iFreePageCount >= iSwapThesholdGood && aInitial < iSwapThesholdGood) changes |= EChangesFreeMemory; + NKern::FMSignal(&iSwapLock); if (changes) Kern::AsyncNotifyChanges(changes); } @@ -462,23 +620,25 @@ void DSwapManager::GetSwapInfo(SVMSwapInfo& aInfoOut) { - __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); aInfoOut.iSwapSize = iBitMap->iSize << KPageShift; - aInfoOut.iSwapFree = iBitMapFree << KPageShift; + NKern::FMWait(&iSwapLock); + aInfoOut.iSwapFree = iFreePageCount << KPageShift; + NKern::FMSignal(&iSwapLock); } TInt DSwapManager::SetSwapThresholds(const SVMSwapThresholds& aThresholds) { - __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); if (aThresholds.iLowThreshold > aThresholds.iGoodThreshold) return KErrArgument; TInt low = (aThresholds.iLowThreshold + KPageSize - 1) >> KPageShift; TInt good = (aThresholds.iGoodThreshold + KPageSize - 1) >> KPageShift; if (good > iBitMap->iSize) return KErrArgument; + NKern::FMWait(&iSwapLock); iSwapThesholdLow = low; iSwapThesholdGood = good; + NKern::FMSignal(&iSwapLock); return KErrNone; } @@ -527,7 +687,7 @@ } -TInt DDataPagedMemoryManager::AcquirePageWriteRequest(DPageWriteRequest*& aRequest, DMemoryObject* aMemory, TUint aIndex, TUint aCount) +TInt DDataPagedMemoryManager::AcquirePageWriteRequest(DPageWriteRequest*& aRequest, DMemoryObject** aMemory, TUint* aIndex, TUint aCount) { aRequest = iDevice->iRequestPool->AcquirePageWriteRequest(aMemory,aIndex,aCount); return KErrNone; @@ -547,11 +707,7 @@ ReAllocDecommitted(aMemory,aIndex,aCount); // Reserve the swap pages required. - RamAllocLock::Lock(); - TInt r = iSwapManager->ReserveSwap(aMemory, aIndex, aCount); - RamAllocLock::Unlock(); - - return r; + return iSwapManager->ReserveSwap(aMemory, aIndex, aCount); } @@ -562,10 +718,8 @@ // Unreserve the swap pages associated with the memory object. Do this before // removing the page array entries to prevent a page fault reallocating these pages. - RamAllocLock::Lock(); TInt freed = iSwapManager->UnreserveSwap(aMemory, aIndex, aCount); (void)freed; - RamAllocLock::Unlock(); DoFree(aMemory,aIndex,aCount); } @@ -573,12 +727,16 @@ /** @copydoc DMemoryManager::Wipe -@todo Not yet implemented. - Need to handle this smartly, e.g. throw RAM away and set to uninitialised */ TInt DDataPagedMemoryManager::Wipe(DMemoryObject* aMemory) { - __NK_ASSERT_ALWAYS(0); // not implemented yet + // This is not implemented + // + // It's possible to implement this by throwing away all pages that are paged in and just setting + // the backing store state to EStateBlank, however there are currently no use cases which + // involve calling Wipe on paged memory. + + __NK_ASSERT_ALWAYS(0); return KErrNotSupported; } @@ -586,7 +744,7 @@ TInt DDataPagedMemoryManager::ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest) { - __NK_ASSERT_DEBUG(aRequest->CheckUse(aMemory,aIndex,aCount)); + __NK_ASSERT_DEBUG(aRequest->CheckUseContiguous(aMemory,aIndex,aCount)); // Map pages temporarily so that we can copy into them. const TLinAddr linAddr = aRequest->MapPages(aIndex, aCount, aPages); @@ -600,70 +758,78 @@ } -TInt DDataPagedMemoryManager::WritePages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageWriteRequest* aRequest) +TInt DDataPagedMemoryManager::WritePages(DMemoryObject** aMemory, TUint* aIndex, TPhysAddr* aPages, TUint aCount, DPageWriteRequest* aRequest, TBool aAnyExecutable, TBool aBackground) { - __NK_ASSERT_DEBUG(aRequest->CheckUse(aMemory,aIndex,aCount)); + // Map pages temporarily so that we can copy into them. + const TLinAddr linAddr = aRequest->MapPages(aIndex[0], aCount, aPages); - // Map pages temporarily so that we can copy into them. - const TLinAddr linAddr = aRequest->MapPages(aIndex, aCount, aPages); - - TInt r = iSwapManager->WriteSwapPages(aMemory, aIndex, aCount, linAddr, aRequest); + TInt r = iSwapManager->WriteSwapPages(aMemory, aIndex, aCount, linAddr, aBackground); // The memory object allows executable mappings then need IMB. - aRequest->UnmapPages(aMemory->IsExecutable()); + aRequest->UnmapPages(aAnyExecutable); return r; } -TInt DDataPagedMemoryManager::CleanPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TPhysAddr*& aPageArrayEntry) +void DDataPagedMemoryManager::CleanPages(TUint aPageCount, SPageInfo** aPageInfos, TBool aBackground) { - if(!aPageInfo->IsDirty()) - return KErrNone; - - // shouldn't be asked to clean a page which is writable... - __NK_ASSERT_DEBUG(!aPageInfo->IsWritable()); + __NK_ASSERT_DEBUG(PageCleaningLock::IsHeld()); + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(aPageCount <= (TUint)KMaxPagesToClean); + + TUint i; + DMemoryObject* memory[KMaxPagesToClean]; + TUint index[KMaxPagesToClean]; + TPhysAddr physAddr[KMaxPagesToClean]; + TBool anyExecutable = EFalse; + + for (i = 0 ; i < aPageCount ; ++i) + { + SPageInfo* pi = aPageInfos[i]; - // mark page as being modified by us... - TUint modifierInstance; // dummy variable used only for it's storage address on the stack - aPageInfo->SetModifier(&modifierInstance); + __NK_ASSERT_DEBUG(!pi->IsWritable()); + __NK_ASSERT_DEBUG(pi->IsDirty()); + + // mark page as being modified by us... + pi->SetModifier(&memory[0]); + + // get info about page... + memory[i] = pi->Owner(); + index[i] = pi->Index(); + physAddr[i] = pi->PhysAddr(); + anyExecutable = anyExecutable || memory[i]->IsExecutable(); + } - // get info about page... - TUint index = aPageInfo->Index(); - TPhysAddr physAddr = aPageInfo->PhysAddr(); - - // Release the mmu lock while we write out the page. This is safe as the - // RamAllocLock stops the physical address being freed from this object. MmuLock::Unlock(); // get paging request object... DPageWriteRequest* req; - TInt r = AcquirePageWriteRequest(req, aMemory, index, 1); - __NK_ASSERT_DEBUG(r==KErrNone); // we should always get a write request because the previous function blocks until it gets one - __NK_ASSERT_DEBUG(req); // we should always get a write request because the previous function blocks until it gets one - - r = WritePages(aMemory, index, 1, &physAddr, req); + TInt r = AcquirePageWriteRequest(req, memory, index, aPageCount); + __NK_ASSERT_DEBUG(r==KErrNone && req); + + r = WritePages(memory, index, physAddr, aPageCount, req, anyExecutable, aBackground); + __NK_ASSERT_DEBUG(r == KErrNone); // this should never return an error req->Release(); MmuLock::Lock(); - if(r!=KErrNone) - return r; - - // check if page is clean... - if(aPageInfo->CheckModified(&modifierInstance) || aPageInfo->IsWritable()) + for (i = 0 ; i < aPageCount ; ++i) { - // someone else modified the page, or it became writable, so fail... - r = KErrInUse; + SPageInfo* pi = aPageInfos[i]; + // check if page is clean... + if(pi->CheckModified(&memory[0]) || pi->IsWritable()) + { + // someone else modified the page, or it became writable, so mark as not cleaned + aPageInfos[i] = NULL; + } + else + { + // page is now clean! + ThePager.SetClean(*pi); + } } - else - { - // page is now clean! - ThePager.SetClean(*aPageInfo); - } - - return r; } @@ -680,22 +846,13 @@ void DDataPagedMemoryManager::GetSwapInfo(SVMSwapInfo& aInfoOut) { - NKern::ThreadEnterCS(); - RamAllocLock::Lock(); iSwapManager->GetSwapInfo(aInfoOut); - RamAllocLock::Unlock(); - NKern::ThreadLeaveCS(); } TInt DDataPagedMemoryManager::SetSwapThresholds(const SVMSwapThresholds& aThresholds) { - NKern::ThreadEnterCS(); - RamAllocLock::Lock(); - TInt r = iSwapManager->SetSwapThresholds(aThresholds); - RamAllocLock::Unlock(); - NKern::ThreadLeaveCS(); - return r; + return iSwapManager->SetSwapThresholds(aThresholds); } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mdefrag.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mdefrag.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mdefrag.cpp Tue May 11 17:28:22 2010 +0300 @@ -124,7 +124,8 @@ TInt M::MovePage(TPhysAddr aOld, TPhysAddr& aNew, TUint aBlockZoneId, TBool aBlockRest) { - TInt r; + // Returns this when page is not paged or managed or free but is a real RAM page. + TInt r = KErrNotSupported; // get memory object corresponding to the page... DMemoryObject* memory = 0; @@ -136,30 +137,76 @@ {// The page is paged so let the pager handle it. return ThePager.DiscardPage(pi, aBlockZoneId, aBlockRest); } - if (pi->Type()==SPageInfo::EManaged) - memory = pi->Owner(); - } - MmuLock::Unlock(); - - // Note, whilst we hold the RamAllocLock the page can't change it's use - // and we can safely assume that it still belongs to the memory object - // at a fixed page index. - // Also, as memory objects can't be destroyed whilst they still own pages - // we can safely access this object without taking an explicit referernce, - // i.e. we don't need to Open() the memory object. - if (!pi) - {// page info for aOld not found so aOld is not a RAM page... - r = KErrArgument; - } - else if(!memory) - { - // page does not have a memory manager, so we can't move it... - r = KErrNotSupported; + switch (pi->Type()) + { + case SPageInfo::EManaged: + memory = pi->Owner(); + // Note, whilst we hold the RamAllocLock the page can't change it's use + // and we can safely assume that it still belongs to the memory object + // at a fixed page index. + // Also, as memory objects can't be destroyed whilst they still own pages + // we can safely access this object without taking an explicit reference, + // i.e. we don't need to Open() the memory object. + MmuLock::Unlock(); + // move page... + r = memory->iManager->MovePage(memory, pi, aNew, aBlockZoneId, aBlockRest); + break; + case SPageInfo::EUnused: + r = KErrNotFound; // This page is free so nothing to do. + // Fall through.. + default: + MmuLock::Unlock(); + } } else - { - // move page... - r = memory->iManager->MovePage(memory, pi, aNew, aBlockZoneId, aBlockRest); + {// page info for aOld not found so aOld is not a RAM page... + MmuLock::Unlock(); + r = KErrArgument; } return r; } + + +TInt M::MoveAndAllocPage(TPhysAddr aAddr, TZonePageType aPageType) + { + // Returns this when page is not paged or managed or free but is a real RAM page. + TInt r = KErrNotSupported; + + // get memory object corresponding to the page... + DMemoryObject* memory = 0; + MmuLock::Lock(); + SPageInfo* pi = SPageInfo::SafeFromPhysAddr(aAddr & ~KPageMask); + if(pi) + { + if (pi->PagedState() != SPageInfo::EUnpaged) + {// The page is paged so let the pager handle it. + return ThePager.DiscardAndAllocPage(pi, aPageType); + } + switch (pi->Type()) + { + case SPageInfo::EManaged: + memory = pi->Owner(); + // Note, whilst we hold the RamAllocLock the page can't change it's use + // and we can safely assume that it still belongs to the memory object + // at a fixed page index. + // Also, as memory objects can't be destroyed whilst they still own pages + // we can safely access this object without taking an explicit referernce, + // i.e. we don't need to Open() the memory object. + MmuLock::Unlock(); + // move page... + r = memory->iManager->MoveAndAllocPage(memory, pi, aPageType); + break; + case SPageInfo::EUnused: + r = KErrNone; // This page is free so nothing to do. + // Fall through.. + default: + MmuLock::Unlock(); + } + } + else + {// page info for aAddr not found so aAddr is not a RAM page... + MmuLock::Unlock(); + r = KErrArgument; + } + return r; + } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mexport.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mexport.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mexport.cpp Tue May 11 17:28:22 2010 +0300 @@ -839,11 +839,3 @@ } - -EXPORT_C void DPagingDevice::NotifyIdle() - { - } - -EXPORT_C void DPagingDevice::NotifyBusy() - { - } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mmanager.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mmanager.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mmanager.cpp Tue May 11 17:28:22 2010 +0300 @@ -108,12 +108,10 @@ } -TInt DMemoryManager::CleanPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TPhysAddr*& /*aPageArrayEntry*/) +void DMemoryManager::CleanPages(TUint aPageCount, SPageInfo** aPageInfos, TBool /*aBackground*/) { - if(aPageInfo->IsDirty()==false) - return KErrNone; - __NK_ASSERT_DEBUG(0); - return KErrNotSupported; + for (TUint i = 0 ; i < aPageCount ; ++i) + __NK_ASSERT_DEBUG(!aPageInfos[i]->IsDirty()); } @@ -136,6 +134,13 @@ return KErrNotSupported; } + +TInt DMemoryManager::MoveAndAllocPage(DMemoryObject*, SPageInfo*, TZonePageType) + { + return KErrNotSupported; + } + + TZonePageType DMemoryManager::PageType() {// This should not be invoked on memory managers that do not use the methods // AllocPages() and FreePages(). @@ -723,6 +728,7 @@ public: // from DMemoryManager... virtual TInt MovePage(DMemoryObject* aMemory, SPageInfo* aOldPageInfo, TPhysAddr& aNewPage, TUint aBlockZoneId, TBool aBlockRest); + virtual TInt MoveAndAllocPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TZonePageType aPageType); virtual TInt HandleFault( DMemoryObject* aMemory, TUint aIndex, DMemoryMapping* aMapping, TUint aMapInstanceCount, TUint aAccessPermissions); virtual TZonePageType PageType(); @@ -889,6 +895,18 @@ } +TInt DMovableMemoryManager::MoveAndAllocPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TZonePageType aPageType) + { + TPhysAddr newPage; + TInt r = MovePage(aMemory, aPageInfo, newPage, KRamZoneInvalidId, EFalse); + if (r == KErrNone) + { + TheMmu.MarkPageAllocated(aPageInfo->PhysAddr(), aPageType); + } + return r; + } + + TInt DMovableMemoryManager::HandleFault(DMemoryObject* aMemory, TUint aIndex, DMemoryMapping* aMapping, TUint aMapInstanceCount, TUint aAccessPermissions) { @@ -1039,6 +1057,9 @@ __NK_ASSERT_DEBUG(MmuLock::IsHeld()); __UNLOCK_GUARD_START(MmuLock); + // must always hold the PageCleaningLock if the page needs to be cleaned + __NK_ASSERT_DEBUG(!aPageInfo->IsDirty() || PageCleaningLock::IsHeld()); + TUint index = aPageInfo->Index(); TInt r; @@ -1096,10 +1117,11 @@ // page successfully unmapped... aPageInfo->SetReadOnly(); // page not mapped, so must be read-only - // if the page can be made clean... - r = aMemory->iManager->CleanPage(aMemory,aPageInfo,p); + // attempt to clean the page if it is dirty... + if (aPageInfo->IsDirty()) + aMemory->iManager->CleanPages(1, &aPageInfo, EFalse); - if(r==KErrNone) + if(aPageInfo) { // page successfully stolen... __NK_ASSERT_DEBUG((*p^page)<(TUint)KPageSize); // sanity check, page should still be allocated to us @@ -1111,13 +1133,10 @@ __NK_ASSERT_ALWAYS((pagerInfo&(RPageArray::EFlagsMask|RPageArray::EStateMask)) == RPageArray::ENotPresent); TheMmu.PageFreed(aPageInfo); + r = KErrNone; } else - { - // only legitimate reason for failing the clean is if the page state was changed - // by a page fault or by pinning, this should return KErrInUse... - __NK_ASSERT_DEBUG(r==KErrInUse); - } + r = KErrInUse; } } @@ -2030,14 +2049,6 @@ __NK_ASSERT_ALWAYS(0); return KErrNotSupported; } - - -TInt DPagedMemoryManager::WritePages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageWriteRequest* aRequest) - { - __NK_ASSERT_ALWAYS(0); - return KErrNotSupported; - } - TZonePageType DPagedMemoryManager::PageType() {// Paged manager's pages should be discardable and will actaully be freed by // the pager so this value won't be used. diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mmanager.h --- a/kernel/eka/memmodel/epoc/flexible/mmu/mmanager.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mmanager.h Tue May 11 17:28:22 2010 +0300 @@ -242,6 +242,7 @@ otherwise one of the system wide error codes. @pre RamAllocLock held. + @pre If the page is dirty the PageCleaning lock must be held. @pre MmuLock held. */ virtual TInt StealPage(DMemoryObject* aMemory, SPageInfo* aPageInfo); @@ -265,31 +266,31 @@ virtual TInt RestrictPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TRestrictPagesType aRestriction); /** - Clean a page of RAM by saving any modifications to it out to backing store. + Clean multiple pages of RAM by saving any modifications to it out to backing store. - This function must only be called when there are no writable MMU mappings of the page. + This function must only be called when there are no writable MMU mappings of the pages. - The function must only return a success after determining no writable MMU mappings - were created for the page, in which case it should also mark the page as clean using - SPageInfo::SetClean. - - This is only intended for use by #StealPage. + The function takes an array of SPageInfo pointers indicating the pages to clean. On return, the + elements of this array are unchanged if the page was successfully cleaned, or set to NULL if + cleaning was abandoned (by the page being written to, for example). - @param aMemory A memory object associated with this manager. - @param aPageInfo The page information structure of the page to be cleaned. - This must be owned by \a aMemory. - @param aPageArrayEntry Reference to the page's page array entry in \a aMemory->iPages. + The pages passed must be sequential in their page colour (index & KPageColourMask). + + Those pages that are successfully cleaned are marked as clean using SPageInfo::SetClean. - @return KErrNone if successful, - KErrInUse if the page state changed, e.g. became pinned or was subject to a page fault making it writable, - KErrNotSupported if the manager doesn't support this function, - otherwise one of the system wide error codes. + This is intended for use by #StealPage and #CleanSomePages. + + @param aPageCount Number of pages to clean. + @param aPageInfos Pointer to an array of aPageCount page info pointers. + @param aBackground Whether the activity should be ignored when determining whether the + paging device is busy. @pre MmuLock held + @pre PageCleaning lock held @pre The memory page must not have any writeable MMU mappings. @post MmuLock held (but may have been released by this function) */ - virtual TInt CleanPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TPhysAddr*& aPageArrayEntry); + virtual void CleanPages(TUint aPageCount, SPageInfo** aPageInfos, TBool aBackground); /** Process a page fault in memory associated with this manager. @@ -358,11 +359,37 @@ virtual void Unpin(DMemoryObject* aMemory, DMemoryMappingBase* aMapping, TPinArgs& aPinArgs) =0; /** - @todo + Attempt to move the page specified to a new physical location. The new physical + location for the page to be moved to is allocated by this method. However, + aBlockZoneId and aBlockRest can be used to control which RAM zone the new + location is in. + + @param aMemory The memory object that owns the page. + @param aOldPageInfo The page info for the physical page to move. + @param aNewPage On success this will hold the physical address of the new + location for the page. + @param aBlockZoneId The ID of a RAM zone not to allocate the new page into. + @param aBlockRest When set to ETrue the search for a new page will stop if it + ever needs to look at aBlockZoneId. + @return KErrNone on success, KErrInUse if the page couldn't be moved, + or KErrNoMemory if it wasn't possible to allocate a new page. */ virtual TInt MovePage(DMemoryObject* aMemory, SPageInfo* aOldPageInfo, TPhysAddr& aNewPage, TUint aBlockZoneId, TBool aBlockRest); /** + Move the page specified to a new physical location and mark the page as + allocated as type aPageType. + + @param aMemory The memory object that owns the page. + @param aPageInfo The page info for the physical page to move. + @param aPageType The type of the page to allocate into the orignal physical + location of the page to move. + @return KErrNone on success, KErrInUse if the page couldn't be moved, + or KErrNoMemory if it wasn't possible to allocate a new page. + */ + virtual TInt MoveAndAllocPage(DMemoryObject* aMemory, SPageInfo* aPageInfo, TZonePageType aPageType); + + /** Return the TZonePageType of the pages that the memory manager can allocate and free. */ virtual TZonePageType PageType(); @@ -567,25 +594,6 @@ virtual TInt ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest) =0; /** - Save the data content of a specified region of a memory object by writing it to - storage media. This is intended for use by an implementation of #CleanPage. - - The memory region must be the same as, or a subset of, the region used when obtaining - the request object \a aRequest. - - @param aMemory A memory object associated with this manager. - @param aIndex Page index for the start of the region. - @param aCount Number of pages in the region. - @param aPages Pointer to array of pages to read into. This must contain \a aCount - number of physical page addresses which are each page aligned. - @param aRequest A request object previously obtained with #AcquirePageWriteRequest. - - @return KErrNone if successful, - otherwise one of the system wide error codes. - */ - virtual TInt WritePages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageWriteRequest* aRequest); - - /** Check if a region of a memory object has been allocated. E.g. that #Alloc has reserved backing store for the memory and this has has not yet been freed by #Free or #Destruct. diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mmu.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mmu.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mmu.cpp Tue May 11 17:28:22 2010 +0300 @@ -622,7 +622,7 @@ __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam(?,%d,%d,?,%d)", aZoneIdCount, aBytes, aPhysAddr, aAlign)); __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); - TInt r = iRamPageAllocator->ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aBytes, aPhysAddr, EPageFixed, aAlign); + TInt r = iRamPageAllocator->ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aBytes, aPhysAddr, aAlign); if(r!=KErrNone) iRamAllocFailed = ETrue; else @@ -871,6 +871,22 @@ } +/** +Mark a page as being allocated to a particular page type. + +NOTE - This page should not be used until PagesAllocated() has been invoked on it. + +@param aPhysAddr The physical address of the page to mark as allocated. +@param aZonePageType The type of the page to mark as allocated. +*/ +void Mmu::MarkPageAllocated(TPhysAddr aPhysAddr, TZonePageType aZonePageType) + { + __KTRACE_OPT(KMMU,Kern::Printf("Mmu::MarkPageAllocated(0x%x, %d)", aPhysAddr, aZonePageType)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); + iRamPageAllocator->MarkPageAllocated(aPhysAddr, aZonePageType); + } + + void Mmu::FreeRam(TPhysAddr* aPages, TUint aCount, TZonePageType aZonePageType) { __KTRACE_OPT(KMMU,Kern::Printf("Mmu::FreeRam(?,%d)",aCount)); @@ -936,15 +952,7 @@ // Only the pager sets EAllocNoPagerReclaim and it shouldn't allocate contiguous ram. __NK_ASSERT_DEBUG(!(aFlags&EAllocNoPagerReclaim)); #endif - TInt r = iRamPageAllocator->AllocContiguousRam(aCount, aPhysAddr, EPageFixed, aAlign+KPageShift); - if(r==KErrNoMemory && aCount > KMaxFreeableContiguousPages) - { - // flush paging cache and retry... - RamAllocLock::Unlock(); - ThePager.FlushAll(); - RamAllocLock::Lock(); - r = iRamPageAllocator->AllocContiguousRam(aCount, aPhysAddr, EPageFixed, aAlign+KPageShift); - } + TInt r = iRamPageAllocator->AllocContiguousRam(aCount, aPhysAddr, aAlign+KPageShift); if(r!=KErrNone) iRamAllocFailed = ETrue; else diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mmu.h --- a/kernel/eka/memmodel/epoc/flexible/mmu/mmu.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mmu.h Tue May 11 17:28:22 2010 +0300 @@ -21,8 +21,6 @@ #ifndef __MMU_H__ #define __MMU_H__ -#define _USE_OLDEST_LISTS - #include "mm.h" #include "mmboot.h" #include @@ -146,7 +144,7 @@ enum TPagedState { /** - Page is not being managed for demand paging purposes, is has been transiently + Page is not being managed for demand paging purposes, or has been transiently removed from the demand paging live list. */ EUnpaged = 0x0, @@ -172,7 +170,6 @@ // NOTE - This must be the same value as EStatePagedLocked as defined in mmubase.h EPagedPinned = 0x4, -#ifdef _USE_OLDEST_LISTS /** Page is in the live list as one of oldest pages that is clean. */ @@ -181,8 +178,7 @@ /** Page is in the live list as one of oldest pages that is dirty. */ - EPagedOldestDirty = 0x6 -#endif + EPagedOldestDirty = 0x6 }; @@ -678,7 +674,7 @@ /** Flag this page as 'dirty', indicating that its contents may no longer match those saved - to a backing store. This sets the flag #EWritable. + to a backing store. This sets the flag #EDirty. This is used in the management of demand paged memory. @@ -687,12 +683,13 @@ FORCE_INLINE void SetDirty() { CheckAccess("SetDirty"); + __NK_ASSERT_DEBUG(IsWritable()); iFlags |= EDirty; } /** Flag this page as 'clean', indicating that its contents now match those saved - to a backing store. This clears the flag #EWritable. + to a backing store. This clears the flag #EDirty. This is used in the management of demand paged memory. @@ -701,6 +698,7 @@ FORCE_INLINE void SetClean() { CheckAccess("SetClean"); + __NK_ASSERT_DEBUG(!IsWritable()); iFlags &= ~EDirty; } @@ -1751,11 +1749,12 @@ #endif } -private: +public: /** The lock */ static NFastMutex iLock; #ifdef _DEBUG +private: static TUint UnlockGuardNest; static TUint UnlockGuardFail; #endif @@ -1963,6 +1962,7 @@ TInt AllocRam( TPhysAddr* aPages, TUint aCount, TRamAllocFlags aFlags, TZonePageType aZonePageType, TUint aBlockZoneId=KRamZoneInvalidId, TBool aBlockRest=EFalse); + void MarkPageAllocated(TPhysAddr aPhysAddr, TZonePageType aZonePageType); void FreeRam(TPhysAddr* aPages, TUint aCount, TZonePageType aZonePageType); TInt AllocContiguousRam(TPhysAddr& aPhysAddr, TUint aCount, TUint aAlign, TRamAllocFlags aFlags); void FreeContiguousRam(TPhysAddr aPhysAddr, TUint aCount); diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mpagecleaner.cpp --- /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 +#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; + } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mpagecleaner.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpagecleaner.h Tue May 11 17:28:22 2010 +0300 @@ -0,0 +1,39 @@ +// 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: +// +// Handles pre-cleaning of dirty pages. +// +// When the paging device is idle (as determined by it calling NotifyIdle/NotifyBusy), a thread +// writes cleans dirty pages in the oldest section of the live list. +// + +/** + @file + @internalComponent +*/ + +#ifndef MPAGECLEANER_H +#define MPAGECLEANER_H + +#include +#include + +class PageCleaner + { +public: + static void Start(); + static void NotifyPagesToClean(); + }; + +#endif diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mpager.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mpager.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpager.cpp Tue May 11 17:28:22 2010 +0300 @@ -27,14 +27,14 @@ #include "mpagearray.h" #include "mswap.h" #include "mthrash.h" +#include "mpagecleaner.h" + #include "cache_maintenance.inl" const TUint16 KDefaultYoungOldRatio = 3; const TUint16 KDefaultMinPages = 256; -#ifdef _USE_OLDEST_LISTS const TUint16 KDefaultOldOldestRatio = 3; -#endif const TUint KMinOldPages = 1; @@ -43,18 +43,24 @@ */ const TUint KAbsoluteMaxPageCount = (1u<<(32-KPageShift))-1u; - +/* +Limit the maximum number of oldest pages to bound the time taken by SelectPagesToClean(), which is +called with the MmuLock held. +*/ +const TUint KMaxOldestPages = 32; + +static DMutex* ThePageCleaningLock = NULL; DPager ThePager; DPager::DPager() : iMinimumPageCount(0), iMaximumPageCount(0), iYoungOldRatio(0), - iYoungCount(0),iOldCount(0), -#ifdef _USE_OLDEST_LISTS - iOldestCleanCount(0), -#endif + iYoungCount(0), iOldCount(0), iOldestCleanCount(0), iNumberOfFreePages(0), iReservePageCount(0), iMinimumPageLimit(0) +#ifdef __DEMAND_PAGING_BENCHMARKS__ + , iBenchmarkLock(TSpinLock::EOrderGenericIrqHigh3) +#endif { } @@ -102,13 +108,8 @@ #ifdef __SMP__ // Adjust min page count so that all CPUs are guaranteed to make progress. - // NOTE: Can't use NKern::NumberOfCpus here because we haven't finished booting yet and will - // always have only one CPU running at this point... - - // TODO: Before we can enable this the base test configuration needs - // updating to have a sufficient minimum page size... - // - // iMinYoungPages *= KMaxCpus; + TInt numberOfCpus = NKern::NumberOfCpus(); + iMinYoungPages *= numberOfCpus; #endif // A minimum young/old ratio of 1 means that we need at least twice iMinYoungPages pages... @@ -123,11 +124,9 @@ iYoungOldRatio = KDefaultYoungOldRatio; if(config.iYoungOldRatio) iYoungOldRatio = config.iYoungOldRatio; -#ifdef _USE_OLDEST_LISTS iOldOldestRatio = KDefaultOldOldestRatio; if(config.iSpare[2]) iOldOldestRatio = config.iSpare[2]; -#endif // Set the minimum page counts... iMinimumPageLimit = iMinYoungPages * (1 + iYoungOldRatio) / iYoungOldRatio @@ -161,7 +160,6 @@ iMaximumPageCount = KAbsoluteMaxPageCount; iInitMaximumPageCount = iMaximumPageCount; - TRACEB(("DPager::InitCache() live list min=%d max=%d ratio=%d",iMinimumPageCount,iMaximumPageCount,iYoungOldRatio)); // Verify the page counts are valid. @@ -179,11 +177,9 @@ TInt ratioLimit = (iMinimumPageCount-KMinOldPages)/KMinOldPages; __NK_ASSERT_ALWAYS(iYoungOldRatio <= ratioLimit); -#ifdef _USE_OLDEST_LISTS // There should always be enough old pages to allow the oldest lists ratio. TUint oldestCount = minOldAndOldest / (1 + iOldOldestRatio); __NK_ASSERT_ALWAYS(oldestCount); -#endif iNumberOfFreePages = 0; iNumberOfDirtyPages = 0; @@ -193,13 +189,9 @@ // old list so don't allocate them again. RamAllocLock::Lock(); iYoungCount = 0; -#ifdef _USE_OLDEST_LISTS iOldCount = 0; iOldestDirtyCount = 0; __NK_ASSERT_DEBUG(iOldestCleanCount == iReservePageCount); -#else - __NK_ASSERT_DEBUG(iOldCount == iReservePageCount); -#endif Mmu& m = TheMmu; for(TUint i = iReservePageCount; i < iMinimumPageCount; i++) { @@ -216,11 +208,7 @@ RamAllocLock::Unlock(); __NK_ASSERT_DEBUG(CacheInitialised()); -#ifdef _USE_OLDEST_LISTS TRACEB(("DPager::InitCache() end with young=%d old=%d oldClean=%d oldDirty=%d min=%d free=%d max=%d",iYoungCount,iOldCount,iOldestCleanCount,iOldestDirtyCount,iMinimumPageCount,iNumberOfFreePages,iMaximumPageCount)); -#else - TRACEB(("DPager::InitCache() end with young=%d old=%d min=%d free=%d max=%d",iYoungCount,iOldCount,iMinimumPageCount,iNumberOfFreePages,iMaximumPageCount)); -#endif } @@ -250,17 +238,12 @@ return EFalse; if (!CheckList(&iYoungList.iA, iYoungCount)) return EFalse; - -#ifdef _USE_OLDEST_LISTS if (!CheckList(&iOldestCleanList.iA, iOldestCleanCount)) return EFalse; if (!CheckList(&iOldestDirtyList.iA, iOldestDirtyCount)) return EFalse; TRACEP(("DP: y=%d o=%d oc=%d od=%d f=%d", iYoungCount, iOldCount, iOldestCleanCount, iOldestDirtyCount, iNumberOfFreePages)); -#else - TRACEP(("DP: y=%d o=%d f=%d", iYoungCount, iOldCount, iNumberOfFreePages)); -#endif //#ifdef _USE_OLDEST_LISTS TraceCounts(); #endif // #ifdef FMM_PAGER_CHECK_LISTS return true; @@ -268,16 +251,10 @@ void DPager::TraceCounts() { -#ifdef _USE_OLDEST_LISTS TRACEP(("DP: y=%d o=%d oc=%d od=%d f=%d min=%d max=%d ml=%d res=%d", iYoungCount, iOldCount, iOldestCleanCount, iOldestDirtyCount, iNumberOfFreePages, iMinimumPageCount, iMaximumPageCount, iMinimumPageLimit, iReservePageCount)); -#else - TRACEP(("DP: y=%d o=%d f=%d min=%d max=%d ml=%d res=%d", - iYoungCount, iOldCount, iNumberOfFreePages, iMinimumPageCount, - iMaximumPageCount, iMinimumPageLimit, iReservePageCount)); -#endif //#ifdef _USE_OLDEST_LISTS } #endif //#ifdef _DEBUG @@ -320,15 +297,9 @@ __NK_ASSERT_DEBUG(aPageInfo->PagedState()==SPageInfo::EUnpaged); // add as oldest page... -#ifdef _USE_OLDEST_LISTS aPageInfo->SetPagedState(SPageInfo::EPagedOldestClean); iOldestCleanList.Add(&aPageInfo->iLink); ++iOldestCleanCount; -#else - aPageInfo->SetPagedState(SPageInfo::EPagedOld); - iOldList.Add(&aPageInfo->iLink); - ++iOldCount; -#endif Event(EEventPageInFree,aPageInfo); } @@ -357,7 +328,6 @@ --iOldCount; break; -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: __NK_ASSERT_DEBUG(iOldestCleanCount); aPageInfo->iLink.Deque(); @@ -369,7 +339,6 @@ aPageInfo->iLink.Deque(); --iOldestDirtyCount; break; -#endif case SPageInfo::EPagedPinned: // this can occur if a pinned mapping is being unmapped when memory is decommitted. @@ -392,7 +361,10 @@ // Update the dirty page count as required... if (aPageInfo->IsDirty()) + { + aPageInfo->SetReadOnly(); SetClean(*aPageInfo); + } if (iNumberOfFreePages > 0) {// The paging cache is not at the minimum size so safe to let the @@ -403,15 +375,9 @@ } // Need to hold onto this page as have reached the page cache limit. // add as oldest page... -#ifdef _USE_OLDEST_LISTS aPageInfo->SetPagedState(SPageInfo::EPagedOldestClean); iOldestCleanList.Add(&aPageInfo->iLink); ++iOldestCleanCount; -#else - aPageInfo->SetPagedState(SPageInfo::EPagedOld); - iOldList.Add(&aPageInfo->iLink); - ++iOldCount; -#endif return KErrNone; } @@ -438,7 +404,6 @@ --iOldCount; break; -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: __NK_ASSERT_DEBUG(iOldestCleanCount); aPageInfo->iLink.Deque(); @@ -450,7 +415,6 @@ aPageInfo->iLink.Deque(); --iOldestDirtyCount; break; -#endif case SPageInfo::EPagedPinned: __NK_ASSERT_DEBUG(0); @@ -521,52 +485,253 @@ } -SPageInfo* DPager::StealOldestPage() +TInt DPager::TryStealOldestPage(SPageInfo*& aPageInfoOut) { __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + // find oldest page in list... + SDblQueLink* link; + if (iOldestCleanCount) + { + __NK_ASSERT_DEBUG(!iOldestCleanList.IsEmpty()); + link = iOldestCleanList.Last(); + } + else if (iOldestDirtyCount) + { + __NK_ASSERT_DEBUG(!iOldestDirtyList.IsEmpty()); + link = iOldestDirtyList.Last(); + } + else if (iOldCount) + { + __NK_ASSERT_DEBUG(!iOldList.IsEmpty()); + link = iOldList.Last(); + } + else + { + __NK_ASSERT_DEBUG(iYoungCount); + __NK_ASSERT_ALWAYS(!iYoungList.IsEmpty()); + link = iYoungList.Last(); + } + SPageInfo* pageInfo = SPageInfo::FromLink(link); + + if (pageInfo->IsDirty() && !PageCleaningLock::IsHeld()) + return 1; + + // try to steal it from owning object... + TInt r = StealPage(pageInfo); + if (r == KErrNone) + { + BalanceAges(); + aPageInfoOut = pageInfo; + } + + return r; + } + + +SPageInfo* DPager::StealOldestPage() + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + TBool pageCleaningLockHeld = EFalse; for(;;) { - // find oldest page in list... - SDblQueLink* link; -#ifdef _USE_OLDEST_LISTS - if (iOldestCleanCount) + SPageInfo* pageInfo = NULL; + TInt r = TryStealOldestPage(pageInfo); + + if (r == KErrNone) { - __NK_ASSERT_DEBUG(!iOldestCleanList.IsEmpty()); - link = iOldestCleanList.Last(); + if (pageCleaningLockHeld) + { + MmuLock::Unlock(); + PageCleaningLock::Unlock(); + MmuLock::Lock(); + } + return pageInfo; + } + else if (r == 1) + { + __NK_ASSERT_ALWAYS(!pageCleaningLockHeld); + MmuLock::Unlock(); + PageCleaningLock::Lock(); + MmuLock::Lock(); + pageCleaningLockHeld = ETrue; } - else if (iOldestDirtyCount) + // else retry... + } + } + +#ifdef __CPU_CACHE_HAS_COLOUR + +template class TSequentialColourSelector + { +public: + static const TInt KMaxLength = maxObjects; + static const TInt KArrayLength = _ALIGN_UP(KMaxLength, KPageColourCount); + + FORCE_INLINE TSequentialColourSelector() + { + memclr(this, sizeof(*this)); + } + + FORCE_INLINE TBool FoundLongestSequence() + { + return iLongestLength >= KMaxLength; + } + + FORCE_INLINE void AddCandidate(T* aObject, TInt aColour) + { + // allocate objects to slots based on colour + for (TInt i = aColour ; i < KArrayLength ; i += KPageColourCount) { - __NK_ASSERT_DEBUG(!iOldestDirtyList.IsEmpty()); - link = iOldestDirtyList.Last(); + if (!iSlot[i]) + { + iSlot[i] = aObject; + iSeqLength[i] = i == 0 ? 1 : iSeqLength[i - 1] + 1; + TInt j = i + 1; + while(j < KArrayLength && iSeqLength[j]) + iSeqLength[j++] += iSeqLength[i]; + TInt currentLength = iSeqLength[j - 1]; + if (currentLength > iLongestLength) + { + iLongestLength = currentLength; + iLongestStart = j - currentLength; + } + break; + } } - else if (iOldCount) -#else - if (iOldCount) -#endif + } + + FORCE_INLINE TInt FindLongestRun(T** aObjectsOut) + { + if (iLongestLength == 0) + return 0; + + if (iLongestLength < KMaxLength && iSlot[0] && iSlot[KArrayLength - 1]) { - __NK_ASSERT_DEBUG(!iOldList.IsEmpty()); - link = iOldList.Last(); - } - else + // check possibility of wrapping + + TInt i = 1; + while (iSlot[i]) ++i; // find first hole + TInt wrappedLength = iSeqLength[KArrayLength - 1] + iSeqLength[i - 1]; + if (wrappedLength > iLongestLength) + { + iLongestLength = wrappedLength; + iLongestStart = KArrayLength - iSeqLength[KArrayLength - 1]; + } + } + + iLongestLength = Min(iLongestLength, KMaxLength); + + __NK_ASSERT_DEBUG(iLongestStart >= 0 && iLongestStart < KArrayLength); + __NK_ASSERT_DEBUG(iLongestStart + iLongestLength < 2 * KArrayLength); + + TInt len = Min(iLongestLength, KArrayLength - iLongestStart); + wordmove(aObjectsOut, &iSlot[iLongestStart], len * sizeof(T*)); + wordmove(aObjectsOut + len, &iSlot[0], (iLongestLength - len) * sizeof(T*)); + + return iLongestLength; + } + +private: + T* iSlot[KArrayLength]; + TInt8 iSeqLength[KArrayLength]; + TInt iLongestStart; + TInt iLongestLength; + }; + +TInt DPager::SelectPagesToClean(SPageInfo** aPageInfosOut) + { + // select up to KMaxPagesToClean oldest dirty pages with sequential page colours + + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + + TSequentialColourSelector selector; + + SDblQueLink* link = iOldestDirtyList.Last(); + while (link != &iOldestDirtyList.iA) + { + SPageInfo* pi = SPageInfo::FromLink(link); + if (!pi->IsWritable()) { - __NK_ASSERT_DEBUG(iYoungCount); - __NK_ASSERT_ALWAYS(!iYoungList.IsEmpty()); - link = iYoungList.Last(); + // the page may be in the process of being restricted, stolen or decommitted, but don't + // check for this as it will occur infrequently and will be detected by CheckModified + // anyway + TInt colour = pi->Index() & KPageColourMask; + selector.AddCandidate(pi, colour); + if (selector.FoundLongestSequence()) + break; } - SPageInfo* pageInfo = SPageInfo::FromLink(link); - - // steal it from owning object... - TInt r = StealPage(pageInfo); - - BalanceAges(); - - if(r==KErrNone) - return pageInfo; // done - - // loop back and try again + link = link->iPrev; + } + + return selector.FindLongestRun(aPageInfosOut); + } + +#else + +TInt DPager::SelectPagesToClean(SPageInfo** aPageInfosOut) + { + // no page colouring restrictions, so just take up to KMaxPagesToClean oldest dirty pages + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + TInt pageCount = 0; + SDblQueLink* link = iOldestDirtyList.Last(); + while (link != &iOldestDirtyList.iA && pageCount < KMaxPagesToClean) + { + SPageInfo* pi = SPageInfo::FromLink(link); + if (!pi->IsWritable()) + { + // the page may be in the process of being restricted, stolen or decommitted, but don't + // check for this as it will occur infrequently and will be detected by CheckModified + // anyway + aPageInfosOut[pageCount++] = pi; + } + link = link->iPrev; } + return pageCount; + } + +#endif + + +TInt DPager::CleanSomePages(TBool aBackground) + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + __NK_ASSERT_DEBUG(PageCleaningLock::IsHeld()); + // ram alloc lock may or may not be held + + SPageInfo* pageInfos[KMaxPagesToClean]; + TInt pageCount = SelectPagesToClean(&pageInfos[0]); + + if (pageCount == 0) + return 0; + + TheDataPagedMemoryManager->CleanPages(pageCount, pageInfos, aBackground); + + for (TInt i = 0 ; i < pageCount ; ++i) + { + SPageInfo* pi = pageInfos[i]; + if (pi) + { + __NK_ASSERT_DEBUG(pi->PagedState() == SPageInfo::EPagedOldestDirty && iOldestDirtyCount); + __NK_ASSERT_DEBUG(!pi->IsDirty() && !pi->IsWritable()); + + pi->iLink.Deque(); + iOldestCleanList.AddHead(&pi->iLink); + --iOldestDirtyCount; + ++iOldestCleanCount; + pi->SetPagedState(SPageInfo::EPagedOldestClean); + } + } + + return pageCount; + } + + +TBool DPager::HasPagesToClean() + { + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + return iOldestDirtyCount > 0; } @@ -647,97 +812,158 @@ } +TInt DPager::DiscardAndAllocPage(SPageInfo* aPageInfo, TZonePageType aPageType) + { + TInt r = DiscardPage(aPageInfo, KRamZoneInvalidId, EFalse); + if (r == KErrNone) + { + TheMmu.MarkPageAllocated(aPageInfo->PhysAddr(), aPageType); + } + // Flash the ram alloc lock as we may have had to write a page out to swap. + RamAllocLock::Unlock(); + RamAllocLock::Lock(); + return r; + } + + +static TBool DiscardCanStealPage(SPageInfo* aOldPageInfo, TBool aBlockRest) + { + // If the page is pinned or if the page is dirty and a general defrag is being performed then + // don't attempt to steal it + return aOldPageInfo->Type() == SPageInfo::EUnused || + (aOldPageInfo->PagedState() != SPageInfo::EPagedPinned && (!aBlockRest || !aOldPageInfo->IsDirty())); + } + + TInt DPager::DiscardPage(SPageInfo* aOldPageInfo, TUint aBlockZoneId, TBool aBlockRest) { + // todo: assert MmuLock not released + + TRACE(("> DPager::DiscardPage %08x", aOldPageInfo)); + __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); __NK_ASSERT_DEBUG(MmuLock::IsHeld()); - TInt r; - // If the page is pinned or if the page is dirty and a general defrag is being - // performed then don't attempt to steal it. - if (aOldPageInfo->Type() != SPageInfo::EUnused && - (aOldPageInfo->PagedState() == SPageInfo::EPagedPinned || - (aBlockRest && aOldPageInfo->IsDirty()))) - {// The page is pinned or is dirty and this is a general defrag so move the page. + if (!DiscardCanStealPage(aOldPageInfo, aBlockRest)) + { + // The page is pinned or is dirty and this is a general defrag so move the page. DMemoryObject* memory = aOldPageInfo->Owner(); // Page must be managed if it is pinned or dirty. __NK_ASSERT_DEBUG(aOldPageInfo->Type()==SPageInfo::EManaged); __NK_ASSERT_DEBUG(memory); MmuLock::Unlock(); TPhysAddr newAddr; - return memory->iManager->MovePage(memory, aOldPageInfo, newAddr, aBlockZoneId, aBlockRest); + TRACE2(("DPager::DiscardPage delegating pinned/dirty page to manager")); + TInt r = memory->iManager->MovePage(memory, aOldPageInfo, newAddr, aBlockZoneId, aBlockRest); + TRACE(("< DPager::DiscardPage %d", r)); + return r; } - if (!iNumberOfFreePages) + TInt r = KErrNone; + SPageInfo* newPageInfo = NULL; + TBool havePageCleaningLock = EFalse; + + TBool needNewPage; + TBool needPageCleaningLock; + while(needNewPage = (iNumberOfFreePages == 0 && newPageInfo == NULL), + needPageCleaningLock = (aOldPageInfo->IsDirty() && !havePageCleaningLock), + needNewPage || needPageCleaningLock) { - // Allocate a new page for the live list as it has reached its minimum size. MmuLock::Unlock(); - SPageInfo* newPageInfo = GetPageFromSystem((Mmu::TRamAllocFlags)(EMemAttNormalCached|Mmu::EAllocNoWipe), - aBlockZoneId, aBlockRest); - if (!newPageInfo) - return KErrNoMemory; + + if (needNewPage) + { + // Allocate a new page for the live list as it has reached its minimum size. + TUint flags = EMemAttNormalCached | Mmu::EAllocNoWipe; + newPageInfo = GetPageFromSystem((Mmu::TRamAllocFlags)flags, aBlockZoneId, aBlockRest); + if (!newPageInfo) + { + TRACE(("< DPager::DiscardPage KErrNoMemory")); + r = KErrNoMemory; + MmuLock::Lock(); + break; + } + } + + if (needPageCleaningLock) + { + // Acquire the page cleaning mutex so StealPage can clean it + PageCleaningLock::Lock(); + havePageCleaningLock = ETrue; + } // Re-acquire the mmulock and re-check that the page is not pinned or dirty. MmuLock::Lock(); - if (aOldPageInfo->Type() != SPageInfo::EUnused && - (aOldPageInfo->PagedState() == SPageInfo::EPagedPinned || - (aBlockRest && aOldPageInfo->IsDirty()))) - {// Page is now pinned or dirty so give up as it is inuse. - ReturnPageToSystem(*newPageInfo); - MmuLock::Unlock(); - return KErrInUse; - } - - // Attempt to steal the page - r = StealPage(aOldPageInfo); - __NK_ASSERT_DEBUG(MmuLock::IsHeld()); - - if (r == KErrCompletion) - {// This was a page table that has been freed but added to the - // live list as a free page. Remove from live list and continue. - __NK_ASSERT_DEBUG(!aOldPageInfo->IsDirty()); - RemovePage(aOldPageInfo); - r = KErrNone; - } - - if (r == KErrNone) - {// Add the new page to the live list as discarding the old page - // will reduce the live list below the minimum. - AddAsFreePage(newPageInfo); - // We've successfully discarded the page so return it to the free pool. - ReturnPageToSystem(*aOldPageInfo); - BalanceAges(); - } - else + if (!DiscardCanStealPage(aOldPageInfo, aBlockRest)) { - // New page not required so just return it to the system. This is safe as - // iNumberOfFreePages will have this page counted but as it is not on the live list - // noone else can touch it. - ReturnPageToSystem(*newPageInfo); + // Page is now pinned or dirty so give up as it is in use. + r = KErrInUse; + break; } } - else + + if (r == KErrNone) { // Attempt to steal the page - r = StealPage(aOldPageInfo); - - __NK_ASSERT_DEBUG(MmuLock::IsHeld()); - - if (r == KErrCompletion) - {// This was a page table that has been freed but added to the - // live list as a free page. Remove from live list. - __NK_ASSERT_DEBUG(!aOldPageInfo->IsDirty()); - RemovePage(aOldPageInfo); - r = KErrNone; + r = StealPage(aOldPageInfo); // temporarily releases MmuLock if page is dirty + } + __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + + if (r == KErrCompletion) + {// This was a page table that has been freed but added to the + // live list as a free page. Remove from live list and continue. + __NK_ASSERT_DEBUG(!aOldPageInfo->IsDirty()); + RemovePage(aOldPageInfo); + r = KErrNone; + } + + if (r == KErrNone && iNumberOfFreePages == 0) + { + if (newPageInfo) + { + // Add a new page to the live list if we have one as discarding the old page will reduce + // the live list below the minimum. + AddAsFreePage(newPageInfo); + newPageInfo = NULL; } - - if (r == KErrNone) - {// We've successfully discarded the page so return it to the free pool. - ReturnPageToSystem(*aOldPageInfo); - BalanceAges(); + else + { + // Otherwise the live list shrank when page was being cleaned so have to give up + AddAsFreePage(aOldPageInfo); + BalanceAges(); // temporarily releases MmuLock + r = KErrInUse; } } + + if (r == KErrNone) + { + // We've successfully discarded the page and ensured the live list is large enough, so + // return it to the free pool. + ReturnPageToSystem(*aOldPageInfo); // temporarily releases MmuLock + BalanceAges(); // temporarily releases MmuLock + } + + if (newPageInfo) + { + // New page not required so just return it to the system. This is safe as + // iNumberOfFreePages will have this page counted but as it is not on the live list noone + // else can touch it. + if (iNumberOfFreePages == 0) + AddAsFreePage(newPageInfo); + else + ReturnPageToSystem(*newPageInfo); // temporarily releases MmuLock + } + + if (havePageCleaningLock) + { + // Release the page cleaning mutex + MmuLock::Unlock(); + PageCleaningLock::Unlock(); + MmuLock::Lock(); + } + MmuLock::Unlock(); + TRACE(("< DPager::DiscardPage returns %d", r)); return r; } @@ -793,6 +1019,9 @@ __NK_ASSERT_DEBUG(RamAllocLock::IsHeld()); __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + // should be unpaged at this point, otherwise Mmu::FreeRam will just give it back to us + __NK_ASSERT_DEBUG(aPageInfo.PagedState() == SPageInfo::EUnpaged); + __NK_ASSERT_DEBUG(iNumberOfFreePages>0); --iNumberOfFreePages; @@ -810,28 +1039,22 @@ SPageInfo* DPager::PageInAllocPage(Mmu::TRamAllocFlags aAllocFlags) { + TBool pageCleaningLockHeld = EFalse; SPageInfo* pageInfo; TPhysAddr pagePhys; - + TInt r = KErrGeneral; + RamAllocLock::Lock(); MmuLock::Lock(); +find_a_page: // try getting a free page from our live list... -#ifdef _USE_OLDEST_LISTS if (iOldestCleanCount) { pageInfo = SPageInfo::FromLink(iOldestCleanList.Last()); if(pageInfo->Type()==SPageInfo::EUnused) - goto get_oldest; + goto try_steal_oldest_page; } -#else - if(iOldCount) - { - pageInfo = SPageInfo::FromLink(iOldList.Last()); - if(pageInfo->Type()==SPageInfo::EUnused) - goto get_oldest; - } -#endif // try getting a free page from the system pool... if(!HaveMaximumPages()) @@ -843,14 +1066,62 @@ MmuLock::Lock(); } + // try stealing a clean page... + if (iOldestCleanCount) + goto try_steal_oldest_page; + + // see if we can clean multiple dirty pages in one go... + if (KMaxPagesToClean > 1 && iOldestDirtyCount > 1) + { + // if we don't hold the page cleaning mutex then temporarily release ram alloc mutex and + // acquire page cleaning mutex; if we hold it already just proceed + if (!pageCleaningLockHeld) + { + MmuLock::Unlock(); + RamAllocLock::Unlock(); + PageCleaningLock::Lock(); + MmuLock::Lock(); + } + + // there may be clean pages now if we've waited on the page cleaning mutex, if so don't + // bother cleaning but just restart + if (iOldestCleanCount == 0) + CleanSomePages(EFalse); + + if (!pageCleaningLockHeld) + { + MmuLock::Unlock(); + PageCleaningLock::Unlock(); + RamAllocLock::Lock(); + MmuLock::Lock(); + } + + if (iOldestCleanCount > 0) + goto find_a_page; + } + // as a last resort, steal a page from the live list... -get_oldest: -#ifdef _USE_OLDEST_LISTS + +try_steal_oldest_page: __NK_ASSERT_ALWAYS(iOldestCleanCount|iOldestDirtyCount|iOldCount|iYoungCount); -#else - __NK_ASSERT_ALWAYS(iOldCount|iYoungCount); -#endif - pageInfo = StealOldestPage(); + r = TryStealOldestPage(pageInfo); + // if this fails we restart whole process + if (r < KErrNone) + goto find_a_page; + + // if we need to clean, acquire page cleaning mutex for life of this function + if (r == 1) + { + __NK_ASSERT_ALWAYS(!pageCleaningLockHeld); + MmuLock::Unlock(); + PageCleaningLock::Lock(); + MmuLock::Lock(); + pageCleaningLockHeld = ETrue; + goto find_a_page; + } + + // otherwise we're done! + __NK_ASSERT_DEBUG(r == KErrNone); MmuLock::Unlock(); // make page state same as a freshly allocated page... @@ -858,7 +1129,10 @@ TheMmu.PagesAllocated(&pagePhys,1,aAllocFlags); done: + if (pageCleaningLockHeld) + PageCleaningLock::Unlock(); RamAllocLock::Unlock(); + return pageInfo; } @@ -915,10 +1189,8 @@ case SPageInfo::EPagedYoung: case SPageInfo::EPagedOld: -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestDirty: case SPageInfo::EPagedOldestClean: -#endif continue; // discard already been allowed case SPageInfo::EPagedPinned: @@ -977,10 +1249,8 @@ case SPageInfo::EPagedYoung: case SPageInfo::EPagedOld: -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: case SPageInfo::EPagedOldestDirty: -#endif changeType = ETrue; break; // remove from live list @@ -1046,6 +1316,7 @@ TheCodePagedMemoryManager->Init3(); TInt r = Kern::AddHalEntry(EHalGroupVM, VMHalFunction, 0); __NK_ASSERT_ALWAYS(r==KErrNone); + PageCleaningLock::Init(); } @@ -1060,12 +1331,8 @@ __NK_ASSERT_DEBUG(MmuLock::IsHeld()); TBool restrictPage = EFalse; SPageInfo* pageInfo = NULL; -#ifdef _USE_OLDEST_LISTS TUint oldestCount = iOldestCleanCount + iOldestDirtyCount; if((iOldCount + oldestCount) * iYoungOldRatio < iYoungCount) -#else - if (iOldCount * iYoungOldRatio < iYoungCount) -#endif { // Need more old pages so make one young page into an old page... __NK_ASSERT_DEBUG(!iYoungList.IsEmpty()); @@ -1084,9 +1351,9 @@ restrictPage = ETrue; } -#ifdef _USE_OLDEST_LISTS // Check we have enough oldest pages. - if (oldestCount * iOldOldestRatio < iOldCount) + if (oldestCount < KMaxOldestPages && + oldestCount * iOldOldestRatio < iOldCount) { __NK_ASSERT_DEBUG(!iOldList.IsEmpty()); __NK_ASSERT_DEBUG(iOldCount); @@ -1099,6 +1366,7 @@ oldestPageInfo->SetPagedState(SPageInfo::EPagedOldestDirty); iOldestDirtyList.AddHead(link); ++iOldestDirtyCount; + PageCleaner::NotifyPagesToClean(); Event(EEventPageAgedDirty,oldestPageInfo); } else @@ -1109,7 +1377,7 @@ Event(EEventPageAgedClean,oldestPageInfo); } } -#endif + if (restrictPage) { // Make the recently aged old page inaccessible. This is done last as it @@ -1144,10 +1412,8 @@ { case SPageInfo::EPagedYoung: case SPageInfo::EPagedOld: -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: case SPageInfo::EPagedOldestDirty: -#endif RemovePage(pi); AddAsYoungestPage(pi); BalanceAges(); @@ -1167,6 +1433,7 @@ } } + TInt DPager::PteAndInfoFromLinAddr( TInt aOsAsid, TLinAddr aAddress, DMemoryMappingBase* aMapping, TUint aMapInstanceCount, TPte*& aPte, SPageInfo*& aPageInfo) { @@ -1192,11 +1459,13 @@ return KErrNone; } + TInt DPager::TryRejuvenate( TInt aOsAsid, TLinAddr aAddress, TUint aAccessPermissions, TLinAddr aPc, DMemoryMappingBase* aMapping, TUint aMapInstanceCount, DThread* aThread, TAny* aExceptionInfo) { __NK_ASSERT_DEBUG(MmuLock::IsHeld()); + START_PAGING_BENCHMARK; SPageInfo* pi; TPte* pPte; @@ -1292,12 +1561,8 @@ Event(EEventPageRejuvenate,pi,aPc,aAddress,aAccessPermissions); TBool balance = false; -#ifdef _USE_OLDEST_LISTS if( state==SPageInfo::EPagedYoung || state==SPageInfo::EPagedOld || state==SPageInfo::EPagedOldestClean || state==SPageInfo::EPagedOldestDirty) -#else - if(state==SPageInfo::EPagedYoung || state==SPageInfo::EPagedOld) -#endif { RemovePage(pi); AddAsYoungestPage(pi); @@ -1318,6 +1583,7 @@ if(balance) BalanceAges(); + END_PAGING_BENCHMARK(EPagingBmRejuvenate); return KErrNone; } @@ -1349,10 +1615,8 @@ { case SPageInfo::EPagedYoung: case SPageInfo::EPagedOld: -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: case SPageInfo::EPagedOldestDirty: -#endif RemovePage(pi); // fall through... case SPageInfo::EUnpaged: @@ -1386,10 +1650,8 @@ { case SPageInfo::EPagedYoung: case SPageInfo::EPagedOld: -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: case SPageInfo::EPagedOldestDirty: -#endif RemovePage(aPageInfo); AddAsYoungestPage(aPageInfo); BalanceAges(); @@ -1446,7 +1708,6 @@ __NK_ASSERT_DEBUG(aPageInfo->PinCount()==1); break; -#ifdef _USE_OLDEST_LISTS case SPageInfo::EPagedOldestClean: __NK_ASSERT_DEBUG(iOldestCleanCount); aPageInfo->iLink.Deque(); @@ -1460,7 +1721,6 @@ --iOldestDirtyCount; __NK_ASSERT_DEBUG(aPageInfo->PinCount()==1); break; -#endif case SPageInfo::EPagedPinned: // nothing more to do... @@ -1749,7 +2009,7 @@ MmuLock::Lock(); - __NK_ASSERT_ALWAYS(iYoungOldRatio!=0); + __NK_ASSERT_ALWAYS(iYoungOldRatio); // Make sure aMinimumPageCount is not less than absolute minimum we can cope with... iMinimumPageLimit = iMinYoungPages * (1 + iYoungOldRatio) / iYoungOldRatio @@ -1830,10 +2090,12 @@ } +// WARNING THIS METHOD MAY HOLD THE RAM ALLOC LOCK FOR EXCESSIVE PERIODS. DON'T USE THIS IN ANY PRODUCTION CODE. void DPager::FlushAll() { NKern::ThreadEnterCS(); RamAllocLock::Lock(); + PageCleaningLock::Lock(); TRACE(("DPager::FlushAll() live list young=%d old=%d min=%d free=%d max=%d",iYoungCount,iOldCount,iMinimumPageCount,iNumberOfFreePages,iMaximumPageCount)); @@ -1856,12 +2118,8 @@ do { SPageInfo::TPagedState state = pi->PagedState(); -#ifdef _USE_OLDEST_LISTS if (state==SPageInfo::EPagedYoung || state==SPageInfo::EPagedOld || state==SPageInfo::EPagedOldestClean || state==SPageInfo::EPagedOldestDirty) -#else - if(state==SPageInfo::EPagedYoung || state==SPageInfo::EPagedOld) -#endif { if (pi->Type() != SPageInfo::EUnused) { @@ -1874,10 +2132,7 @@ ++pi; if(((TUint)pi&(0xf<= EMaxPagingBm) return KErrNotFound; - NKern::LockSystem(); - SPagingBenchmarkInfo info = ThePager.iBenchmarkInfo[index]; - NKern::UnlockSystem(); + SPagingBenchmarkInfo info; + ThePager.ReadBenchmarkData((TPagingBenchmark)index, info); kumemput32(a2,&info,sizeof(info)); } return KErrNone; @@ -2078,9 +2333,7 @@ TUint index = (TInt) a1; if (index >= EMaxPagingBm) return KErrNotFound; - NKern::LockSystem(); ThePager.ResetBenchmarkData((TPagingBenchmark)index); - NKern::UnlockSystem(); } return KErrNone; #endif @@ -2096,28 +2349,39 @@ void DPager::ResetBenchmarkData(TPagingBenchmark aBm) { SPagingBenchmarkInfo& info = iBenchmarkInfo[aBm]; + __SPIN_LOCK_IRQ(iBenchmarkLock); info.iCount = 0; info.iTotalTime = 0; info.iMaxTime = 0; info.iMinTime = KMaxTInt; + __SPIN_UNLOCK_IRQ(iBenchmarkLock); } -void DPager::RecordBenchmarkData(TPagingBenchmark aBm, TUint32 aStartTime, TUint32 aEndTime) +void DPager::RecordBenchmarkData(TPagingBenchmark aBm, TUint32 aStartTime, TUint32 aEndTime, TUint aCount) { SPagingBenchmarkInfo& info = iBenchmarkInfo[aBm]; - ++info.iCount; #if !defined(HIGH_RES_TIMER) || defined(HIGH_RES_TIMER_COUNTS_UP) TInt64 elapsed = aEndTime - aStartTime; #else TInt64 elapsed = aStartTime - aEndTime; #endif + __SPIN_LOCK_IRQ(iBenchmarkLock); + info.iCount += aCount; info.iTotalTime += elapsed; if (elapsed > info.iMaxTime) info.iMaxTime = elapsed; if (elapsed < info.iMinTime) info.iMinTime = elapsed; + __SPIN_UNLOCK_IRQ(iBenchmarkLock); } +void DPager::ReadBenchmarkData(TPagingBenchmark aBm, SPagingBenchmarkInfo& aDataOut) + { + __SPIN_LOCK_IRQ(iBenchmarkLock); + aDataOut = iBenchmarkInfo[aBm]; + __SPIN_UNLOCK_IRQ(iBenchmarkLock); + } + #endif //__DEMAND_PAGING_BENCHMARKS__ @@ -2129,62 +2393,86 @@ // DPagingRequest // -DPagingRequest::DPagingRequest(DPagingRequestPool::TGroup& aPoolGroup) - : iPoolGroup(aPoolGroup), iUseRegionMemory(0), iUseRegionIndex(0), iUseRegionCount(0) +DPagingRequest::DPagingRequest() + : iMutex(NULL), iUseRegionCount(0) { } -FORCE_INLINE void DPagingRequest::SetUse(DMemoryObject* aMemory, TUint aIndex, TUint aCount) +void DPagingRequest::SetUseContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount) { __ASSERT_SYSTEM_LOCK; - iUseRegionMemory = aMemory; - iUseRegionIndex = aIndex; + __NK_ASSERT_DEBUG(iUseRegionCount == 0); + __NK_ASSERT_DEBUG(aCount > 0 && aCount <= EMaxPages); + for (TUint i = 0 ; i < aCount ; ++i) + { + iUseRegionMemory[i] = aMemory; + iUseRegionIndex[i] = aIndex + i; + } + iUseRegionCount = aCount; + } + + +void DPagingRequest::SetUseDiscontiguous(DMemoryObject** aMemory, TUint* aIndex, TUint aCount) + { + __ASSERT_SYSTEM_LOCK; + __NK_ASSERT_DEBUG(iUseRegionCount == 0); + __NK_ASSERT_DEBUG(aCount > 0 && aCount <= EMaxPages); + for (TUint i = 0 ; i < aCount ; ++i) + { + iUseRegionMemory[i] = aMemory[i]; + iUseRegionIndex[i] = aIndex[i]; + } iUseRegionCount = aCount; } -TBool DPagingRequest::CheckUse(DMemoryObject* aMemory, TUint aIndex, TUint aCount) - { - return aMemory==iUseRegionMemory - && TUint(aIndex-iUseRegionIndex) < iUseRegionCount - && TUint(iUseRegionCount-TUint(aIndex-iUseRegionIndex)) <= aCount; - } - - -void DPagingRequest::Release() - { - NKern::LockSystem(); - SetUse(0,0,0); - Signal(); - } - - -void DPagingRequest::Wait() +void DPagingRequest::ResetUse() { __ASSERT_SYSTEM_LOCK; - ++iUsageCount; - TInt r = iMutex->Wait(); - __NK_ASSERT_ALWAYS(r == KErrNone); + __NK_ASSERT_DEBUG(iUseRegionCount > 0); + iUseRegionCount = 0; } -void DPagingRequest::Signal() +TBool DPagingRequest::CheckUseContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount) { - __ASSERT_SYSTEM_LOCK; - iPoolGroup.Signal(this); + if (iUseRegionCount != aCount) + return EFalse; + for (TUint i = 0 ; i < iUseRegionCount ; ++i) + { + if (iUseRegionMemory[i] != aMemory || iUseRegionIndex[i] != aIndex + i) + return EFalse; + } + return ETrue; } -FORCE_INLINE TBool DPagingRequest::IsCollision(DMemoryObject* aMemory, TUint aIndex, TUint aCount) +TBool DPagingRequest::CheckUseDiscontiguous(DMemoryObject** aMemory, TUint* aIndex, TUint aCount) { + if (iUseRegionCount != aCount) + return EFalse; + for (TUint i = 0 ; i < iUseRegionCount ; ++i) + { + if (iUseRegionMemory[i] != aMemory[i] || iUseRegionIndex[i] != aIndex[i]) + return EFalse; + } + return ETrue; + } + + + TBool DPagingRequest::IsCollisionContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount) + { + // note this could be optimised as most of the time we will be checking read/read collusions, + // both of which will be contiguous __ASSERT_SYSTEM_LOCK; - DMemoryObject* memory = iUseRegionMemory; - TUint index = iUseRegionIndex; - TUint count = iUseRegionCount; - // note, this comparison would fail if either region includes page number KMaxTUint, - // but it isn't possible to create a memory object which is > KMaxTUint pages... - return (memory == aMemory) && ((index + count) > aIndex) && (index < (aIndex + aCount)); + for (TUint i = 0 ; i < iUseRegionCount ; ++i) + { + if (iUseRegionMemory[i] == aMemory && + TUint(iUseRegionIndex[i] - aIndex) < aCount) + return ETrue; + } + return EFalse; } @@ -2201,6 +2489,38 @@ iTempMapping.Unmap(aIMBRequired); } +// +// DPoolPagingRequest +// + +DPoolPagingRequest::DPoolPagingRequest(DPagingRequestPool::TGroup& aPoolGroup) : + iPoolGroup(aPoolGroup) + { + } + + +void DPoolPagingRequest::Release() + { + NKern::LockSystem(); + ResetUse(); + Signal(); + } + + +void DPoolPagingRequest::Wait() + { + __ASSERT_SYSTEM_LOCK; + ++iUsageCount; + TInt r = iMutex->Wait(); + __NK_ASSERT_ALWAYS(r == KErrNone); + } + + +void DPoolPagingRequest::Signal() + { + __ASSERT_SYSTEM_LOCK; + iPoolGroup.Signal(this); + } // // DPageReadRequest @@ -2208,6 +2528,13 @@ TInt DPageReadRequest::iAllocNext = 0; +DPageReadRequest::DPageReadRequest(DPagingRequestPool::TGroup& aPoolGroup) : + DPoolPagingRequest(aPoolGroup) + { + // allocate space for mapping pages whilst they're being loaded... + iTempMapping.Alloc(EMaxPages); + } + TInt DPageReadRequest::Construct() { // allocate id and mutex... @@ -2219,9 +2546,6 @@ if(r!=KErrNone) return r; - // allocate space for mapping pages whilst they're being loaded... - iTempMapping.Alloc(EMaxPages); - // create memory buffer... TUint bufferSize = EMaxPages+1; DMemoryObject* bufferMemory; @@ -2248,23 +2572,20 @@ // DPageWriteRequest // -TInt DPageWriteRequest::iAllocNext = 0; - -TInt DPageWriteRequest::Construct() + +DPageWriteRequest::DPageWriteRequest() { - // allocate id and mutex... - TUint id = (TUint)__e32_atomic_add_ord32(&iAllocNext, 1); - _LIT(KLitPagingRequest,"PageWriteRequest-"); - TBuf mutexName(KLitPagingRequest); - mutexName.AppendNum(id); - TInt r = K::MutexCreate(iMutex, mutexName, NULL, EFalse, KMutexOrdPageOut); - if(r!=KErrNone) - return r; - + iMutex = ThePageCleaningLock; // allocate space for mapping pages whilst they're being loaded... - iTempMapping.Alloc(EMaxPages); - - return r; + iTempMapping.Alloc(KMaxPagesToClean); + } + + +void DPageWriteRequest::Release() + { + NKern::LockSystem(); + ResetUse(); + NKern::UnlockSystem(); } @@ -2272,11 +2593,10 @@ // DPagingRequestPool // -DPagingRequestPool::DPagingRequestPool(TUint aNumPageReadRequest,TUint aNumPageWriteRequest) - : iPageReadRequests(aNumPageReadRequest), iPageWriteRequests(aNumPageWriteRequest) +DPagingRequestPool::DPagingRequestPool(TUint aNumPageReadRequest, TBool aWriteRequest) + : iPageReadRequests(aNumPageReadRequest) { TUint i; - for(i=0; iConstruct(); - __NK_ASSERT_ALWAYS(r==KErrNone); - iPageWriteRequests.iRequests[i] = req; - iPageWriteRequests.iFreeList.Add(req); + iPageWriteRequest = new DPageWriteRequest(); + __NK_ASSERT_ALWAYS(iPageWriteRequest); } } @@ -2309,24 +2625,23 @@ { NKern::LockSystem(); - DPagingRequest* req; - - // if we collide with page write operation... - req = iPageWriteRequests.FindCollision(aMemory,aIndex,aCount); - if(req) + DPoolPagingRequest* req; + + // check for collision with existing write + if(iPageWriteRequest && iPageWriteRequest->IsCollisionContiguous(aMemory,aIndex,aCount)) { - // wait until write completes... - req->Wait(); - req->Signal(); + NKern::UnlockSystem(); + PageCleaningLock::Lock(); + PageCleaningLock::Unlock(); return 0; // caller expected to retry if needed } // get a request object to use... req = iPageReadRequests.GetRequest(aMemory,aIndex,aCount); - // check no new requests collide with us... - if(iPageWriteRequests.FindCollision(aMemory,aIndex,aCount) - || iPageReadRequests.FindCollision(aMemory,aIndex,aCount)) + // check no new read or write requests collide with us... + if ((iPageWriteRequest && iPageWriteRequest->IsCollisionContiguous(aMemory,aIndex,aCount)) || + iPageReadRequests.FindCollisionContiguous(aMemory,aIndex,aCount)) { // another operation is colliding with this region, give up and retry... req->Signal(); @@ -2334,61 +2649,57 @@ } // we have a request object which we can use... - req->SetUse(aMemory,aIndex,aCount); + req->SetUseContiguous(aMemory,aIndex,aCount); NKern::UnlockSystem(); return (DPageReadRequest*)req; } -DPageWriteRequest* DPagingRequestPool::AcquirePageWriteRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount) +DPageWriteRequest* DPagingRequestPool::AcquirePageWriteRequest(DMemoryObject** aMemory, TUint* aIndex, TUint aCount) { + __NK_ASSERT_DEBUG(iPageWriteRequest); + __NK_ASSERT_DEBUG(PageCleaningLock::IsHeld()); + NKern::LockSystem(); - DPagingRequest* req; - - for(;;) - { - // get a request object to use... - req = iPageWriteRequests.GetRequest(aMemory,aIndex,aCount); - - if(iPageWriteRequests.FindCollision(aMemory,aIndex,aCount)) - { - // another write operation is colliding with this region, give up and retry... - req->Signal(); - // Reacquire the system lock as Signal() above will release it. - NKern::LockSystem(); - continue; - } - - break; - } - - // we have a request object which we can use... - req->SetUse(aMemory,aIndex,aCount); - + // Collision with existing read requests is not possible here. For a page to be read it must + // not be present, and for it to be written it must be present and dirty. There is no way for a + // page to go between these states without an intervening read on an uninitialised (freshly + // committed) page, which will wait on the first read request. In other words something like + // this: + // + // read (blocks), decommit, re-commit, read (waits on mutex), write (now no pending reads!) + // + // Note that a read request can be outstanding and appear to collide with this write, but only + // in the case when the thread making the read has blocked just after acquiring the request but + // before it checks whether the read is still necessasry. This makes it difficult to assert + // that no collisions take place. + + iPageWriteRequest->SetUseDiscontiguous(aMemory,aIndex,aCount); NKern::UnlockSystem(); - return (DPageWriteRequest*)req; + + return iPageWriteRequest; } DPagingRequestPool::TGroup::TGroup(TUint aNumRequests) { iNumRequests = aNumRequests; - iRequests = new DPagingRequest*[aNumRequests]; + iRequests = new DPoolPagingRequest*[aNumRequests]; __NK_ASSERT_ALWAYS(iRequests); } -DPagingRequest* DPagingRequestPool::TGroup::FindCollision(DMemoryObject* aMemory, TUint aIndex, TUint aCount) +DPoolPagingRequest* DPagingRequestPool::TGroup::FindCollisionContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount) { __ASSERT_SYSTEM_LOCK; - DPagingRequest** ptr = iRequests; - DPagingRequest** ptrEnd = ptr+iNumRequests; + DPoolPagingRequest** ptr = iRequests; + DPoolPagingRequest** ptrEnd = ptr+iNumRequests; while(ptrIsCollision(aMemory,aIndex,aCount)) + DPoolPagingRequest* req = *ptr++; + if(req->IsCollisionContiguous(aMemory,aIndex,aCount)) return req; } return 0; @@ -2397,16 +2708,16 @@ static TUint32 RandomSeed = 33333; -DPagingRequest* DPagingRequestPool::TGroup::GetRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount) +DPoolPagingRequest* DPagingRequestPool::TGroup::GetRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount) { __NK_ASSERT_DEBUG(iNumRequests > 0); // try using an existing request which collides with this region... - DPagingRequest* req = FindCollision(aMemory,aIndex,aCount); + DPoolPagingRequest* req = FindCollisionContiguous(aMemory,aIndex,aCount); if(!req) { // use a free request... - req = (DPagingRequest*)iFreeList.GetFirst(); + req = (DPoolPagingRequest*)iFreeList.GetFirst(); if(req) { // free requests aren't being used... @@ -2429,7 +2740,7 @@ } -void DPagingRequestPool::TGroup::Signal(DPagingRequest* aRequest) +void DPagingRequestPool::TGroup::Signal(DPoolPagingRequest* aRequest) { // if there are no threads waiting on the mutex then return it to the free pool... __NK_ASSERT_DEBUG(aRequest->iUsageCount > 0); @@ -2457,8 +2768,8 @@ TInt r = KErrNotSupported; // Will return this if unsupported device type is installed // create the pools of page out and page in requests... - const TInt writeReqs = (aDevice->iType & DPagingDevice::EData) ? KPagingRequestsPerDevice : 0; - aDevice->iRequestPool = new DPagingRequestPool(KPagingRequestsPerDevice,writeReqs); + const TBool writeReq = (aDevice->iType & DPagingDevice::EData) != 0; + aDevice->iRequestPool = new DPagingRequestPool(KPagingRequestsPerDevice, writeReq); if(!aDevice->iRequestPool) { r = KErrNoMemory; @@ -2488,6 +2799,9 @@ if (K::MemModelAttributes & (EMemModelAttrRomPaging | EMemModelAttrCodePaging | EMemModelAttrDataPaging)) TheThrashMonitor.Start(); + + if (K::MemModelAttributes & EMemModelAttrDataPaging) + PageCleaner::Start(); exit: TRACEB(("Kern::InstallPagingDevice returns %d",r)); @@ -2637,3 +2951,32 @@ } + +// +// PageCleaningLock +// + +_LIT(KLitPageCleaningLock,"PageCleaningLock"); + +void PageCleaningLock::Init() + { + __NK_ASSERT_DEBUG(!ThePageCleaningLock); + TInt r = Kern::MutexCreate(ThePageCleaningLock, KLitPageCleaningLock, KMutexOrdPageOut); + __NK_ASSERT_ALWAYS(r == KErrNone); + } + +void PageCleaningLock::Lock() + { + Kern::MutexWait(*ThePageCleaningLock); + } + + +void PageCleaningLock::Unlock() + { + Kern::MutexSignal(*ThePageCleaningLock); + } + +TBool PageCleaningLock::IsHeld() + { + return ThePageCleaningLock->iCleanup.iThread == &Kern::CurrentThread(); + } diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mpager.h --- a/kernel/eka/memmodel/epoc/flexible/mmu/mpager.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mpager.h Tue May 11 17:28:22 2010 +0300 @@ -21,6 +21,14 @@ #ifndef MPAGER_H #define MPAGER_H +#include "mmu.h" +#include + +/** +Maximum number of pages to attempt to clean in one go. +*/ +const TInt KMaxPagesToClean = 4; + struct SVMCacheInfo; class DMemModelThread; class DMemoryMappingBase; @@ -61,9 +69,9 @@ if (!aPageInfo.IsDirty()) {// This is the first mapping to write to the page so increase the // dirty page count. - aPageInfo.SetWritable(); iNumberOfDirtyPages++; } + aPageInfo.SetWritable(); } FORCE_INLINE void SetClean(SPageInfo& aPageInfo) @@ -246,6 +254,31 @@ void UnreservePages(TUint& aCount); /** + Indicates whether there are any dirty pages available to be cleaned by #CleanSomePages. + + This is called by the page cleaner to work out whether it has any work to do. + + @return Whether there are any dirty pages in the oldest section of the live list. + */ + TBool HasPagesToClean(); + + /** + Attempt to clean one or more dirty pages in one go. + + Called by the page cleaner to clean pages and by PageInAllocPage when needs to steal a page from + the live list, but the oldest clean list is empty. + + May or may not succeed in acually cleaning any pages. + + @param aBackground Whether the activity should be ignored when determining whether the paging + device is busy. This is used by the page cleaner. + + @return The number of pages this method attempted to clean. If it returns zero, there were no + pages eligible to be cleaned. + */ + TInt CleanSomePages(TBool aBackground); + + /** Enumeration of instrumented paging events which only require the SPageInfo object as an argument. */ @@ -306,6 +339,15 @@ */ TInt DiscardPage(SPageInfo* aOldPageInfo, TUint aBlockZoneId, TBool aBlockRest); + /** + Attempt to discard the specified page and then allocate a page of type aPageType + in its place. + + @param aPageInfo The page info of the page to discard. + @param aPageType The new page type to allocate into aPageInfo's physical address. + */ + TInt DiscardAndAllocPage(SPageInfo* aPageInfo, TZonePageType aPageType); + /** Update any live list links to replace the old page with the new page. @@ -357,10 +399,23 @@ void RemovePage(SPageInfo* aPageInfo); /** + Try to remove the oldest page from the live page list and perform #StealPage. + + @param aPageInfoOut Set to the SPageInfo pointer for the stolen page if any. + + @return KErrNone on success, KErrInUse if stealing failed or 1 to indicate the the oldest page + was dirty and the PageCleaning mutex was not held. + + @pre MmuLock held + @post MmuLock left unchanged. + */ + TInt TryStealOldestPage(SPageInfo*& aPageInfoOut); + + /** Remove the oldest page from the live page list and perform #StealPage. @pre MmuLock held - @post MmuLock left unchanged. + @post MmuLock held (but may have been released by this function) */ SPageInfo* StealOldestPage(); @@ -371,6 +426,7 @@ if the page had been allocated by Mmu::AllocRam. @pre RamAlloc mutex held + @pre If the page is dirty the PageCleaning lock must be held. @pre MmuLock held @post MmuLock held (but may have been released by this function) */ @@ -428,6 +484,22 @@ SPageInfo* PageInAllocPage(Mmu::TRamAllocFlags aAllocFlags); /** + Called by CleanSomePages() to determine which pages should be cleaned. + + This deals with the complexity of page colouring, which means that pages can only be mapped at + certain locations. When cleaning multiple pages at once we need to find a set of pages that we + can map in memory sequentially. + + @pre MmuLock held + + @param aPageInfosOut Pointer to an array of SPageInfo pointers, which must be at least + KMaxPagesToClean long. This will be filled in to indicate the pages to clean. + + @return The numnber of pages to clean. + */ + TInt SelectPagesToClean(SPageInfo** aPageInfosOut); + + /** If the number of young pages exceeds that specified by iYoungOldRatio then a single page is made 'old'. Call this after adding a new 'young' page. @@ -521,13 +593,11 @@ TUint iYoungCount; /**< Number of young pages */ SDblQue iOldList; /**< Head of 'old' page list. */ TUint iOldCount; /**< Number of old pages */ -#ifdef _USE_OLDEST_LISTS SDblQue iOldestCleanList; /**< Head of 'oldestClean' page list. */ TUint iOldestCleanCount; /**< Number of 'oldestClean' pages */ SDblQue iOldestDirtyList; /**< Head of 'oldestDirty' page list. */ TUint iOldestDirtyCount; /**< Number of 'oldestDirty' pages */ TUint16 iOldOldestRatio; /**< Ratio of old pages to oldest to clean and dirty in the live page list*/ -#endif TUint iNumberOfFreePages; TUint iNumberOfDirtyPages; /**< The total number of dirty pages in the paging cache. Protected by MmuLock */ TUint iInitMinimumPageCount;/**< Initial value for iMinimumPageCount */ @@ -539,8 +609,10 @@ #ifdef __DEMAND_PAGING_BENCHMARKS__ public: - void RecordBenchmarkData(TPagingBenchmark aBm, TUint32 aStartTime, TUint32 aEndTime); + void RecordBenchmarkData(TPagingBenchmark aBm, TUint32 aStartTime, TUint32 aEndTime, TUint aCount); void ResetBenchmarkData(TPagingBenchmark aBm); + void ReadBenchmarkData(TPagingBenchmark aBm, SPagingBenchmarkInfo& aDataOut); + TSpinLock iBenchmarkLock; SPagingBenchmarkInfo iBenchmarkInfo[EMaxPagingBm]; #endif //__DEMAND_PAGING_BENCHMARKS__ }; @@ -551,12 +623,14 @@ #ifdef __DEMAND_PAGING_BENCHMARKS__ #define START_PAGING_BENCHMARK TUint32 _bmStart = NKern::FastCounter() -#define END_PAGING_BENCHMARK(bm) ThePager.RecordBenchmarkData(bm, _bmStart, NKern::FastCounter()) +#define END_PAGING_BENCHMARK(bm) ThePager.RecordBenchmarkData(bm, _bmStart, NKern::FastCounter(), 1) +#define END_PAGING_BENCHMARK_N(bm, n) ThePager.RecordBenchmarkData(bm, _bmStart, NKern::FastCounter(), (n)) #else #define START_PAGING_BENCHMARK #define END_PAGING_BENCHMARK(bm) +#define END_PAGING_BENCHMARK_N(bm, n) #endif // __DEMAND_PAGING_BENCHMARKS__ @@ -698,7 +772,7 @@ const TInt KPagingRequestsPerDevice = 2; -class DPagingRequest; +class DPoolPagingRequest; class DPageReadRequest; class DPageWriteRequest; @@ -708,9 +782,9 @@ class DPagingRequestPool : public DBase { public: - DPagingRequestPool(TUint aNumPageReadRequest,TUint aNumPageWriteRequest); + DPagingRequestPool(TUint aNumPageReadRequest, TBool aWriteRequest); DPageReadRequest* AcquirePageReadRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount); - DPageWriteRequest* AcquirePageWriteRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + DPageWriteRequest* AcquirePageWriteRequest(DMemoryObject** aMemory, TUint* aIndex, TUint aCount); private: ~DPagingRequestPool(); private: @@ -718,18 +792,18 @@ { public: TGroup(TUint aNumRequests); - DPagingRequest* FindCollision(DMemoryObject* aMemory, TUint aIndex, TUint aCount); - DPagingRequest* GetRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount); - void Signal(DPagingRequest* aRequest); + DPoolPagingRequest* FindCollisionContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + DPoolPagingRequest* GetRequest(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + void Signal(DPoolPagingRequest* aRequest); public: TUint iNumRequests; - DPagingRequest** iRequests; + DPoolPagingRequest** iRequests; SDblQue iFreeList; }; TGroup iPageReadRequests; - TGroup iPageWriteRequests; + DPageWriteRequest* iPageWriteRequest; - friend class DPagingRequest; + friend class DPoolPagingRequest; friend class DPageReadRequest; friend class DPageWriteRequest; }; @@ -741,45 +815,59 @@ class DPagingRequest : public SDblQueLink { public: - DPagingRequest(DPagingRequestPool::TGroup& aPoolGroup); - void Release(); - void Wait(); - void Signal(); - void SetUse(DMemoryObject* aMemory, TUint aIndex, TUint aCount); - TBool CheckUse(DMemoryObject* aMemory, TUint aIndex, TUint aCount); - TBool IsCollision(DMemoryObject* aMemory, TUint aIndex, TUint aCount); - TLinAddr MapPages(TUint aColour, TUint aCount, TPhysAddr* aPages); - void UnmapPages(TBool aIMBRequired); -public: - TThreadMessage iMessage; /**< Used by the media driver to queue requests */ - DMutex* iMutex; /**< A mutex for synchronisation and priority inheritance. */ - TInt iUsageCount;/**< How many threads are using or waiting for this object. */ - TLinAddr iBuffer; /**< A buffer to read compressed data into. Size is EMaxPages+1 pages.*/ -protected: - Mmu::TTempMapping iTempMapping; -private: - DPagingRequestPool::TGroup& iPoolGroup; - // used to identify memory request is used for... - DMemoryObject* iUseRegionMemory; - TUint iUseRegionIndex; - TUint iUseRegionCount; - }; - - -/** -Resources needed to service a page in request. -*/ -class DPageReadRequest : public DPagingRequest - { -public: - inline DPageReadRequest(DPagingRequestPool::TGroup& aPoolGroup) - : DPagingRequest(aPoolGroup) - {} - TInt Construct(); enum { EMaxPages = 4 }; + DPagingRequest(); + TLinAddr MapPages(TUint aColour, TUint aCount, TPhysAddr* aPages); + void UnmapPages(TBool aIMBRequired); + void SetUseContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + void SetUseDiscontiguous(DMemoryObject** aMemory, TUint* aIndex, TUint aCount); + void ResetUse(); + TBool CheckUseContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount); + TBool CheckUseDiscontiguous(DMemoryObject** aMemory, TUint* aIndex, TUint aCount); + TBool IsCollisionContiguous(DMemoryObject* aMemory, TUint aIndex, TUint aCount); +public: + DMutex* iMutex; /**< A mutex for synchronisation and priority inheritance. */ +protected: + Mmu::TTempMapping iTempMapping; +private: + // used to identify memory request is used for... + TUint iUseRegionCount; + DMemoryObject* iUseRegionMemory[EMaxPages]; + TUint iUseRegionIndex[EMaxPages]; + }; + + +__ASSERT_COMPILE(DPagingRequest::EMaxPages >= KMaxPagesToClean); + + +/** +A paging request that is part of a pool of similar request objects. +*/ +class DPoolPagingRequest : public DPagingRequest + { +public: + DPoolPagingRequest(DPagingRequestPool::TGroup& aPoolGroup); + void Release(); + void Wait(); + void Signal(); +public: + TInt iUsageCount; /**< How many threads are using or waiting for this object. */ +private: + DPagingRequestPool::TGroup& iPoolGroup; + }; + + +/** +Resources needed to service a page in request. +*/ +class DPageReadRequest : public DPoolPagingRequest + { +public: + DPageReadRequest(DPagingRequestPool::TGroup& aPoolGroup); + TInt Construct(); static TUint ReservedPagesRequired(); private: ~DPageReadRequest(); // can't delete @@ -804,18 +892,45 @@ class DPageWriteRequest : public DPagingRequest { public: - inline DPageWriteRequest(DPagingRequestPool::TGroup& aPoolGroup) - : DPagingRequest(aPoolGroup) - {} - TInt Construct(); - enum - { - EMaxPages = 1 - }; + DPageWriteRequest(); + void Release(); private: ~DPageWriteRequest(); // can't delete -private: - static TInt iAllocNext; + }; + + +/** +Class providing access to the mutex used to protect page cleaning operations; +this is the mutex DPager::iPageCleaningLock. +*/ +class PageCleaningLock + { +public: + /** + Acquire the lock. + The lock may be acquired multiple times by a thread, and will remain locked + until #Unlock has been used enough times to balance this. + */ + static void Lock(); + + /** + Release the lock. + + @pre The current thread has previously acquired the lock. + */ + static void Unlock(); + + /** + Return true if the current thread holds the lock. + This is used for debug checks. + */ + static TBool IsHeld(); + + /** + Create the lock. + Called by DPager::Init3(). + */ + static void Init(); }; diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mptalloc.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mptalloc.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mptalloc.cpp Tue May 11 17:28:22 2010 +0300 @@ -54,10 +54,6 @@ uses memory from #ThePager which will reclaim paged memory if necessary. Providing the live list always has #DPager::iMinYoungPages, this guarantees that handling page faults can never fail by running out of memory. - -TODO: In really pathological situations page table allocation can fail due to -being out of virtual address space to map the table, this needs to be prevented -from happening when handling demand paging faults. */ diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/flexible/mmu/mrom.cpp --- a/kernel/eka/memmodel/epoc/flexible/mmu/mrom.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/flexible/mmu/mrom.cpp Tue May 11 17:28:22 2010 +0300 @@ -530,15 +530,18 @@ TInt DRomMemoryManager::ReadPages(DMemoryObject* aMemory, TUint aIndex, TUint aCount, TPhysAddr* aPages, DPageReadRequest* aRequest) { - __NK_ASSERT_DEBUG(aRequest->CheckUse(aMemory,aIndex,aCount)); + __NK_ASSERT_DEBUG(aRequest->CheckUseContiguous(aMemory,aIndex,aCount)); + __ASSERT_CRITICAL; TLinAddr linAddr = aRequest->MapPages(aIndex,aCount,aPages); TInt r = KErrNone; + TThreadMessage message; const TInt readUnitShift = iDevice->iReadUnitShift; for(; aCount; ++aIndex, --aCount, linAddr+=KPageSize) { + START_PAGING_BENCHMARK; if(!iRomPageIndex) { @@ -548,7 +551,7 @@ // by readUnitShift. const TInt dataOffset = aIndex << KPageShift; START_PAGING_BENCHMARK; - r = iDevice->Read( const_cast(&aRequest->iMessage), + r = iDevice->Read( &message, linAddr, dataOffset >> readUnitShift, KPageSize >> readUnitShift, DPagingDevice::EDriveRomPaging); __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocated memory, therefore can't fail with KErrNoMemory @@ -571,13 +574,12 @@ __NK_ASSERT_ALWAYS(romPageInfo->iPagingAttributes & SRomPageInfo::EPageable); // Read data for page... - TThreadMessage* msg = const_cast(&aRequest->iMessage); const TLinAddr buffer = aRequest->iBuffer; const TUint readStart = dataOffset >> readUnitShift; const TUint readSize = ((dataOffset + dataSize - 1) >> readUnitShift) - readStart + 1; __NK_ASSERT_DEBUG((readSize << readUnitShift) <= (DPageReadRequest::EMaxPages << KPageShift)); START_PAGING_BENCHMARK; - r = iDevice->Read(msg, buffer, readStart, readSize, DPagingDevice::EDriveRomPaging); + r = iDevice->Read(&message, buffer, readStart, readSize, DPagingDevice::EDriveRomPaging); __NK_ASSERT_DEBUG(r!=KErrNoMemory); // not allowed to allocated memory, therefore can't fail with KErrNoMemory END_PAGING_BENCHMARK(EPagingBmReadMedia); if(r==KErrNone) diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/mmubase/mmubase.cpp --- a/kernel/eka/memmodel/epoc/mmubase/mmubase.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/mmubase/mmubase.cpp Tue May 11 17:28:22 2010 +0300 @@ -483,7 +483,7 @@ TInt MmuBase::AllocPhysicalRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign) { __KTRACE_OPT(KMMU,Kern::Printf("Mmu::AllocPhysicalRam() size=%x align=%d",aSize,aAlign)); - TInt r=AllocContiguousRam(aSize, aPhysAddr, EPageFixed, aAlign); + TInt r=AllocContiguousRam(aSize, aPhysAddr, aAlign); if (r!=KErrNone) { iAllocFailed=ETrue; @@ -516,7 +516,7 @@ TInt MmuBase::ZoneAllocPhysicalRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign) { __KTRACE_OPT(KMMU,Kern::Printf("Mmu::ZoneAllocPhysicalRam() size=0x%x align=%d", aSize, aAlign)); - TInt r = ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, EPageFixed, aAlign); + TInt r = ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aAlign); if (r!=KErrNone) { iAllocFailed=ETrue; @@ -714,20 +714,19 @@ } -TInt MmuBase::AllocContiguousRam(TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aPageType, TInt aAlign, TUint aBlockedZoneId, TBool aBlockRest) +TInt MmuBase::AllocContiguousRam(TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign) { #ifdef _DEBUG if(K::CheckForSimulatedAllocFail()) return KErrNoMemory; #endif - __NK_ASSERT_DEBUG(aPageType == EPageFixed); TUint contigPages = (aSize + KPageSize - 1) >> KPageShift; - TInt r = iRamPageAllocator->AllocContiguousRam(contigPages, aPhysAddr, aPageType, aAlign, aBlockedZoneId, aBlockRest); + TInt r = iRamPageAllocator->AllocContiguousRam(contigPages, aPhysAddr, aAlign); if (r == KErrNoMemory && contigPages > KMaxFreeableContiguousPages) {// Allocation failed but as this is a large allocation flush the RAM cache // and reattempt the allocation as large allocation wouldn't discard pages. iRamCache->FlushAll(); - r = iRamPageAllocator->AllocContiguousRam(contigPages, aPhysAddr, aPageType, aAlign, aBlockedZoneId, aBlockRest); + r = iRamPageAllocator->AllocContiguousRam(contigPages, aPhysAddr, aAlign); } return r; } @@ -739,16 +738,15 @@ @param aZoneIdCount The number of IDs listed in aZoneIdList @param aSize The number of bytes to allocate @param aPhysAddr Will receive the physical base address of the allocated RAM -@param aPageType The type of the pages being allocated @param aAlign The log base 2 alginment required */ -TInt MmuBase::ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aPageType, TInt aAlign) +TInt MmuBase::ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign) { #ifdef _DEBUG if(K::CheckForSimulatedAllocFail()) return KErrNoMemory; #endif - return iRamPageAllocator->ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aPageType, aAlign); + return iRamPageAllocator->ZoneAllocContiguousRam(aZoneIdList, aZoneIdCount, aSize, aPhysAddr, aAlign); } SPageInfo* SPageInfo::SafeFromPhysAddr(TPhysAddr aAddress) @@ -5225,6 +5223,12 @@ // Misc DPagingDevice methods +EXPORT_C NFastMutex* DPagingDevice::NotificationLock() + { + // use the system lock + return &TheScheduler.iLock; + } + EXPORT_C void DPagingDevice::NotifyIdle() { // Not used on this memory model diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp --- a/kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/mmubase/ramalloc.cpp Tue May 11 17:28:22 2010 +0300 @@ -354,13 +354,13 @@ aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard])); Panic(EZonesCountErr); } - __ASSERT_DEBUG(free == (TUint32)aZone->iBma[KBmaAllPages]->iAvail, Panic(EAllocRamPagesInconsistent)); __KTRACE_OPT(KMMU2,Kern::Printf("ZoneFreePages - aCount %x free %x, alloc %x",aCount,free,alloc)); __KTRACE_OPT(KMMU2,Kern::Printf("Alloc Unk %x Fx %x Mv %x Dis %x",aZone->iAllocPages[EPageUnknown], aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard])); - if (iAllowBmaVerify) + if (!iContiguousReserved) { + __ASSERT_DEBUG(free == (TUint32)aZone->iBma[KBmaAllPages]->iAvail, Panic(EAllocRamPagesInconsistent)); TBitMapAllocator& bmaType = *(aZone->iBma[(aType != EPageUnknown)? aType : EPageFixed]); TUint allocPages; if (aType == EPageFixed || aType == EPageUnknown) @@ -495,13 +495,13 @@ aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard])); Panic(EZonesCountErr); } - __ASSERT_DEBUG(free == (TUint32)aZone->iBma[KBmaAllPages]->iAvail, Panic(EAllocRamPagesInconsistent)); __KTRACE_OPT(KMMU2,Kern::Printf("ZoneFreePages - aCount %x free %x, alloc %x",aCount,free,alloc)); __KTRACE_OPT(KMMU2,Kern::Printf("Alloc Unk %x Fx %x Mv %x Dis %x",aZone->iAllocPages[EPageUnknown], aZone->iAllocPages[EPageFixed], aZone->iAllocPages[EPageMovable],aZone->iAllocPages[EPageDiscard])); - if (iAllowBmaVerify) + if (!iContiguousReserved) { + __ASSERT_DEBUG(free == (TUint32)aZone->iBma[KBmaAllPages]->iAvail, Panic(EAllocRamPagesInconsistent)); TBitMapAllocator& bmaType = *(aZone->iBma[(aType != EPageUnknown)? aType : EPageFixed]); TUint allocPages; if (aType == EPageFixed || aType == EPageUnknown) @@ -968,7 +968,7 @@ // Temporarily fill preference list so SetPhysicalRamState can succeed #ifdef _DEBUG // Block bma verificaitons as bma and alloc counts aren't consistent yet. - iAllowBmaVerify = EFalse; + iContiguousReserved = 1; #endif const SZone* const lastZone = iZones + iNumZones; zone = iZones; @@ -984,7 +984,7 @@ } #ifdef _DEBUG // Only now is it safe to enable bma verifications - iAllowBmaVerify = ETrue; + iContiguousReserved = 0; #endif /////////////////////////////////////////////////////////////////////////// @@ -1135,6 +1135,7 @@ } } + TInt DRamAllocator::MarkPageAllocated(TPhysAddr aAddr, TZonePageType aType) { __KTRACE_OPT(KMMU,Kern::Printf("DRamAllocator::MarkPageAllocated %08x",aAddr)); @@ -1160,7 +1161,12 @@ return KErrAlreadyExists; // page is already allocated } bmaAll.Alloc(n,1); - bmaType.Alloc(n,1); + if (bmaType.NotAllocated(n,1)) + bmaType.Alloc(n,1); +#ifdef _DEBUG + else // Allow this page to already be reserved in bmaType as AllocContiguousRam() may have done this. + __NK_ASSERT_DEBUG(aType == EPageFixed); +#endif --iTotalFreeRamPages; ZoneAllocPages(z, 1, aType); __KTRACE_OPT(KMMU,Kern::Printf("Total free RAM pages now = %d",iTotalFreeRamPages)); @@ -1171,6 +1177,7 @@ return KErrNone; } + TInt DRamAllocator::FreeRamPage(TPhysAddr aAddr, TZonePageType aType) { __KTRACE_OPT(KMMU,Kern::Printf("FreeRamPage %08x",aAddr)); @@ -1201,17 +1208,27 @@ __KTRACE_OPT(KMMU2,Kern::Printf("Zone index %d page index %04x",z-iZones,n)); TBitMapAllocator& bmaAll = *(z->iBma[KBmaAllPages]); TBitMapAllocator& bmaType = *(z->iBma[aType]); - bmaAll.Free(n); + bmaType.Free(n); - ++iTotalFreeRamPages; - ZoneFreePages(z, 1, aType); - + if (iContiguousReserved && aType != EPageFixed && z->iBma[EPageFixed]->NotFree(n, 1)) + {// This page has been reserved by AllocContiguous() so don't free it + // but allocate it as fixed. + ZoneFreePages(z, 1, aType); + ZoneAllocPages(z, 1, EPageFixed); + } + else + { + bmaAll.Free(n); + ++iTotalFreeRamPages; + ZoneFreePages(z, 1, aType); + } #ifdef BTRACE_RAM_ALLOCATOR BTrace8(BTrace::ERamAllocator, BTrace::ERamAllocFreePage, aType, aAddr); #endif return KErrNone; } + void DRamAllocator::FreeRamPages(TPhysAddr* aPageList, TInt aNumPages, TZonePageType aType) { __KTRACE_OPT(KMMU,Kern::Printf("FreeRamPages count=%08x",aNumPages)); @@ -1259,11 +1276,37 @@ pa += KPageSize; } __KTRACE_OPT(KMMU2,Kern::Printf("%d consecutive pages, zp_rem=%x, %d remaining pages",n,zp_rem,aNumPages)); - bmaAll.Free(ix,n); TBitMapAllocator& bmaType = *(z->iBma[aType]); bmaType.Free(ix,n); - iTotalFreeRamPages += n; - ZoneFreePages(z, n, aType); + + if (iContiguousReserved && aType != EPageFixed) + {// See if a page has been reserved by AllocContiguous() in this range. + TUint pagesFreed = 0; + TUint allocStart = ix; + TUint freeOffset = ix; + TUint endOffset = ix + n - 1; + while (freeOffset <= endOffset) + { + TUint runLength = NextAllocatedRun(z, allocStart, endOffset, EPageFixed); + if (allocStart > freeOffset) + { + TUint freed = allocStart - freeOffset; + bmaAll.Free(freeOffset, freed); + pagesFreed += freed; + } + allocStart += runLength; + freeOffset = allocStart; + } + iTotalFreeRamPages += pagesFreed; + ZoneFreePages(z, n, aType); + ZoneAllocPages(z, n - pagesFreed, EPageFixed); + } + else + { + bmaAll.Free(ix,n); + iTotalFreeRamPages += n; + ZoneFreePages(z, n, aType); + } #ifdef BTRACE_RAM_ALLOCATOR BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocFreePages, aType, n, first_pa); #endif @@ -1273,6 +1316,7 @@ #endif } + /** Attempt to clear upto the required amount of discardable or movable pages from the RAM zone. @@ -1464,7 +1508,7 @@ {// Allocating as part of a general defragmentation and // can't allocate without using a RAM zone less preferable than // the current least prefeable RAM zone with movable and/or - //discardable. + // discardable. __NK_ASSERT_DEBUG(numMissing); goto exit; } @@ -1679,29 +1723,173 @@ return r; } + +#if !defined(__MEMMODEL_MULTIPLE__) && !defined(__MEMMODEL_MOVING__) +void DRamAllocator::BlockContiguousRegion(TPhysAddr aAddrBase, TUint aNumPages) + { + // Shouldn't be asked to block zero pages, addrEndPage would be wrong if we did. + __NK_ASSERT_DEBUG(aNumPages); + TPhysAddr addr = aAddrBase; + TPhysAddr addrEndPage = aAddrBase + ((aNumPages - 1) << KPageShift); + TInt tmpOffset; + SZone* endZone = GetZoneAndOffset(addrEndPage, tmpOffset); + SZone* tmpZone; + do + { + tmpZone = GetZoneAndOffset(addr, tmpOffset); + __NK_ASSERT_DEBUG(tmpZone != NULL); + TUint runLength = (addrEndPage < tmpZone->iPhysEnd)? + ((addrEndPage - addr) >> KPageShift) + 1: + tmpZone->iPhysPages - tmpOffset; + TUint reserved = tmpZone->iBma[KBmaAllPages]->SelectiveAlloc(tmpOffset, runLength); + if (reserved) + { +#ifdef _DEBUG + TUint runEnd = tmpOffset + runLength; + TUint free = 0; + for (TUint i = tmpOffset; i < runEnd; i++) + if (tmpZone->iBma[EPageMovable]->NotAllocated(i,1) && tmpZone->iBma[EPageDiscard]->NotAllocated(i,1)) + free++; + __NK_ASSERT_DEBUG(free == reserved); +#endif + ZoneAllocPages(tmpZone, reserved, EPageFixed); + iTotalFreeRamPages -= reserved; + } + tmpZone->iBma[EPageFixed]->Alloc(tmpOffset, runLength); + addr = tmpZone->iPhysEnd + 1; + } + while (tmpZone != endZone); + } + + +FORCE_INLINE void DRamAllocator::UnblockSetAllocRuns( TUint& aOffset1, TUint& aOffset2, + TUint aRunLength1, TUint aRunLength2, + TUint& aAllocLength, TUint& aAllocStart) + { + aAllocStart = aOffset1; + aAllocLength = aRunLength1; + aOffset1 += aAllocLength; + if (aOffset1 == aOffset2) + { + aAllocLength += aRunLength2; + aOffset2 += aRunLength2; + aOffset1 = aOffset2; + } + } + + +void DRamAllocator::UnblockContiguousRegion(TPhysAddr aAddrBase, TUint aNumPages) + { + // Shouldn't be asked to unblock zero pages, addrEndPage would be wrong if we did. + __NK_ASSERT_DEBUG(aNumPages); + TPhysAddr addr = aAddrBase; + TPhysAddr addrEndPage = aAddrBase + ((aNumPages - 1) << KPageShift); + TInt tmpOffset; + SZone* endZone = GetZoneAndOffset(addrEndPage, tmpOffset); + SZone* tmpZone; + do + { + tmpZone = GetZoneAndOffset(addr, tmpOffset); + __NK_ASSERT_DEBUG(tmpZone != NULL); + TUint runLength = (addrEndPage < tmpZone->iPhysEnd)? + ((addrEndPage - addr) >> KPageShift) + 1: + tmpZone->iPhysPages - tmpOffset; + TUint unreserved = 0; + TUint runEnd = tmpOffset + runLength - 1; + TUint freeOffset = tmpOffset; + TUint discardOffset = freeOffset; + TUint movableOffset = freeOffset; + __KTRACE_OPT(KMMU2, Kern::Printf("freeOff %d, runEnd %d", freeOffset, runEnd)); + while (freeOffset <= runEnd) + { + TUint discardRun; + TUint movableRun; + discardRun = NextAllocatedRun(tmpZone, discardOffset, runEnd, EPageDiscard); + movableRun = NextAllocatedRun(tmpZone, movableOffset, runEnd, EPageMovable); + TUint allocLength; + TUint allocStart; + __KTRACE_OPT(KMMU2, Kern::Printf("disOff %d len %d movOff %d len %d", discardOffset, discardRun, movableOffset, movableRun)); + if (discardOffset < movableOffset) + UnblockSetAllocRuns(discardOffset, movableOffset, discardRun, movableRun, allocLength, allocStart); + else + UnblockSetAllocRuns(movableOffset, discardOffset, movableRun, discardRun, allocLength, allocStart); + + if (allocStart > freeOffset) + { + unreserved += allocStart - freeOffset; + tmpZone->iBma[KBmaAllPages]->Free(freeOffset, allocStart - freeOffset); + __NK_ASSERT_DEBUG( !tmpZone->iBma[EPageMovable]->NotFree(freeOffset, allocStart - freeOffset) && + !tmpZone->iBma[EPageDiscard]->NotFree(freeOffset, allocStart - freeOffset)); + } + __KTRACE_OPT(KMMU2, Kern::Printf("disOff %d len %d movOff %d len %d start %d len %d", discardOffset, discardRun, movableOffset, movableRun, allocStart, allocLength)); + freeOffset = allocStart + allocLength; + __KTRACE_OPT(KMMU2, Kern::Printf("freeOff %d", freeOffset)); + } + tmpZone->iBma[EPageFixed]->Free(tmpOffset, runLength); + ZoneFreePages(tmpZone, unreserved, EPageFixed); + iTotalFreeRamPages += unreserved; + addr = tmpZone->iPhysEnd + 1; + } + while (tmpZone != endZone); + } + + +TBool DRamAllocator::ClearContiguousRegion(TPhysAddr aAddrBase, TPhysAddr aZoneBase, TUint aNumPages, TInt& aOffset) + { + TPhysAddr addr = aAddrBase; + TPhysAddr addrEnd = aAddrBase + (aNumPages << KPageShift); + TInt contigOffset = 0; + SZone* contigZone = GetZoneAndOffset(addr, contigOffset); + for (; addr != addrEnd; addr += KPageSize, contigOffset++) + { + if (contigZone->iPhysEnd < addr) + { + contigZone = GetZoneAndOffset(addr, contigOffset); + __NK_ASSERT_DEBUG(contigZone != NULL); + } + + __NK_ASSERT_DEBUG(contigZone != NULL); + __NK_ASSERT_DEBUG(contigZone->iBma[EPageFixed]->NotFree(contigOffset, 1)); + __NK_ASSERT_DEBUG(SPageInfo::SafeFromPhysAddr(addr) != NULL); + + // WARNING - This may flash the ram alloc mutex. + TInt exRet = M::MoveAndAllocPage(addr, EPageFixed); + if (exRet != KErrNone) + {// This page couldn't be moved or discarded so + // restart the search the page after this one. + __KTRACE_OPT(KMMU2, Kern::Printf("ContigMov fail contigOffset 0x%x exRet %d", contigOffset, exRet)); + aOffset = (addr < aZoneBase)? 0 : contigOffset + 1; + break; + } + } + return addr == addrEnd; + } + + /** Search through the zones for the requested contiguous RAM, first in preference order then, if that fails, in address order. +No support for non-fixed pages as this will discard and move pages if required. + @param aNumPages The number of contiguous pages to find @param aPhysAddr Will contain the base address of any contiguous run if found -@param aType The page type of the memory to be allocated @param aAlign Alignment specified as the alignment shift -@param aBlockedZoneId The ID of a zone that can't be allocated into, by default this has no effect -@param aBlockRest Set to ETrue to stop allocation as soon as aBlockedZoneId is reached in preference ordering. EFalse otherwise. @return KErrNone on success, KErrNoMemory otherwise */ -TInt DRamAllocator::AllocContiguousRam(TUint aNumPages, TPhysAddr& aPhysAddr, TZonePageType aType, TInt aAlign, TUint aBlockedZoneId, TBool aBlockRest) +TInt DRamAllocator::AllocContiguousRam(TUint aNumPages, TPhysAddr& aPhysAddr, TInt aAlign) { __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam size %08x align %d",aNumPages,aAlign)); M::RamAllocIsLocked(); - // No support for non-fixed pages as this will discard and move - // pages if required. - __NK_ASSERT_DEBUG(aType == EPageFixed); + if ((TUint)aNumPages > iTotalFreeRamPages + M::NumberOfFreeDpPages()) + {// Not enough free space and not enough freeable pages. + return KErrNoMemory; + } + TInt alignWrtPage = Max(aAlign - KPageShift, 0); TUint32 alignmask = (1u << alignWrtPage) - 1; @@ -1716,7 +1904,124 @@ TInt offset = 0; iZoneTmpAddrIndex = -1; iZoneTmpPrefLink = iZonePrefList.First(); - while (NextAllocZone(zone, searchState, aType, aBlockedZoneId, aBlockRest)) + while (NextAllocZone(zone, searchState, EPageFixed, KRamZoneInvalidId, EFalse)) + { + // Be sure to start from scratch if zone not contiguous with previous zone + if (prevZone && (zone->iPhysBase == 0 || (zone->iPhysBase - 1) != prevZone->iPhysEnd)) + { + carryAll = 0; + carryImmov = 0; + } + prevZone = zone; + TBitMapAllocator& bmaAll = *(zone->iBma[KBmaAllPages]); + base = TInt(zone->iPhysBase >> KPageShift); + TInt runLength; + __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: base=%08x carryAll=%08x offset=%08x", base, carryAll, offset)); + offset = bmaAll.AllocAligned(aNumPages, alignWrtPage, base, EFalse, carryAll, runLength); + __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: offset=%08x", offset)); + + if (offset >= 0) + { + // Have found enough contiguous pages so return address of physical page + // at the start of the region + aPhysAddr = TPhysAddr((base + offset - carryAll + alignmask) & ~alignmask) << KPageShift; + MarkPagesAllocated(aPhysAddr, aNumPages, EPageFixed); + + __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam returns %08x",aPhysAddr)); +#ifdef BTRACE_RAM_ALLOCATOR + BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, EPageFixed, aNumPages, aPhysAddr); +#endif + return KErrNone; + } + // No run found when looking in just the free pages so see if this + // RAM zone could be used if pages where moved or discarded. + TBitMapAllocator& bmaImmov = *(zone->iBma[EPageFixed]); + offset = 0; // Clear so searches whole of fixed BMA on the first pass. + do + { + __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: base=%08x carryImmov=%08x offset=%08x", base, carryImmov, offset)); + offset = bmaImmov.AllocAligned(aNumPages, alignWrtPage, base, EFalse, carryImmov, runLength, offset); + __KTRACE_OPT(KMMU,Kern::Printf("AllocAligned: offset=%08x", offset)); + if (offset >= 0) + {// Have found a run in immovable page bma so attempt to clear + // it for the allocation. + TPhysAddr addrBase = TPhysAddr((base + offset - carryImmov + alignmask) & ~alignmask) << KPageShift; + __KTRACE_OPT(KMMU2, Kern::Printf(">AllocContig fix run 0x%08x - 0x%08x 0x%x", addrBase, addrBase + (aNumPages << KPageShift), TheCurrentThread)); + + // Block the contiguous region from being allocated. + iContiguousReserved++; + BlockContiguousRegion(addrBase, aNumPages); + if (ClearContiguousRegion(addrBase, zone->iPhysBase, aNumPages, offset)) + {// Cleared all the required pages. + // Return address of physical page at the start of the region. + iContiguousReserved--; + aPhysAddr = addrBase; + __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam returns %08x",aPhysAddr)); +#ifdef BTRACE_RAM_ALLOCATOR + BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, EPageFixed, aNumPages, aPhysAddr); +#endif + __KTRACE_OPT(KMMU2, Kern::Printf("= 0 && (TUint)offset < zone->iPhysPages); + } + return KErrNoMemory; + } + +#else + +/** +Search through the zones for the requested contiguous RAM, first in preference +order then, if that fails, in address order. + +No support for non-fixed pages as this will discard and move pages if required. + +@param aNumPages The number of contiguous pages to find +@param aPhysAddr Will contain the base address of any contiguous run if found +@param aAlign Alignment specified as the alignment shift + +@return KErrNone on success, KErrNoMemory otherwise +*/ +TInt DRamAllocator::AllocContiguousRam(TUint aNumPages, TPhysAddr& aPhysAddr, TInt aAlign) + { + __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam size %08x align %d",aNumPages,aAlign)); + + M::RamAllocIsLocked(); + + TInt alignWrtPage = Max(aAlign - KPageShift, 0); + TUint32 alignmask = (1u << alignWrtPage) - 1; + + // Attempt to find enough pages searching in preference order first then + // in address order + TZoneSearchState searchState = EZoneSearchPref; + SZone* zone; + SZone* prevZone = NULL; + TInt carryAll = 0; // Carry for all pages bma, clear to start new run. + TInt carryImmov = 0; // Carry for immovable pages bma, clear to start new run. + TInt base = 0; + TInt offset = 0; + iZoneTmpAddrIndex = -1; + iZoneTmpPrefLink = iZonePrefList.First(); + while (NextAllocZone(zone, searchState, EPageFixed, KRamZoneInvalidId, EFalse)) { // Be sure to start from scratch if zone not contiguous with previous zone if (prevZone && (zone->iPhysBase == 0 || (zone->iPhysBase - 1) != prevZone->iPhysEnd)) @@ -1736,11 +2041,11 @@ {// Have found enough contiguous pages so return address of physical page // at the start of the region aPhysAddr = TPhysAddr((base + offset - carryAll + alignmask) & ~alignmask) << KPageShift; - MarkPagesAllocated(aPhysAddr, aNumPages, aType); + MarkPagesAllocated(aPhysAddr, aNumPages, EPageFixed); __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam returns %08x",aPhysAddr)); #ifdef BTRACE_RAM_ALLOCATOR - BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, aType, aNumPages, aPhysAddr); + BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, EPageFixed, aNumPages, aPhysAddr); #endif return KErrNone; } @@ -1788,12 +2093,11 @@ contigZone = GetZoneAndOffset(addr, contigOffset); __NK_ASSERT_DEBUG(contigZone != NULL); } -#ifdef _DEBUG // This page shouldn't be allocated as fixed, only movable or discardable. + // This page shouldn't be allocated as fixed, only movable or discardable. __NK_ASSERT_DEBUG(contigZone != NULL); __NK_ASSERT_DEBUG(contigZone->iBma[EPageFixed]->NotAllocated(contigOffset, 1)); - SPageInfo* pageInfo = SPageInfo::SafeFromPhysAddr(addr); - __NK_ASSERT_DEBUG(pageInfo != NULL); -#endif + __NK_ASSERT_DEBUG(SPageInfo::SafeFromPhysAddr(addr) != NULL); + TPhysAddr newAddr; TInt moveRet = M::MovePage(addr, newAddr, contigZone->iId, EFalse); if (moveRet != KErrNone && moveRet != KErrNotFound) @@ -1827,11 +2131,11 @@ {// Cleared all the required pages so allocate them. // Return address of physical page at the start of the region. aPhysAddr = addrBase; - MarkPagesAllocated(aPhysAddr, aNumPages, aType); + MarkPagesAllocated(aPhysAddr, aNumPages, EPageFixed); __KTRACE_OPT(KMMU,Kern::Printf("AllocContiguousRam returns %08x",aPhysAddr)); #ifdef BTRACE_RAM_ALLOCATOR - BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, aType, aNumPages, aPhysAddr); + BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocContiguousRam, EPageFixed, aNumPages, aPhysAddr); #endif return KErrNone; } @@ -1844,6 +2148,7 @@ } return KErrNoMemory; } +#endif // !defined(__MEMODEL_MULTIPLE__) || !defined(__MEMODEL_MOVING__) /** @@ -1858,19 +2163,17 @@ @param aZoneIdCount The number of the IDs listed by aZoneIdList. @param aSize The number of contiguous bytes to find @param aPhysAddr Will contain the base address of the contiguous run if found -@param aType The page type of the memory to be allocated @param aAlign Alignment specified as the alignment shift @return KErrNone on success, KErrNoMemory if allocation couldn't succeed or the RAM zone has the KRamZoneFlagNoAlloc flag set. KErrArgument if a zone of aZoneIdList exists or if aSize is larger than the size of the zone. */ -TInt DRamAllocator::ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TZonePageType aType, TInt aAlign) +TInt DRamAllocator::ZoneAllocContiguousRam(TUint* aZoneIdList, TUint aZoneIdCount, TInt aSize, TPhysAddr& aPhysAddr, TInt aAlign) { __KTRACE_OPT(KMMU,Kern::Printf("ZoneAllocContiguousRam zones 0x%x size 0x%08x align %d",aZoneIdCount, aSize, aAlign)); M::RamAllocIsLocked(); - __NK_ASSERT_DEBUG(aType == EPageFixed); TUint numPages = (aSize + KPageSize - 1) >> KPageShift; @@ -1930,11 +2233,11 @@ // Have found enough contiguous pages so mark the pages allocated and // return address of physical page at the start of the region. aPhysAddr = TPhysAddr((base + offset - carry + alignmask) & ~alignmask) << KPageShift; - MarkPagesAllocated(aPhysAddr, numPages, aType); + MarkPagesAllocated(aPhysAddr, numPages, EPageFixed); __KTRACE_OPT(KMMU,Kern::Printf("ZoneAllocContiguousRam returns %08x",aPhysAddr)); #ifdef BTRACE_RAM_ALLOCATOR - BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocZoneContiguousRam, aType, numPages, aPhysAddr); + BTrace12(BTrace::ERamAllocator, BTrace::ERamAllocZoneContiguousRam, EPageFixed, numPages, aPhysAddr); #endif return KErrNone; } @@ -2106,34 +2409,34 @@ // Makes things simpler for bma selection. __NK_ASSERT_DEBUG(aType != EPageUnknown); - if (aOffset >= aZone->iPhysPages) + TUint zoneEndOffset = aZone->iPhysPages - 1; + if (aOffset > zoneEndOffset) {// Starting point is outside the zone return KErrArgument; } - TUint offset = aOffset; - TUint endOffset = aZone->iPhysPages; - TUint endOffsetAligned = endOffset & KWordAlignMask; + TUint wordIndex = aOffset >> 5; + TUint endWordIndex = zoneEndOffset >> 5; // Select the BMA to search, TUint bmaIndex = (aType == EPageTypes)? KBmaAllPages : aType; - TUint32* map = &(aZone->iBma[bmaIndex]->iMap[offset >> 5]); - TUint32 bits = *map++; + TUint32* map = &(aZone->iBma[bmaIndex]->iMap[wordIndex]); + TUint32* mapEnd = &(aZone->iBma[bmaIndex]->iMap[endWordIndex]); + TUint32 bits = *map; // Set bits for pages before 'offset' (i.e. ones we want to ignore)... - bits |= ~(KMaxTUint32 >> (offset & ~KWordAlignMask)); + bits |= ~(KMaxTUint32 >> (aOffset & ~KWordAlignMask)); // Find the first bit map word from aOffset in aZone with allocated pages - while (bits == KMaxTUint32 && offset < endOffsetAligned) + while (bits == KMaxTUint32 && map < mapEnd) { - bits = *map++; - offset = (offset + 32) & KWordAlignMask; + bits = *++map; } - if (offset >= endOffsetAligned && endOffset != endOffsetAligned) + if (map == mapEnd) {// Have reached the last bit mask word so set the bits that are // outside of the zone so that they are ignored. - bits |= KMaxTUint32 >> (endOffset - endOffsetAligned); + bits |= (KMaxTUint32 >> (zoneEndOffset & ~KWordAlignMask)) >> 1; } if (bits == KMaxTUint32) @@ -2143,25 +2446,104 @@ // Now we have bits with allocated pages in it so determine the exact // offset of the next allocated page - TUint32 mask = 0x80000000 >> (offset & ~KWordAlignMask); - while (bits & mask) - { - mask >>= 1; - offset++; - } - - if (offset >= endOffset) - {// Reached the end of the zone without finding an allocated page after aOffset - return KErrNotFound; - } - - // Should definitely have found an allocated page within aZone's pages - __NK_ASSERT_DEBUG(mask != 0 && !(bits & mask) && offset < aZone->iPhysPages); - - aOffset = offset; + TInt msOne = __e32_find_ms1_32(~bits); + __NK_ASSERT_DEBUG(msOne >= 0); // Must have at least one allocated page in the word. + TUint msOneOffset = 31 - msOne; + aOffset = ((map - aZone->iBma[bmaIndex]->iMap) << 5) + msOneOffset; return KErrNone; } + +/** +Get the next run of pages in this zone that are allocated after aOffset. + +@param aZone The zone to find the next allocated page in. +@param aOffset On entry this is the offset from which the next allocated + page in the zone should be found, on return it will be the offset + of the next allocated page. +@param aEndOffset The last offset within this RAM zone to check for allocated runs. +@return The length of any run found, KErrNotFound if no more pages in +the zone after aOffset are allocated, KErrArgument if aOffset is outside the zone. +*/ +TInt DRamAllocator::NextAllocatedRun(SZone* aZone, TUint& aOffset, TUint aEndOffset, TZonePageType aType) const + { + const TUint KWordAlignMask = KMaxTUint32 << 5; + + M::RamAllocIsLocked(); + + __NK_ASSERT_DEBUG(aZone - iZones < (TInt)iNumZones); + // Makes things simpler for bma selection. + __NK_ASSERT_DEBUG(aType != EPageUnknown); + + if (aOffset > aEndOffset) + {// UnblockContiguous() has already searched the whole range for this page type. + return 0; + } + + TUint wordIndex = aOffset >> 5; + TUint endWordIndex = aEndOffset >> 5; + + // Select the BMA to search, + TUint bmaIndex = (aType == EPageTypes)? KBmaAllPages : aType; + TUint32* map = &(aZone->iBma[bmaIndex]->iMap[wordIndex]); + TUint32* mapEnd = &(aZone->iBma[bmaIndex]->iMap[endWordIndex]); + TUint32 bits = *map; + + // Set bits for pages before 'offset' (i.e. ones we want to ignore)... + bits |= ~(KMaxTUint32 >> (aOffset & ~KWordAlignMask)); + + // Find the first bit map word from aOffset in aZone with allocated pages + while (bits == KMaxTUint32 && map < mapEnd) + { + bits = *++map; + } + + if (map == mapEnd) + {// Have reached the last bit mask word so set the bits that are + // outside of the range so that they are ignored. + bits |= (KMaxTUint32 >> (aEndOffset & ~KWordAlignMask)) >> 1; + } + + if (bits == KMaxTUint32) + {// No allocated pages found in the range. + aOffset = aEndOffset + 1; + return 0; + } + + // Now we have bits with allocated pages in it so determine the exact + // offset of the next allocated page + TInt msOne = __e32_find_ms1_32(~bits); + __NK_ASSERT_DEBUG(msOne >= 0); // Must have at least one allocated page in the word. + TUint msOneOffset = 31 - msOne; + aOffset = ((map - aZone->iBma[bmaIndex]->iMap) << 5) + msOneOffset; + TUint32* runWord = map; + + if (map < mapEnd && __e32_bit_count_32(~bits) == msOne + 1) + {// The whole of the region in this word is allocated. + // Find the next word which isn't completely allocated within the range. + do + { + bits = *++map; + } + while (!bits && map < mapEnd); + } + + // Clear any bits before the run so can get next free from __e32_find_msl_32(). + if (runWord == map) + bits &= KMaxTUint32 >> (aOffset & ~KWordAlignMask); + TInt msFree = __e32_find_ms1_32(bits); + __NK_ASSERT_DEBUG(msFree >= 0 || map == mapEnd); + TUint msFreeOffset = (msFree >= 0)? 31 - msFree : 32; + TUint endIndex = map - aZone->iBma[bmaIndex]->iMap; + TUint runEnd = (endIndex << 5) + msFreeOffset; + if (runEnd > aEndOffset + 1) // Ensure we don't go past the range. + runEnd = aEndOffset + 1; + __NK_ASSERT_DEBUG(runEnd > aOffset); + + return runEnd - aOffset; + } + + /** See if any of the least preferable RAM zones can be emptied. If they can then initialise the allocator for a general defragmentation operation. diff -r 41f0cfe18c80 -r c734af59ce98 kernel/eka/memmodel/epoc/moving/mchunk.cpp --- a/kernel/eka/memmodel/epoc/moving/mchunk.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kernel/eka/memmodel/epoc/moving/mchunk.cpp Tue May 11 17:28:22 2010 +0300 @@ -467,7 +467,8 @@ { // Allocate a block of contiguous RAM from the free pool TInt numPages=(endOffset-offset)>>m.iPageShift; - r=m.AllocContiguousRam(numPages<1) { TInt align=aSeparateGlobal ? KPageDirectoryShift : KPageDirectoryShift-1; - r=AllocContiguousRam(aNumPages<>m.iPageShift; - r=m.AllocContiguousRam(numPages<Avail()==aSize); + // Allocate whole free bma + test_Equal(aSize, pA->SelectiveAlloc(0, aSize)); + test_Equal(0,pA->Avail()); + // Allocate whole full bma + test_Equal(0, pA->SelectiveAlloc(0, aSize)); + test_Equal(0,pA->Avail()); + TInt i; + TInt j; + TInt l; + for (i=2; i<8; ++i) + { + for (l=1; l<=aSize; ++l) + { + new (pA) TBitMapAllocator(aSize, ETrue); + for (j=0; jAlloc(j,1); + TInt orig=pA->Avail(); + test_Equal(aSize-(aSize+i-1)/i, orig); + TUint newAllocs = pA->SelectiveAlloc(0,l); + TInt allocated = orig - pA->Avail(); + test_Equal(allocated, newAllocs); + test_Equal(l - (l+i-1)/i, allocated); + Check(*pA); + } + } + for (i=0; i<=Min(32,aSize-1); ++i) + { + for (l=1; l<=aSize-i; ++l) + { + for (j=1; j<=aSize; ++j) + { + new (pA) TBitMapAllocator(aSize, ETrue); + pA->Alloc(i,l); + test_Equal(aSize-l, pA->Avail()); + TUint newAllocs = pA->SelectiveAlloc(0,j); + TUint allocated = j - Max(0,Min(i+l,j)-i); + test_Equal(allocated, newAllocs); + test_Equal(pA->Avail(), aSize-l-allocated); + test(!pA->NotAllocated(0,j)); + if (j>=i && jNotAllocated(0,j+1)); + Check(*pA); + } + } + } + delete pA; + } + + TBitMapAllocator* DoSetupBMA(TInt aSize, VA_LIST aList) { TBitMapAllocator* pA=TBitMapAllocator::New(aSize, EFalse); @@ -1246,6 +1302,11 @@ TestSelectiveFree(128); TestSelectiveFree(149); + TestSelectiveAlloc(3); + TestSelectiveAlloc(31); + TestSelectiveAlloc(128); + TestSelectiveAlloc(149); + TestAllocConsecutive(); TestChain(); diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/buffer/t_tbma.h --- a/kerneltest/e32test/buffer/t_tbma.h Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/buffer/t_tbma.h Tue May 11 17:28:22 2010 +0300 @@ -39,6 +39,7 @@ IMPORT_C void Free(TInt aPos); IMPORT_C void Alloc(TInt aStart, TInt aLength); IMPORT_C void Free(TInt aStart, TInt aLength); + IMPORT_C TUint SelectiveAlloc(TInt aStart, TInt aLength); IMPORT_C void SelectiveFree(TInt aStart, TInt aLength); IMPORT_C TBool NotFree(TInt aStart, TInt aLength) const; IMPORT_C TBool NotAllocated(TInt aStart, TInt aLength) const; diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/defrag/t_ramdefrag.cpp --- a/kerneltest/e32test/defrag/t_ramdefrag.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/defrag/t_ramdefrag.cpp Tue May 11 17:28:22 2010 +0300 @@ -33,6 +33,7 @@ #include #include #include "testdefs.h" +#include "..\mmu\mmudetect.h" #include @@ -128,6 +129,7 @@ LOCAL_D TInt* gCandList1; // Array of zones that have the same preference and the same LOCAL_D TInt* gCandList2; // amount of free pages const TInt KInvalidCandIndex = -1; +LOCAL_D TUint gMemModel; // // GetDrive @@ -489,6 +491,8 @@ currentCacheSize >> gPageShift)); } + // Get the memory model of the kernel that this test is running on. + gMemModel = MemModelType(); return KErrNone; } @@ -1536,7 +1540,7 @@ } if (totalMorePrefInUse > requiredMovDis) - {// There enough allocatable pages in the RAM zones below the currently + {// There are enough allocatable pages in the RAM zones below the currently // least preferable RAM in use. test.Printf(_L("Memory is spread out totalMorePref 0x%x required 0x%x\n"), totalMorePrefInUse, requiredMovDis); if (verifySpread) @@ -9384,6 +9388,14 @@ test.Next(_L("Test5: Filling the FS Cache and allocating more than 16 contiguous fixed pages")); TestStart(); + + if (gMemModel >= EMemModelTypeFlexible) + {// The flexible memory model won't flush the whole paging cache for + // contiguous allocations >16 pages so skip the next test. + test.Printf(_L("This memory model won't flush the cache - Skipping...\n")); + goto SkipTest5; + } + // TestEnd() will have reduced any cache pages to minimum so just get current // count of discardable pages. GetAllPageInfo(); diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/demandpaging/t_datapaging.cpp --- a/kerneltest/e32test/demandpaging/t_datapaging.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/demandpaging/t_datapaging.cpp Tue May 11 17:28:22 2010 +0300 @@ -44,9 +44,10 @@ #include "../mmu/d_memorytest.h" #include "../mmu/paging_info.h" -RTest test(_L("T_DATAPAGING")); +_LIT(KChunkName, "t_datapaging chunk"); -_LIT(KChunkName, "t_datapaging chunk"); +RTest test(_L("T_DATAPAGING")); +SVMSwapInfo InitialSwapInfo; class TRandom { @@ -235,7 +236,7 @@ CLOSE_AND_WAIT(thread); } CLOSE_AND_WAIT(gChunk); - User::After(1000000); + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); __KHEAP_MARKEND; } @@ -330,8 +331,8 @@ if (aActual != aExpected) { StopSoakTest(aMsgQueue); - RDebug::Printf(" thread %d failure reading page %d at iteration %d address %08x: expected %08x but got %08x", - aThread, aPage, aIteration, aPtr, aExpected, aActual); + RDebug::Printf(" thread %d failure reading page %d at iteration %d address %08x line %d: expected %08x but got %08x", + aThread, aPage, aIteration, aPtr, aLine, aExpected, aActual); return EFalse; } return ETrue; @@ -341,7 +342,6 @@ { SSoakTestArgs* args = (SSoakTestArgs*)aArg; - RMsgQueue msgQueue; TInt r = msgQueue.OpenGlobal(KMsgQueueName, EOwnerThread); if (r != KErrNone) @@ -595,41 +595,35 @@ test(swapInfo.iSwapFree <= swapInfo.iSwapSize); test.Printf(_L(" Swap size == 0x%x bytes\n"), swapInfo.iSwapSize); test.Printf(_L(" Swap free == 0x%x bytes\n"), swapInfo.iSwapFree); - if (!gDataPagingSupported) - { - test_Equal(0, swapInfo.iSwapSize); - } - else - { - test(swapInfo.iSwapSize != 0); + test(swapInfo.iSwapSize != 0); + InitialSwapInfo = swapInfo; - CommitPage(chunk, 0); - SVMSwapInfo swapInfo2; - test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); - test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); - test_Equal(swapInfo.iSwapFree - gPageSize, swapInfo2.iSwapFree); + CommitPage(chunk, 0); + SVMSwapInfo swapInfo2; + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); + test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); + test_Equal(swapInfo.iSwapFree - gPageSize, swapInfo2.iSwapFree); - DecommitPage(chunk, 0); - test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); - test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); - test_Equal(swapInfo.iSwapFree, swapInfo2.iSwapFree); + DecommitPage(chunk, 0); + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); + test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); + test_Equal(swapInfo.iSwapFree, swapInfo2.iSwapFree); - // Test that closing the chunk releases the swap page. - CommitPage(chunk, 0); - test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); - test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); - test_Equal(swapInfo.iSwapFree - gPageSize, swapInfo2.iSwapFree); + // Test that closing the chunk releases the swap page. + CommitPage(chunk, 0); + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); + test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); + test_Equal(swapInfo.iSwapFree - gPageSize, swapInfo2.iSwapFree); - chunk.Close(); - test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); - test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); - test_Equal(swapInfo.iSwapFree, swapInfo2.iSwapFree); + chunk.Close(); + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo2, 0)); + test_Equal(swapInfo.iSwapSize, swapInfo2.iSwapSize); + test_Equal(swapInfo.iSwapFree, swapInfo2.iSwapFree); - // Chunk must be created for rest of testing. - test_KErrNone(chunk.Create(createInfo)); - if (gDataPagingSupported) - test(chunk.IsPaged()); - } + // Chunk must be created for rest of testing. + test_KErrNone(chunk.Create(createInfo)); + if (gDataPagingSupported) + test(chunk.IsPaged()); // EVMHalSetSwapThresholds, test.Next(_L("Test EVMHalSetSwapThresholds")); @@ -690,6 +684,16 @@ CLOSE_AND_WAIT(chunk); } +void TestSwapInfoUnchanged() + { + SVMSwapInfo swapInfo; + test_KErrNone(UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, &swapInfo, 0)); + test.Printf(_L(" Swap size == 0x%x bytes\n"), swapInfo.iSwapSize); + test.Printf(_L(" Swap free == 0x%x bytes\n"), swapInfo.iSwapFree); + test_Equal(InitialSwapInfo.iSwapSize, swapInfo.iSwapSize); + test_Equal(InitialSwapInfo.iSwapFree, swapInfo.iSwapFree); + } + void TestSwapHalNotSupported() { test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalGetSwapInfo, 0, 0)); @@ -779,6 +783,9 @@ test_KErrNone(timeoutStatus.Int()); CLOSE_AND_WAIT(gChunk); + + UserSvr::HalFunction(EHalGroupKernel, EKernelHalSupervisorBarrier, 0, 0); + __KHEAP_MARKEND; } @@ -1241,15 +1248,18 @@ for (TUint pin = 0 ; pin <= 1 ; ++pin) { test.Printf(_L("processes=%d threads=%d pages=%d maxcachesize=%d pin=%d\r\n"),processes, threads, pages, gMaxCacheSize,pin); - SoakTest(processes, threads, pages, pin, 3); + SoakTest(processes, threads, pages, pin, 5); } } } } - //Reset the cache size to normal - test.Next(_L("Soak test: Reset cache size to normal")); - test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax)); + //Reset the cache size to normal + test.Next(_L("Soak test: Reset cache size to normal")); + test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax)); + + test.Next(_L("Check we haven't leaked any swap in the course of the test")); + TestSwapInfoUnchanged(); } test.End(); diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/demandpaging/t_thrash.cpp --- a/kerneltest/e32test/demandpaging/t_thrash.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/demandpaging/t_thrash.cpp Tue May 11 17:28:22 2010 +0300 @@ -27,6 +27,7 @@ #include #include #include +#include #include "t_dpcmn.h" #include "../mmu/mmudetect.h" @@ -39,11 +40,12 @@ _LIT(KChunkName, "t_thrash chunk"); -class TRandom +class TPRNG { public: - TRandom(); - TUint32 Next(); + TPRNG(); + TUint32 IntRand(); + TReal FloatRand(); private: enum @@ -54,17 +56,178 @@ TUint32 iV; }; -TRandom::TRandom() +TPRNG::TPRNG() { iV = (TUint32)this + RThread().Id() + User::FastCounter() + 23; } -TUint32 TRandom::Next() +TUint32 TPRNG::IntRand() { iV = KA * iV + KB; return iV; } +TReal TPRNG::FloatRand() + { + return (TReal)IntRand() / KMaxTUint32; + } + +class TRandom + { +public: + virtual ~TRandom() { } + virtual TUint32 Next() = 0; + }; + + class TUniformRandom : public TRandom + { +public: + void SetParams(TUint aMax) { iMax = aMax; } + virtual TUint32 Next(); + +private: + TPRNG iRand; + TUint iMax; + }; + +TUint32 TUniformRandom::Next() + { + return iRand.IntRand() % iMax; + } + +class TNormalRandom : public TRandom + { +public: + void SetParams(TInt aMax, TInt aSd); + virtual TUint32 Next(); + +private: + TUint32 GetNext(); + +private: + TPRNG iRand; + TInt iMax; + TInt iExpectation; + TInt iSd; + TUint32 iCached; + }; + +void TNormalRandom::SetParams(TInt aMax, TInt aSd) + { + iMax = aMax; + iExpectation = aMax / 2; + iSd = aSd; + iCached = KMaxTUint32; + } + +TUint32 TNormalRandom::Next() + { + TUint32 r; + do + { + r = GetNext(); + } + while (r > (TUint)iMax); + return r; + } + +TUint32 TNormalRandom::GetNext() + { + if (iCached != KMaxTUint32) + { + TUint32 r = iCached; + iCached = KMaxTUint32; + return r; + } + + // box-muller transform + // from http://www.taygeta.com/random/gaussian.html + + TReal x1, x2, w, ln_w, y1, y2; + do + { + x1 = 2.0 * iRand.FloatRand() - 1.0; + x2 = 2.0 * iRand.FloatRand() - 1.0; + w = x1 * x1 + x2 * x2; + } + while ( w >= 1.0 ); + + TInt r = Math::Ln(ln_w, w); + __ASSERT_ALWAYS(r == KErrNone, User::Invariant()); + w = (-2.0 * ln_w ) / w; + TReal w2; + r = Math::Sqrt(w2, w); + __ASSERT_ALWAYS(r == KErrNone, User::Invariant()); + y1 = x1 * w2; + y2 = x2 * w2; + + y1 = y1 * iSd + iExpectation; + y2 = y2 * iSd + iExpectation; + + iCached = (TUint32)y2; + + return (TUint32)y1; + } + +static TBool BenchmarksSupported = EFalse; +static TReal BenchmarkMultiplier; + +static TInt InitBenchmarks() + { + BenchmarksSupported = UserSvr::HalFunction(EHalGroupVM, EVMHalResetPagingBenchmark, (TAny*)EPagingBmReadRomPage, NULL) == KErrNone; + if (!BenchmarksSupported) + return KErrNone; + + TInt freq = 0; + TInt r = HAL::Get(HAL::EFastCounterFrequency, freq); + if (r != KErrNone) + return r; + BenchmarkMultiplier = 1000000.0 / freq; + return KErrNone; + } + +static void ResetBenchmarks() + { + if (!BenchmarksSupported) + return; + for (TInt i = 0 ; i < EMaxPagingBm ; ++i) + { + TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalResetPagingBenchmark, (TAny*)i, NULL); + if (r != KErrNone) + test.Printf(_L("Error resetting benchmark %d\n"), i); + test_KErrNone(r); + } + } + +static TInt GetBenchmark(TPagingBenchmark aBenchmark, TInt& aCountOut, TInt& aTotalTimeInMicrosOut) + { + + SPagingBenchmarkInfo info; + TInt r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetPagingBenchmark, (TAny*)aBenchmark, &info); + if (r!=KErrNone) + return r; + + aCountOut = info.iCount; + aTotalTimeInMicrosOut = (TInt)(info.iTotalTime * BenchmarkMultiplier); + return KErrNone; + } + +static TInt GetAllBenchmarks(TInt aTestLengthInSeconds, TInt aCountOut[EMaxPagingBm], TInt aTimeOut[EMaxPagingBm]) + { + for (TInt i = 0 ; i < EMaxPagingBm ; ++i) + { + TInt count = 0; + TInt timeInMicros = 0; + TInt r = GetBenchmark((TPagingBenchmark)i, count, timeInMicros); + if (r != KErrNone) + return r; + + aCountOut[i] = count / aTestLengthInSeconds; + aTimeOut[i] = timeInMicros / aTestLengthInSeconds; + } + return KErrNone; + } + void CreatePagedChunk(TInt aSizeInPages) { test_Equal(0,gChunk.Handle()); @@ -84,11 +247,70 @@ return (TUint32*)(gChunk.Base() + (gPageSize * aPage)); } +TInt EnsureSystemIdleThread(TAny*) + { + RThread::Rendezvous(KErrNone); + for (;;) + { + // Spin + } + } + +void EnsureSystemIdle() + { + const TInt KMaxWait = 60 * 1000000; + const TInt KSampleTime = 1 * 1000000; + const TInt KWaitTime = 5 * 1000000; + + test.Printf(_L("Waiting for system to become idle\n")); + TInt totalTime = 0; + TBool idle; + do + { + RThread thread; + test_KErrNone(thread.Create(_L("EnsureSystemIdleThread"), EnsureSystemIdleThread, 1024, NULL, NULL)); + thread.SetPriority(EPriorityLess); + thread.Resume(); + + TRequestStatus status; + thread.Rendezvous(status); + User::WaitForRequest(status); + test_KErrNone(status.Int()); + + User::After(KSampleTime); + thread.Suspend(); + + TTimeIntervalMicroSeconds time; + test_KErrNone(thread.GetCpuTime(time)); + TReal error = (100.0 * Abs(time.Int64() - KSampleTime)) / KSampleTime; + test.Printf(_L(" time == %ld, error == %f%%\n"), time.Int64(), error); + + idle = error < 2.0; + + thread.Kill(KErrNone); + thread.Logon(status); + User::WaitForRequest(status); + test_KErrNone(status.Int()); + CLOSE_AND_WAIT(thread); + + if (!idle) + User::After(KWaitTime); // Allow system to finish whatever it's doing + + totalTime += KSampleTime + KWaitTime; + test(totalTime < KMaxWait); + } + while(!idle); + } + enum TWorkload { EWorkloadSequential, - EWorkloadRandom, - EWorkloadShuffle + EWorkloadUniformRandom, + EWorkloadNormalRandom1, + EWorkloadNormalRandom2, + EWorkloadShuffle, + + EMaxWorkloads }; struct SThrashTestArgs @@ -105,55 +327,86 @@ { SThrashTestArgs* args = (SThrashTestArgs*)aArg; - TRandom random; + TPRNG random; + TUniformRandom uniformRand; + TNormalRandom normalRand; + TInt startPage = args->iThreadGroup * args->iGroupSize; TInt* ptr = (TInt*)(args->iBasePtr + startPage * gPageSize); + + switch (args->iWorkload) { case EWorkloadSequential: while (gRunThrashTest) { - TInt size = (args->iPageCount * gPageSize) / sizeof(TInt); - for (TInt i = 0 ; i < size && gRunThrashTest ; ++i) + for (TUint i = 0 ; + gRunThrashTest && i < (args->iPageCount * gPageSize) / sizeof(TInt) ; + ++i) { - ptr[i] = random.Next(); + ptr[i] = 1; __e32_atomic_add_ord64(&args->iAccesses, 1); } } break; - case EWorkloadRandom: + case EWorkloadUniformRandom: + case EWorkloadNormalRandom1: + case EWorkloadNormalRandom2: { TInt acc = 0; + TInt oldSize = -1; + TUint32 writeMask = 0; + switch (args->iWorkload) + { + case EWorkloadUniformRandom: + case EWorkloadNormalRandom1: + writeMask = 0x80000000; break; + case EWorkloadNormalRandom2: + writeMask = 0xc0000000; break; + default: test(EFalse); break; + } while (gRunThrashTest) { - TInt size = (args->iPageCount * gPageSize) / sizeof(TInt); - for (TInt i = 0 ; i < size && gRunThrashTest ; ++i) + TInt size = args->iPageCount; + if (size != oldSize) { - TUint32 rand = random.Next(); - TInt action = rand >> 31; - TInt r = rand % size; - if (action == 0) - acc += ptr[r]; - else - ptr[r] = acc; - __e32_atomic_add_ord64(&args->iAccesses, 1); + switch (args->iWorkload) + { + case EWorkloadUniformRandom: + uniformRand.SetParams(size); break; + case EWorkloadNormalRandom1: + case EWorkloadNormalRandom2: + normalRand.SetParams(size, size / 8); break; + default: test(EFalse); break; + } + oldSize = size; } + + TInt page = args->iWorkload == EWorkloadUniformRandom ? + uniformRand.Next() : normalRand.Next(); + TInt index = page * (gPageSize / sizeof(TInt)); + TBool write = (random.IntRand() & writeMask) == 0; + if (write) + ptr[index] = acc; + else + acc += ptr[index]; + __e32_atomic_add_ord64(&args->iAccesses, 1); } } break; case EWorkloadShuffle: { - TInt i; + TInt i = 0; while (gRunThrashTest) { TInt size = (args->iPageCount * gPageSize) / sizeof(TInt); - for (i = 0 ; gRunThrashTest && i < (size - 1) ; ++i) - { - Mem::Swap(&ptr[i], &ptr[i + random.Next() % (size - i - 1) + 1], sizeof(TInt)); - __e32_atomic_add_ord64(&args->iAccesses, 2); - } + Mem::Swap(&ptr[i], &ptr[i + random.IntRand() % (size - i - 1) + 1], sizeof(TInt)); + __e32_atomic_add_ord64(&args->iAccesses, 2); + ++i; + if (i >= size - 1) + i = 0; } } break; @@ -172,17 +425,32 @@ SThrashTestArgs iArgs; }; -void ThrashTest(TInt aThreads, // number of threads to run +void ThrashTest(const TDesC& aTestName, // name and description + TInt aThreads, // number of threads to run TBool aSharedData, // whether all threads share the same data TWorkload aWorkload, TInt aBeginPages, // number of pages to start with for last/all threads TInt aEndPages, // number of pages to end with for last/all threads TInt aOtherPages) // num of pages for other threads, or zero to use same value for all { - RDebug::Printf("\nPages Accesses ThL"); + const TInt KTestLengthInSeconds = 2; + + test.Next(_L("Thrash test")); + + DPTest::FlushCache(); + EnsureSystemIdle(); - DPTest::FlushCache(); - User::After(1000000); + TInt i; + test.Printf(_L("Table: %S\n"), &aTestName); + test.Printf(_L("totalPages, totalAccesses, thrashLevel")); + if (BenchmarksSupported) + test.Printf(_L(", rejuveCount, rejuveTime, codePageInCount, codePageInTime, initCount, initTime, readCount, readTime, writePages, writeCount, writeTime")); + if (aThreads > 1) + { + for (TInt i = 0 ; i < aThreads ; ++i) + test.Printf(_L(", Thread%dPages, Thread%dAccesses"), i, i); + } + test.Printf(_L("\n")); TInt pagesNeeded; TInt maxPages = Max(aBeginPages, aEndPages); @@ -208,11 +476,10 @@ test_NotNull(threads); gRunThrashTest = ETrue; - TInt pageCount = aBeginPages; const TInt maxSteps = 30; TInt step = aEndPages >= aBeginPages ? Max((aEndPages - aBeginPages) / maxSteps, 1) : Min((aEndPages - aBeginPages) / maxSteps, -1); + TInt pageCount = aBeginPages - 5 * step; // first run ignored - TInt i; for (i = 0 ; i < aThreads ; ++i) { SThrashThreadData& thread = threads[i]; @@ -242,8 +509,10 @@ for (i = 0 ; i < aThreads ; ++i) __e32_atomic_store_ord64(&threads[i].iArgs.iAccesses, 0); + ResetBenchmarks(); - User::After(2000000); + User::After(KTestLengthInSeconds * 1000 * 1000); + TInt thrashLevel = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0); test(thrashLevel >= 0 && thrashLevel <= 255); @@ -257,20 +526,50 @@ else totalPages += threads[i].iArgs.iPageCount; } + TInt accessesPerSecond = (TInt)(totalAccesses / KTestLengthInSeconds); - test.Printf(_L("%5d %12ld %3d"), totalPages, totalAccesses, thrashLevel); - for (i = 0 ; i < aThreads ; ++i) + TBool warmingUp = (step > 0) ? pageCount < aBeginPages : pageCount > aBeginPages; + if (!warmingUp) { - test.Printf(_L(" %5d %12ld"), - threads[i].iArgs.iPageCount, - __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses)); - test_Equal(KRequestPending, threads[i].iStatus.Int()); + test.Printf(_L("%10d, %13d, %11.2f"), totalPages, accessesPerSecond, (TReal)thrashLevel / 255); + + if (BenchmarksSupported) + { + TInt benchmarkCount[EMaxPagingBm]; + TInt benchmarkTime[EMaxPagingBm]; + test_KErrNone(GetAllBenchmarks(KTestLengthInSeconds, benchmarkCount, benchmarkTime)); + + TInt otherPageInCount = benchmarkCount[EPagingBmReadRomPage] + benchmarkCount[EPagingBmReadCodePage]; + TInt otherPageInTime = benchmarkTime[EPagingBmReadRomPage] + benchmarkTime[EPagingBmReadCodePage]; + + TInt initCount = benchmarkCount[EPagingBmReadDataPage] - benchmarkCount[EPagingBmReadDataMedia]; + TInt initTime = benchmarkTime[EPagingBmReadDataPage] - benchmarkTime[EPagingBmReadDataMedia]; + + test.Printf(_L(", %11d, %10d, %15d, %14d, %9d, %8d, %9d, %8d, %10d, %10d, %9d"), + benchmarkCount[EPagingBmRejuvenate], benchmarkTime[EPagingBmRejuvenate], + otherPageInCount, otherPageInTime, + initCount, initTime, + benchmarkCount[EPagingBmReadDataMedia], benchmarkTime[EPagingBmReadDataMedia], + benchmarkCount[EPagingBmWriteDataPage], + benchmarkCount[EPagingBmWriteDataMedia], benchmarkTime[EPagingBmWriteDataMedia]); + } + + if (aThreads > 1) + { + for (i = 0 ; i < aThreads ; ++i) + { + test.Printf(_L(", %12d, %15ld"), + threads[i].iArgs.iPageCount, + __e32_atomic_load_acq64(&threads[i].iArgs.iAccesses)); + test_Equal(KRequestPending, threads[i].iStatus.Int()); + } + } + test.Printf(_L("\n")); } - test.Printf(_L("\n")); + pageCount += step; if (aEndPages >= aBeginPages ? pageCount >= aEndPages : pageCount < aEndPages) break; - pageCount += step; } gRunThrashTest = EFalse; @@ -285,7 +584,7 @@ } gChunk.Close(); - RDebug::Printf("\n"); + test.Printf(_L("\n")); } void TestThrashing() @@ -298,47 +597,96 @@ TInt maxPages4 = (5 * gMaxCacheSize) / 16; // Single thread increasing in size - test.Next(_L("Thrash test: single thread, sequential workload")); - ThrashTest(1, ETrue, EWorkloadSequential, minPages, maxPages, 0); + ThrashTest(_L("single thread, sequential workload"), + 1, ETrue, EWorkloadSequential, minPages, maxPages, 0); - test.Next(_L("Thrash test: single thread, random workload")); - ThrashTest(1, ETrue, EWorkloadRandom, minPages, maxPages, 0); + ThrashTest(_L("single thread, random workload"), + 1, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0); - test.Next(_L("Thrash test: single thread, shuffle workload")); - ThrashTest(1, ETrue, EWorkloadShuffle, minPages, maxPages, 0); + ThrashTest(_L("single thread, shuffle workload"), + 1, ETrue, EWorkloadShuffle, minPages, maxPages, 0); // Multiple threads with shared data, one thread incresing in size - test.Next(_L("Thrash test: two threads with shared data, one thread increasing, random workload")); - ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, minPages); + ThrashTest(_L("two threads with shared data, one thread increasing, random workload"), + 2, ETrue, EWorkloadUniformRandom, minPages, maxPages, minPages); - test.Next(_L("Thrash test: four threads with shared data, one thread increasing, random workload")); - ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, minPages); + ThrashTest(_L("four threads with shared data, one thread increasing, random workload"), + 4, ETrue, EWorkloadUniformRandom, minPages, maxPages, minPages); // Multiple threads with shared data, all threads incresing in size - test.Next(_L("Thrash test: two threads with shared data, all threads increasing, random workload")); - ThrashTest(2, ETrue, EWorkloadRandom, minPages, maxPages, 0); + ThrashTest(_L("two threads with shared data, all threads increasing, random workload"), + 2, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0); - test.Next(_L("Thrash test: four threads with shared data, all threads increasing, random workload")); - ThrashTest(4, ETrue, EWorkloadRandom, minPages, maxPages, 0); + ThrashTest(_L("four threads with shared data, all threads increasing, random workload"), + 4, ETrue, EWorkloadUniformRandom, minPages, maxPages, 0); // Multiple threads with independent data, one thread incresing in size - test.Next(_L("Thrash test: two threads with independent data, one thread increasing, random workload")); - ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, gMaxCacheSize / 2); + ThrashTest(_L("two threads with independent data, one thread increasing, random workload"), + 2, EFalse, EWorkloadUniformRandom, minPages2, maxPages2, gMaxCacheSize / 2); - test.Next(_L("Thrash test: four threads with independent data, one thread increasing, random workload")); - ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, gMaxCacheSize / 4); + ThrashTest(_L("four threads with independent data, one thread increasing, random workload"), + 4, EFalse, EWorkloadUniformRandom, minPages4, maxPages4, gMaxCacheSize / 4); // Multiple threads with independant data, all threads incresing in size - test.Next(_L("Thrash test: two threads with independent data, all threads increasing, random workload")); - ThrashTest(2, EFalse, EWorkloadRandom, minPages2, maxPages2, 0); + ThrashTest(_L("two threads with independent data, all threads increasing, random workload"), + 2, EFalse, EWorkloadUniformRandom, minPages2, maxPages2, 0); - test.Next(_L("Thrash test: four threads with independent data, all threads increasing, random workload")); - ThrashTest(4, EFalse, EWorkloadRandom, minPages4, maxPages4, 0); + ThrashTest(_L("four threads with independent data, all threads increasing, random workload"), + 4, EFalse, EWorkloadUniformRandom, minPages4, maxPages4, 0); // Attempt to create thrash state where there is sufficient cache - test.Next(_L("Thrash test: two threads with independent data, one threads decreasing, random workload")); TInt halfCacheSize = gMaxCacheSize / 2; - ThrashTest(2, EFalse, EWorkloadRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize); + ThrashTest(_L("two threads with independent data, one threads decreasing, random workload"), + 2, EFalse, EWorkloadUniformRandom, halfCacheSize + 10, halfCacheSize - 30, halfCacheSize); + } + +void TestDistribution(TRandom& aRandom, TInt aSamples) + { + TUint32* data = new TUint32[aSamples]; + test_NotNull(data); + + TInt i; + TReal mean = 0.0; + for (i = 0 ; i < aSamples ; ++i) + { + data[i] = aRandom.Next(); + mean += (TReal)data[i] / aSamples; + } + + TReal sum2 = 0.0; + for (i = 0 ; i < aSamples ; ++i) + { + TReal d = (TReal)data[i] - mean; + sum2 += d * d; + } + TReal variance = sum2 / (aSamples - 1); + + test.Printf(_L(" mean == %f\n"), mean); + test.Printf(_L(" variance == %f\n"), variance); + + delete [] data; + } + +void BenchmarkReplacement() + { + test.Next(_L("Test uniform distribution")); + TUniformRandom rand1; + rand1.SetParams(100); + TestDistribution(rand1, 10000); + + test.Next(_L("Test normal distribution")); + TNormalRandom rand2; + rand2.SetParams(100, 25); + TestDistribution(rand2, 10000); + + ThrashTest(_L("Thrash test: single thread, normal random workload 1"), + 1, ETrue, EWorkloadNormalRandom1, (2 * gMaxCacheSize) / 3, 2 * gMaxCacheSize, 0); + + ThrashTest(_L("Thrash test: single thread, normal random workload 2"), + 1, ETrue, EWorkloadNormalRandom2, (2 * gMaxCacheSize) / 3, 2 * gMaxCacheSize, 0); + + ThrashTest(_L("Thrash test: single thread, uniform random workload"), + 1, ETrue, EWorkloadUniformRandom, (2 * gMinCacheSize) / 3, (3 * gMaxCacheSize) / 2, 0); } void TestThrashHal() @@ -369,27 +717,44 @@ test_Equal(KRequestPending, status.Int()); // stress system and check thrash level and notification - ThrashTest(1, ETrue, EWorkloadRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0); + ThrashTest(_L("stress system"), + 1, ETrue, EWorkloadUniformRandom, gMaxCacheSize * 2, gMaxCacheSize * 2 + 5, 0); r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0); test(r >= 0 && r <= 255); test.Printf(_L("Thrash level == %d\n"), r); test(r > 200); // should indicate thrashing - test_Equal(EChangesThrashLevel, status.Int()); - User::WaitForAnyRequest(); - // wait for system to calm down and check notification again - test_KErrNone(notifier.Logon(status)); - User::WaitForAnyRequest(); - test_Equal(EChangesThreadDeath, status.Int()); - - test_KErrNone(notifier.Logon(status)); + TBool gotThrashNotification = EFalse; + + // wait for EChangesThrashLevel notification + while(status.Int() != KRequestPending) + { + gotThrashNotification = (status.Int() & EChangesThrashLevel) != 0; + User::WaitForAnyRequest(); + test_KErrNone(notifier.Logon(status)); + User::After(1); + } + test(gotThrashNotification); + User::After(2000000); r = UserSvr::HalFunction(EHalGroupVM, EVMHalGetThrashLevel, 0, 0); test(r >= 0 && r <= 255); test.Printf(_L("Thrash level == %d\n"), r); test(r <= 10); // should indicate lightly loaded system - test_Equal(EChangesThrashLevel, status.Int()); + + // wait for EChangesThrashLevel notification + gotThrashNotification = EFalse; + while(status.Int() != KRequestPending) + { + gotThrashNotification = (status.Int() & EChangesThrashLevel) != 0; + User::WaitForAnyRequest(); + test_KErrNone(notifier.Logon(status)); + User::After(1); + } + test(gotThrashNotification); + test_KErrNone(notifier.LogonCancel()); User::WaitForAnyRequest(); + notifier.Close(); } void TestThrashHalNotSupported() @@ -398,45 +763,105 @@ test_Equal(KErrNotSupported, UserSvr::HalFunction(EHalGroupVM, EVMHalSetThrashThresholds, 0, 0)); } +_LIT(KUsageMessage, "usage: t_thrash [ test ] [ thrashing ] [ benchmarks ]\n"); + +enum TTestAction + { + EActionTest = 1 << 0, + EActionThrashing = 1 << 1, + EActionBenchmarks = 1 << 2 + }; + +void BadUsage() + { + test.Printf(KUsageMessage); + test(EFalse); + } + +TInt ParseCommandLine() + { + const TInt KMaxLineLength = 64; + + if (User::CommandLineLength() > KMaxLineLength) + BadUsage(); + TBuf buffer; + User::CommandLine(buffer); + + if (buffer == KNullDesC) + return EActionTest; + + TLex lex(buffer); + TInt result = 0; + while (!lex.Eos()) + { + TPtrC word = lex.NextToken(); + if (word == _L("test")) + result |= EActionTest; + else if (word == _L("thrashing")) + result |= EActionThrashing; + else if (word == _L("benchmarks")) + result |= EActionBenchmarks; + else + { + test.Printf(_L("bad token '%S'\n"), &word); + BadUsage(); + } + } + + return result; + } + TInt E32Main() { test.Title(); test.Start(_L("Test thrashing monitor")); + + test_KErrNone(InitBenchmarks()); + TInt actions = ParseCommandLine(); + test_KErrNone(GetGlobalPolicies()); TUint cacheOriginalMin = 0; TUint cacheOriginalMax = 0; - TUint cacheCurrentSize = 0; if (gDataPagingSupported) { - test.Next(_L("Thrash test: change maximum cache size to minimal")); - //store original values + test.Next(_L("Thrash test: change cache size to maximum 2Mb")); + TUint cacheCurrentSize = 0; DPTest::CacheSize(cacheOriginalMin, cacheOriginalMax, cacheCurrentSize); - gMaxCacheSize = 256; - gMinCacheSize = 64; + gMinCacheSize = 512; + gMaxCacheSize = 520; test_KErrNone(DPTest::SetCacheSize(gMinCacheSize * gPageSize, gMaxCacheSize * gPageSize)); } + + if (actions & EActionTest) + { + TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible; + if (flexibleMemoryModel) + TestThrashHal(); + else + TestThrashHalNotSupported(); + } - TBool flexibleMemoryModel = (MemModelAttributes() & EMemModelTypeMask) == EMemModelTypeFlexible; - if (flexibleMemoryModel) - TestThrashHal(); - else - TestThrashHalNotSupported(); - - if (gDataPagingSupported && User::CommandLineLength() > 0) - { + if (actions & EActionThrashing) + { test.Next(_L("Extended thrashing tests")); TestThrashing(); } + + if (actions & EActionBenchmarks) + { + test.Next(_L("Benchmarking page replacement")); + BenchmarkReplacement(); + } + if (gDataPagingSupported) { - //Reset the cache size to normal test.Next(_L("Thrash test: Reset cache size to normal")); test_KErrNone(DPTest::SetCacheSize(cacheOriginalMin, cacheOriginalMax)); } - + test.End(); return 0; } diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/group/t_ramall.mmp --- a/kerneltest/e32test/group/t_ramall.mmp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/group/t_ramall.mmp Tue May 11 17:28:22 2010 +0300 @@ -19,7 +19,7 @@ targettype exe sourcepath ../mmu source t_ramall.cpp -library euser.lib +library euser.lib dptest.lib OS_LAYER_SYSTEMINCLUDE_SYMBIAN diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/mmu/paging_info.cpp --- a/kerneltest/e32test/mmu/paging_info.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/mmu/paging_info.cpp Tue May 11 17:28:22 2010 +0300 @@ -36,6 +36,7 @@ (const TUint8*)"Del notify data page", (const TUint8*)"Read media data page", (const TUint8*)"Write media data page", + (const TUint8*)"Rejuvenate page", }; __ASSERT_COMPILE(sizeof(BenchmarkNames)/sizeof(TUint8*) == EMaxPagingBm); diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/mmu/t_ramall.cpp --- a/kerneltest/e32test/mmu/t_ramall.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/mmu/t_ramall.cpp Tue May 11 17:28:22 2010 +0300 @@ -20,6 +20,7 @@ #include #include #include +#include #include "d_shadow.h" #include "mmudetect.h" #include "freeram.h" @@ -32,6 +33,18 @@ TInt PageShift; RShadow Shadow; TInt InitFreeRam; +RChunk Chunk; +TUint ChunkCommitEnd; +RThread TouchThread; +TRequestStatus TouchStatus; +TBool TouchDataStop; +RThread FragThread; +TRequestStatus FragStatus; +TBool FragThreadStop; +TBool ManualTest; +TBool CacheSizeAdjustable; +TUint OrigMinCacheSize; +TUint OrigMaxCacheSize; TInt AllocPhysicalRam(TUint32& aAddr, TInt aSize, TInt aAlign) { @@ -118,6 +131,333 @@ } + +struct SPhysAllocData + { + TUint iSize; + TUint iAlign; + TBool iCheckMaxAllocs; + TBool iCheckFreeRam; + }; + + +TInt FillPhysicalRam(TAny* aArgs) + { + SPhysAllocData& allocData = *((SPhysAllocData*)aArgs); + TUint maxAllocs = FreeRam() / allocData.iSize; + TUint32* physAddrs = new TUint32[maxAllocs + 1]; + if (!physAddrs) + return KErrNoMemory; + TUint32* pa = physAddrs; + TUint32 alignMask = (1 << allocData.iAlign) - 1; + TUint initialFreeRam = FreeRam(); + TInt r = KErrNone; + TUint allocations = 0; + for (; allocations <= maxAllocs + 1; allocations++) + { + TUint freeRam = FreeRam(); + r = AllocPhysicalRam(*pa, allocData.iSize, allocData.iAlign); + if (r != KErrNone) + break; + if (*pa++ & alignMask) + { + r = KErrGeneral; + RDebug::Printf("Error alignment phys addr 0x%08x", *(pa - 1)); + break; + } + if (allocData.iCheckFreeRam && freeRam - allocData.iSize != (TUint)FreeRam()) + { + r = KErrGeneral; + RDebug::Printf("Error in free ram 0x%08x orig 0x%08x", FreeRam(), freeRam); + } + if (allocData.iCheckMaxAllocs && allocations > maxAllocs && r == KErrNone) + { + r = KErrOverflow; + RDebug::Printf("Error able to allocate too many pages"); + break; + } + } + + TUint32* physEnd = pa; + TBool failFrees = EFalse; + for (pa = physAddrs; pa < physEnd; pa++) + { + if (FreePhysicalRam(*pa, allocData.iSize) != KErrNone) + failFrees = ETrue; + } + if (failFrees) + r = KErrNotFound; + if (allocData.iCheckFreeRam && initialFreeRam != (TUint)FreeRam()) + { + r = KErrGeneral; + RDebug::Printf("Error in free ram 0x%08x initial 0x%08x", FreeRam(), initialFreeRam); + } + delete[] physAddrs; + if (r != KErrNone && r != KErrNoMemory) + return r; + TUint possibleAllocs = initialFreeRam / allocData.iSize; + if (allocData.iCheckMaxAllocs && possibleAllocs != allocations) + { + RDebug::Printf("Error in number of allocations possibleAllocs %d allocations %d", possibleAllocs, allocations); + return KErrGeneral; + } + return allocations; + } + + +void TestMultipleContiguousAllocations(TUint aNumThreads, TUint aSize, TUint aAlign) + { + test.Printf(_L("TestMultiContig threads %d size 0x%x, align %d\n"), aNumThreads, aSize, aAlign); + SPhysAllocData allocData; + allocData.iSize = aSize; + allocData.iAlign = aAlign; + allocData.iCheckMaxAllocs = EFalse; + allocData.iCheckFreeRam = EFalse; + // Start several threads all contiguous allocating memory. + RThread* threads = new RThread[aNumThreads]; + TRequestStatus* status = new TRequestStatus[aNumThreads]; + TUint i = 0; + for (; i < aNumThreads; i++) + { + TInt r = threads[i].Create(KNullDesC, FillPhysicalRam, KDefaultStackSize, PageSize, PageSize, (TAny*)&allocData); + test_KErrNone(r); + threads[i].Logon(status[i]); + } + for (i = 0; i < aNumThreads; i++) + { + threads[i].Resume(); + } + for (i = 0; i < aNumThreads; i++) + { + User::WaitForRequest(status[i]); + test_Equal(EExitKill, threads[i].ExitType()); + TInt exitReason = threads[i].ExitReason(); + test_Value(exitReason, exitReason >= 0 || exitReason == KErrNoMemory); + threads[i].Close(); + } + delete[] status; + delete[] threads; + } + +struct STouchData + { + TUint iSize; + TUint iFrequency; + }TouchData; + + +TInt TouchMemory(TAny*) + { + while (!TouchDataStop) + { + TUint8* p = Chunk.Base(); + TUint8* pEnd = p + ChunkCommitEnd; + TUint8* fragPEnd = p + TouchData.iFrequency; + for (TUint8* fragP = p + TouchData.iSize; fragPEnd < pEnd;) + { + TUint8* data = fragP; + for (; data < fragPEnd; data += PageSize) + { + *data = (TUint8)(data - fragP); + } + for (data = fragP; data < fragPEnd; data += PageSize) + { + if (*data != (TUint8)(data - fragP)) + { + RDebug::Printf("Error unexpected data 0x%x read from 0x%08x", *data, data); + return KErrGeneral; + } + } + fragP = fragPEnd + TouchData.iSize; + fragPEnd += TouchData.iFrequency; + } + } + return KErrNone; + } + +struct SFragData + { + TUint iSize; + TUint iFrequency; + TUint iDiscard; + TBool iFragThread; + }FragData; + +void FragmentMemoryFunc() + { + ChunkCommitEnd = 0; + TInt r; + while(KErrNone == (r = Chunk.Commit(ChunkCommitEnd,PageSize)) && !FragThreadStop) + { + ChunkCommitEnd += PageSize; + } + if (FragThreadStop) + return; + test_Equal(KErrNoMemory, r); + TUint freeBlocks = 0; + for ( TUint offset = 0; + (offset + FragData.iSize) < ChunkCommitEnd; + offset += FragData.iFrequency, freeBlocks++) + { + test_KErrNone(Chunk.Decommit(offset, FragData.iSize)); + } + if (!FragData.iFragThread) + test_Equal(FreeRam(), freeBlocks * FragData.iSize); + + if (FragData.iDiscard && CacheSizeAdjustable && !FragThreadStop) + { + TUint minCacheSize = FreeRam(); + TUint maxCacheSize = minCacheSize; + TUint currentCacheSize; + test_KErrNone(DPTest::CacheSize(OrigMinCacheSize, OrigMaxCacheSize, currentCacheSize)); + test_KErrNone(DPTest::SetCacheSize(minCacheSize, maxCacheSize)); + test_KErrNone(DPTest::SetCacheSize(OrigMinCacheSize, maxCacheSize)); + } + } + + +void UnfragmentMemoryFunc() + { + if (FragData.iDiscard && CacheSizeAdjustable) + test_KErrNone(DPTest::SetCacheSize(OrigMinCacheSize, OrigMaxCacheSize)); + Chunk.Decommit(0, Chunk.MaxSize()); + } + + +TInt FragmentMemoryThreadFunc(TAny*) + { + while (!FragThreadStop) + { + FragmentMemoryFunc(); + UnfragmentMemoryFunc(); + } + return KErrNone; + } + + +void FragmentMemory(TUint aSize, TUint aFrequency, TBool aDiscard, TBool aTouchMemory, TBool aFragThread) + { + test_Value(aTouchMemory, !aTouchMemory || !aFragThread); + test_Value(aSize, aSize < aFrequency); + FragData.iSize = aSize; + FragData.iFrequency = aFrequency; + FragData.iDiscard = aDiscard; + FragData.iFragThread = aFragThread; + + TChunkCreateInfo chunkInfo; + chunkInfo.SetDisconnected(0, 0, FreeRam()); + chunkInfo.SetPaging(TChunkCreateInfo::EUnpaged); + test_KErrNone(Chunk.Create(chunkInfo)); + + if (aFragThread) + { + TInt r = FragThread.Create(KNullDesC, FragmentMemoryThreadFunc, KDefaultStackSize, PageSize, PageSize, NULL); + test_KErrNone(r); + FragThread.Logon(FragStatus); + FragThreadStop = EFalse; + FragThread.Resume(); + } + else + { + FragmentMemoryFunc(); + } + if (aTouchMemory && !ManualTest) + { + TouchData.iSize = aSize; + TouchData.iFrequency = aFrequency; + TInt r = TouchThread.Create(KNullDesC, TouchMemory, KDefaultStackSize, PageSize, PageSize, NULL); + test_KErrNone(r); + TouchThread.Logon(TouchStatus); + TouchDataStop = EFalse; + TouchThread.Resume(); + } + } + + +void UnfragmentMemory(TBool aDiscard, TBool aTouchMemory, TBool aFragThread) + { + test_Value(aTouchMemory, !aTouchMemory || !aFragThread); + if (aTouchMemory && !ManualTest) + { + TouchDataStop = ETrue; + User::WaitForRequest(TouchStatus); + test_Equal(EExitKill, TouchThread.ExitType()); + test_KErrNone(TouchThread.ExitReason()); + CLOSE_AND_WAIT(TouchThread); + } + if (aFragThread) + { + FragThreadStop = ETrue; + User::WaitForRequest(FragStatus); + test_Equal(EExitKill, FragThread.ExitType()); + test_KErrNone(FragThread.ExitReason()); + CLOSE_AND_WAIT(FragThread); + } + else + UnfragmentMemoryFunc(); + CLOSE_AND_WAIT(Chunk); + } + + +void TestFillPhysicalRam(TUint aFragSize, TUint aFragFreq, TUint aAllocSize, TUint aAllocAlign, TBool aDiscard, TBool aTouchMemory) + { + test.Printf(_L("TestFillPhysicalRam aFragSize 0x%x aFragFreq 0x%x aAllocSize 0x%x aAllocAlign %d dis %d touch %d\n"), + aFragSize, aFragFreq, aAllocSize, aAllocAlign, aDiscard, aTouchMemory); + FragmentMemory(aFragSize, aFragFreq, aDiscard, aTouchMemory, EFalse); + SPhysAllocData allocData; + // Only check free all ram could be allocated in manual tests as fixed pages may be fragmented. + allocData.iCheckMaxAllocs = (ManualTest && !aTouchMemory && !aAllocAlign)? ETrue : EFalse; + allocData.iCheckFreeRam = ETrue; + allocData.iSize = aAllocSize; + allocData.iAlign = aAllocAlign; + FillPhysicalRam(&allocData); + UnfragmentMemory(aDiscard, aTouchMemory, EFalse); + } + + +void TestFragmentedAllocation() + { + // Test every other page free. + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize, 0, EFalse, EFalse); + if (ManualTest) + { + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize * 2, 0, EFalse, EFalse); + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize, 0, EFalse, ETrue); + } + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize * 2, 0, EFalse, ETrue); + // Test every 2 pages free. + TestFillPhysicalRam(PageSize * 2, PageSize * 4, PageSize * 8, 0, EFalse, EFalse); + if (ManualTest) + TestFillPhysicalRam(PageSize * 2, PageSize * 4, PageSize * 8, 0, EFalse, ETrue); + // Test 10 pages free then 20 pages allocated, allocate 256 pages (1MB in most cases). + if (ManualTest) + TestFillPhysicalRam(PageSize * 10, PageSize * 30, PageSize * 256, 0, EFalse, EFalse); + TestFillPhysicalRam(PageSize * 10, PageSize * 30, PageSize * 256, 0, EFalse, ETrue); + + if (CacheSizeAdjustable) + {// It is possible to adjust the cache size so test phyiscally contiguous + // allocations discard and move pages when required. + test.Next(_L("TestFragmentedAllocations with discardable data no true free memory")); + // Test every other page free. + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize, 0, ETrue, EFalse); + if (ManualTest) + { + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize, 0, ETrue, ETrue); + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize * 2, 0, ETrue, EFalse); + } + TestFillPhysicalRam(PageSize, PageSize * 2, PageSize * 2, 0, ETrue, ETrue); + // Test every 2 pages free. + TestFillPhysicalRam(PageSize * 2, PageSize * 4, PageSize * 8, 0, ETrue, EFalse); + if (ManualTest) + TestFillPhysicalRam(PageSize * 2, PageSize * 4, PageSize * 8, 0, ETrue, ETrue); + // Test 10 pages free then 20 pages allocated, allocate 256 pages (1MB in most cases). + if (ManualTest) + TestFillPhysicalRam(PageSize * 10, PageSize * 30, PageSize * 256, 0, ETrue, EFalse); + TestFillPhysicalRam(PageSize * 10, PageSize * 30, PageSize * 256, 0, ETrue, ETrue); + } + } + + GLDEF_C TInt E32Main() // // Test RAM allocation @@ -135,6 +475,22 @@ PageShift=-1; for (; psz; psz>>=1, ++PageShift); + TUint currentCacheSize; + CacheSizeAdjustable = DPTest::CacheSize(OrigMinCacheSize, OrigMaxCacheSize, currentCacheSize) == KErrNone; + + TUint memodel = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL) & EMemModelTypeMask; + + TInt cmdLineLen = User::CommandLineLength(); + if(cmdLineLen) + { + _LIT(KManual, "manual"); + RBuf cmdLine; + test_KErrNone(cmdLine.Create(cmdLineLen)); + User::CommandLine(cmdLine); + cmdLine.LowerCase(); + ManualTest = cmdLine.Find(KManual) != KErrNotFound; + } + InitFreeRam=FreeRam(); test.Printf(_L("Free RAM=%08x, Page size=%x, Page shift=%d\n"),InitFreeRam,PageSize,PageShift); @@ -148,8 +504,54 @@ test.Next(_L("TestClaimPhys")); TestClaimPhys(); + if (memodel >= EMemModelTypeFlexible) + { + test.Next(_L("TestFragmentedAllocation")); + TestFragmentedAllocation(); + + test.Next(_L("TestMultipleContiguousAllocations")); + TestMultipleContiguousAllocations(20, PageSize * 16, 0); + TestMultipleContiguousAllocations(20, PageSize * 16, PageShift + 1); + TestMultipleContiguousAllocations(20, PageSize * 128, PageShift + 2); + + FragmentMemory(PageSize, PageSize * 2, EFalse, EFalse, EFalse); + TestMultipleContiguousAllocations(20, PageSize * 128, PageShift + 2); + UnfragmentMemory(EFalse, EFalse, EFalse); + + test.Next(_L("TestMultipleContiguousAllocations while accessing memory")); + FragmentMemory(PageSize, PageSize * 2, EFalse, ETrue, EFalse); + TestMultipleContiguousAllocations(20, PageSize * 128, PageShift + 2); + UnfragmentMemory(EFalse, ETrue, EFalse); + FragmentMemory(PageSize, PageSize * 2, ETrue, ETrue, EFalse); + TestMultipleContiguousAllocations(50, PageSize * 256, PageShift + 5); + UnfragmentMemory(ETrue, ETrue, EFalse); + FragmentMemory(PageSize * 16, PageSize * 32, ETrue, ETrue, EFalse); + TestMultipleContiguousAllocations(10, PageSize * 512, PageShift + 8); + UnfragmentMemory(ETrue, ETrue, EFalse); + FragmentMemory(PageSize * 32, PageSize * 64, ETrue, ETrue, EFalse); + TestMultipleContiguousAllocations(10, PageSize * 1024, PageShift + 10); + UnfragmentMemory(ETrue, ETrue, EFalse); + + test.Next(_L("TestMultipleContiguousAllocations with repeated movable and discardable allocations")); + FragmentMemory(PageSize, PageSize * 2, EFalse, EFalse, ETrue); + TestMultipleContiguousAllocations(20, PageSize * 2, PageShift); + UnfragmentMemory(EFalse, EFalse, ETrue); + FragmentMemory(PageSize, PageSize * 2, EFalse, EFalse, ETrue); + TestMultipleContiguousAllocations(20, PageSize * 128, PageShift + 2); + UnfragmentMemory(EFalse, EFalse, ETrue); + FragmentMemory(PageSize, PageSize * 2, ETrue, EFalse, ETrue); + TestMultipleContiguousAllocations(50, PageSize * 256, PageShift + 5); + UnfragmentMemory(ETrue, EFalse, ETrue); + FragmentMemory(PageSize * 16, PageSize * 32, ETrue, EFalse, ETrue); + TestMultipleContiguousAllocations(20, PageSize * 512, PageShift + 8); + UnfragmentMemory(ETrue, EFalse, ETrue); + FragmentMemory(PageSize * 32, PageSize * 64, ETrue, EFalse, ETrue); + TestMultipleContiguousAllocations(20, PageSize * 1024, PageShift + 10); + UnfragmentMemory(ETrue, EFalse, ETrue); + } + Shadow.Close(); + test.Printf(_L("Free RAM=%08x at end of test\n"),FreeRam()); test.End(); return(KErrNone); } - diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32test/mmu/t_shadow.cpp --- a/kerneltest/e32test/mmu/t_shadow.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32test/mmu/t_shadow.cpp Tue May 11 17:28:22 2010 +0300 @@ -338,6 +338,9 @@ const TUint KChunkShift = 20; const TUint KChunkSize = 1 << KChunkShift; +const TUint KRomSizeAlign = 16; // This should match CFG_RomSizeAlign defined in bootcpu.inc +const TUint KRomSizeAlignMask = (1 << KRomSizeAlign) - 1; + void TestRomIsSectionMapped() { test.Start(_L("Test ROM is section mapped")); @@ -350,13 +353,24 @@ test_KErrNone(Shadow.GetPdInfo(KGlobalPageDirectory, pdSize, pdBase, offset)); test.Printf(_L("pd base == %08x, pd size == %08x, pd offset == %08x\n"), pdBase, pdSize, offset); - for (TLinAddr addr = RomUnpagedStart ; addr <= RomUnpagedEnd ; addr += KChunkSize) + TUint romSize = RomUnpagedEnd - RomUnpagedStart; + test.Printf(_L("rom size == %x\n"), romSize); + if (RomPagedStart == RomPagedEnd) { + // If rom is not paged then we must round the ROM size up to a mutiple of 64KB (or whatever + // the CFG_RomSizeAlign settings is), because that's how the bootstrap maps it + romSize = (romSize + KRomSizeAlignMask) & ~KRomSizeAlignMask; + test.Printf(_L("rom size rounded up to %x\n"), romSize); + } + + for (TUint pos = 0 ; pos < romSize ; pos += KChunkSize) + { + TLinAddr addr = RomUnpagedStart + pos; TUint i = (addr >> KChunkShift) - offset; TUint pde = Shadow.Read(pdBase + i*4); test.Printf(_L(" %08x: PDE %08x\n"), addr, pde); - TUint expectedPdeType = (RomUnpagedEnd - addr) >= KChunkSize ? 2 : 1; + TUint expectedPdeType = (romSize - pos) >= KChunkSize ? 2 : 1; test_Equal(expectedPdeType, pde & 3); } #else diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32utils/group/base_e32utils.mrp --- a/kerneltest/e32utils/group/base_e32utils.mrp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32utils/group/base_e32utils.mrp Tue May 11 17:28:22 2010 +0300 @@ -17,6 +17,7 @@ source \sf\os\kernelhwsrv\kerneltest\e32utils\trace source \sf\os\kernelhwsrv\kerneltest\e32utils\usbmsapp source \sf\os\kernelhwsrv\kerneltest\e32utils\sdpartition +source \sf\os\kernelhwsrv\kerneltest\e32utils\nandboot\coreldr\bootstrap_smrif.h #MattD: Reltools 2.67 don't understand 'cwtools' are the CW equivelent of 'tools' and try to do '-what cwtools udeb' instead of '-what cwtools deb'. #binary \sf\os\kernelhwsrv\kerneltest\e32utils\group cwtools diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/e32utils/group/bld.inf --- a/kerneltest/e32utils/group/bld.inf Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/e32utils/group/bld.inf Tue May 11 17:28:22 2010 +0300 @@ -1,4 +1,4 @@ -// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies). +// Copyright (c) 1999-2010 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" @@ -30,20 +30,19 @@ PRJ_EXPORTS - - +../nandboot/coreldr/bootstrap_smrif.h SYMBIAN_OS_LAYER_PLATFORM_EXPORT_PATH(bootstrap_smrif.h) ../profiler/profiler.h SYMBIAN_OS_LAYER_PLATFORM_EXPORT_PATH(profiler.h) -../analyse/profiler.rtf /epoc32/engdoc/profiler/profiler.rtf +../analyse/profiler.rtf /epoc32/engdoc/profiler/profiler.rtf -../d_exc/printstk.pl /epoc32/rom/tools/printstk.pl -../d_exc/printsym.pl /epoc32/rom/tools/printsym.pl +../d_exc/printstk.pl /epoc32/rom/tools/printstk.pl +../d_exc/printsym.pl /epoc32/rom/tools/printsym.pl -../setcap/setcap.iby /epoc32/rom/include/setcap.iby +../setcap/setcap.iby /epoc32/rom/include/setcap.iby ../demandpaging/dptest.h SYMBIAN_OS_LAYER_PLATFORM_EXPORT_PATH(dptest.h) -../demandpaging/dptestcons.oby /epoc32/rom/include/dptestcons.oby +../demandpaging/dptestcons.oby /epoc32/rom/include/dptestcons.oby PRJ_TESTEXPORTS ../trace/btracevw.pl /epoc32/tools/btracevw.pl diff -r 41f0cfe18c80 -r c734af59ce98 kerneltest/f32test/server/t_fsys.cpp --- a/kerneltest/f32test/server/t_fsys.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/kerneltest/f32test/server/t_fsys.cpp Tue May 11 17:28:22 2010 +0300 @@ -933,38 +933,57 @@ //-- 2. open this file nRes = file.Open(TheFs, KFile, EFileRead); test_KErrNone(nRes); + + const TInt drvNumber = CurrentDrive(); //-- 2.1 try to dismount the FS, it must fail because of the opened object. TBuf<40> fsName; - nRes = TheFs.FileSystemName(fsName, CurrentDrive()); + nRes = TheFs.FileSystemName(fsName, drvNumber); test_KErrNone(nRes); - nRes = TheFs.DismountFileSystem(fsName, CurrentDrive()); - test(nRes == KErrInUse); - + nRes = TheFs.DismountFileSystem(fsName, drvNumber); + test_Value(nRes, nRes == KErrInUse); + // Flag from locmedia.h to simulate ejecting and re-inserting the media. + const TUint KMediaRemountForceMediaChange = 0x00000001; + TRequestStatus changeStatus; + TheFs.NotifyChange(ENotifyAll, changeStatus); + TDriveInfo driveInfo; + //-- 3. forcedly remount the drive - nRes = TheFs.RemountDrive(CurrentDrive()); + nRes = TheFs.RemountDrive(drvNumber, NULL, KMediaRemountForceMediaChange); + if(nRes == KErrNotSupported) - {//-- this feature is not supported and the test is inconsistent. + {//-- this feature is not supported and the test is inconsistent. test.Printf(_L("RemountDrive() is not supported, the test is inconsistent!")); //-- remounting must work at least on MMC drives - const TBool isFAT = Is_Fat(TheFs, CurrentDrive()); + const TBool isFAT = Is_Fat(TheFs, drvNumber); - TDriveInfo driveInfo; - nRes = TheFs.Drive(driveInfo, CurrentDrive()); + nRes = TheFs.Drive(driveInfo, drvNumber); test_KErrNone(nRes); - test(!isFAT || (!(driveInfo.iDriveAtt & KDriveAttRemovable))); - - } + test_Value(driveInfo.iDriveAtt, !isFAT || (!(driveInfo.iDriveAtt & KDriveAttRemovable))); + } else - { - test_KErrNone(nRes); - } - - User::After(500*K1mSec); + { + test_Value(nRes, nRes == KErrNotReady || nRes == KErrNone); + + //-- 3.1 wait for media change to complete + do + { + // Waiting for media change... + User::WaitForRequest(changeStatus); + nRes = TheFs.Drive(driveInfo, drvNumber); + TheFs.NotifyChange(ENotifyAll, changeStatus); + } + while (nRes == KErrNotReady); + + test_KErrNone(nRes); + User::After(1000*K1mSec); // Wait 1 sec (needed by certain platforms) + } + + TheFs.NotifyChangeCancel(changeStatus); //-- 4. read this file. The FS will be remounted and the read must be OK. TBuf8<40> buf; diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/etshell/ts_com.cpp --- a/userlibandfileserver/fileserver/etshell/ts_com.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/etshell/ts_com.cpp Tue May 11 17:28:22 2010 +0300 @@ -1,4 +1,4 @@ -// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). +// Copyright (c) 1996-2010 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" @@ -978,7 +978,11 @@ default: aPrintBuf.Append(_L("??? Unknown Type")); break; }; - + if (aDrvInfo.iConnectionBusType) + { + aPrintBuf.Append(_L(" USB")); + } + aPrintBuf.Append(_L("\n")); } diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/group/release.txt --- a/userlibandfileserver/fileserver/group/release.txt Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/group/release.txt Tue May 11 17:28:22 2010 +0300 @@ -1,3 +1,27 @@ +Version 2.00.2054 +================= +(Made by vfebvre 23/04/2010) + +1. michcox + 1. ou1cimx1#357507 TB92 Maps 3.04: File mode EFileWrite (defined in f32file.h) corrupts memory of other processes under certain conditions on 5.0 devices + + +Version 2.00.2053 +================= +(Made by vfebvre 22/04/2010) + +1. niccox + 1. ou1cimx1#356808 Disconnecting memory card reader/writer from phone causes phone crash + + +Version 2.00.2052 +================= +(Made by vfebvre 14/04/2010) + +1. famustaf + 1. PDEF145305 F32TEST T_FSYS test failure investigation + + Version 2.00.2051 ================= (Made by vfebvre 09/04/2010) diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/inc/f32ver.h --- a/userlibandfileserver/fileserver/inc/f32ver.h Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/inc/f32ver.h Tue May 11 17:28:22 2010 +0300 @@ -58,6 +58,6 @@ @see TVersion */ -const TInt KF32BuildVersionNumber=2051; +const TInt KF32BuildVersionNumber=2054; // #endif diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/sfile/sf_file_cache.cpp --- a/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp Tue May 11 17:28:22 2010 +0300 @@ -1503,6 +1503,11 @@ // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure // TFsFileWrite::PostInitialise() doesn't think there's no data left to process aMsgRequest.ReStart(); + + //Need to preserve the current state otherwise if we are over the ram threshold + //the request can end up in a livelock trying to repeatedly flush. + currentOperation->iState = EStWriteThrough; + if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) return r; } diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/msproxy/debug.h --- a/userlibandfileserver/fileserver/shostmassstorage/msproxy/debug.h Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/msproxy/debug.h Tue May 11 17:28:22 2010 +0300 @@ -22,7 +22,7 @@ #ifndef PXY_DEBUG_H #define PXY_DEBUG_H -//#define _HOST_DEBUG_PRINT_ +// #define _HOST_DEBUG_PRINT_ // #define _PROXY_DEBUG_PRINT_ // #define _PROXY_FN_TRACE_ diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/msproxy/hostusbmsproxy.cpp --- a/userlibandfileserver/fileserver/shostmassstorage/msproxy/hostusbmsproxy.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/msproxy/hostusbmsproxy.cpp Tue May 11 17:28:22 2010 +0300 @@ -596,12 +596,18 @@ // do nothing } } - else if (KErrNotReady) + else if (KErrNotReady == r) { __HOSTPRINT(_L("<<< HOST Caps Media Not Present")); c.iType = EMediaNotPresent; r = KErrNone; } + else if (KErrGeneral == r) + { + RDebug::Print(_L("<<< HOST Caps Unable to communicate with media")); + c.iType = EMediaUnknown; + } + else { __HOSTPRINT(_L("<<< HOST Caps Unknown Error")); diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/server/controller/cusbhostmsdevice.cpp --- a/userlibandfileserver/fileserver/shostmassstorage/server/controller/cusbhostmsdevice.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/server/controller/cusbhostmsdevice.cpp Tue May 11 17:28:22 2010 +0300 @@ -166,14 +166,14 @@ // interface suspension to the transport layer for (TInt i = 0; i < iLuList.Count(); i++) { - CUsbHostMsLogicalUnit& lu = iLuList.GetLuL(i); + CUsbHostMsLogicalUnit& lu = iLuList.GetLu(i); if (!lu.IsReadyToSuspend() && lu.IsConnected()) return; } for (TInt i = 0; i < iLuList.Count(); i++) { - CUsbHostMsLogicalUnit& lu = iLuList.GetLuL(i); + CUsbHostMsLogicalUnit& lu = iLuList.GetLu(i); SetLunL(lu); lu.SuspendL(); } @@ -299,7 +299,7 @@ TInt err; for (TInt i = 0; i < iLuList.Count(); i++) { - CUsbHostMsLogicalUnit& lu = iLuList.GetLuL(i); + CUsbHostMsLogicalUnit& lu = iLuList.GetLu(i); SetLunL(lu); TRAP(err, lu.DoLunReadyCheckL()); } @@ -315,7 +315,7 @@ // interface suspension to the transport layer for (TInt i = 0; i < iLuList.Count(); i++) { - CUsbHostMsLogicalUnit& lu = iLuList.GetLuL(i); + CUsbHostMsLogicalUnit& lu = iLuList.GetLu(i); // Has any of the logical units have got its state changed? if ( (lu.IsReadyToSuspend() && !lu.IsConnected()) || (!lu.IsReadyToSuspend() && lu.IsConnected()) ) @@ -327,7 +327,7 @@ for (TInt i = 0; i < iLuList.Count(); i++) { - CUsbHostMsLogicalUnit& lu = iLuList.GetLuL(i); + CUsbHostMsLogicalUnit& lu = iLuList.GetLu(i); SetLunL(lu); lu.SuspendL(); } @@ -341,7 +341,7 @@ __MSFNLOG for (TInt i = 0; i < iLuList.Count(); i++) { - CUsbHostMsLogicalUnit& lu = iLuList.GetLuL(i); + CUsbHostMsLogicalUnit& lu = iLuList.GetLu(i); SetLunL(lu); lu.ResumeL(); } diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/server/controller/include/tlogicalunitlist.h --- a/userlibandfileserver/fileserver/shostmassstorage/server/controller/include/tlogicalunitlist.h Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/server/controller/include/tlogicalunitlist.h Tue May 11 17:28:22 2010 +0300 @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// Copyright (c) 2008-2010 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" @@ -29,9 +29,10 @@ void RemoveLuL(TLun aLun); void RemoveAllLuL(); CUsbHostMsLogicalUnit& GetLuL(TLun aLun) const; - TInt FindLu(TLun aLun) const; + CUsbHostMsLogicalUnit& GetLu(TInt aIndex) const; TInt Count() const; private: + TInt FindLu(TLun aLun) const; RPointerArray iLu; }; diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/server/controller/tlogicalunitlist.cpp --- a/userlibandfileserver/fileserver/shostmassstorage/server/controller/tlogicalunitlist.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/server/controller/tlogicalunitlist.cpp Tue May 11 17:28:22 2010 +0300 @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// Copyright (c) 2008-2010 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" @@ -124,3 +124,10 @@ User::LeaveIfError(index); return *iLu[index]; } + + +CUsbHostMsLogicalUnit& TLogicalUnitList::GetLu(TInt aIndex) const + { + __MSFNSLOG + return *iLu[aIndex]; + } diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/server/protocol/cscsiprotocol.cpp --- a/userlibandfileserver/fileserver/shostmassstorage/server/protocol/cscsiprotocol.cpp Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/server/protocol/cscsiprotocol.cpp Tue May 11 17:28:22 2010 +0300 @@ -264,7 +264,8 @@ __MSFNLOG if (!IsConnected()) { - DoScsiReadyCheckEventL(); + if (!DoScsiReadyCheckEventL()) + return; } if (!iSbcInterface) @@ -800,7 +801,7 @@ } -void CScsiProtocol::DoScsiReadyCheckEventL() +TBool CScsiProtocol::DoScsiReadyCheckEventL() { __MSFNLOG TInt err = KErrNone; @@ -833,6 +834,7 @@ iMediaChangeNotifier.DoNotifyL(); } } + return err = KErrNone ? ETrue : EFalse; } diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/server/protocol/include/cscsiprotocol.h --- a/userlibandfileserver/fileserver/shostmassstorage/server/protocol/include/cscsiprotocol.h Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/server/protocol/include/cscsiprotocol.h Tue May 11 17:28:22 2010 +0300 @@ -85,7 +85,7 @@ // unit testing void CreateSbcInterfaceL(TUint32 aBlockLen, TUint32 aLastLba); - void DoScsiReadyCheckEventL(); + TBool DoScsiReadyCheckEventL(); void NotifyChange(const RMessage2& aMessage); void ForceCompleteNotifyChangeL(); diff -r 41f0cfe18c80 -r c734af59ce98 userlibandfileserver/fileserver/shostmassstorage/server/protocol/include/mprotocol.h --- a/userlibandfileserver/fileserver/shostmassstorage/server/protocol/include/mprotocol.h Tue Apr 27 18:02:57 2010 +0300 +++ b/userlibandfileserver/fileserver/shostmassstorage/server/protocol/include/mprotocol.h Tue May 11 17:28:22 2010 +0300 @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// Copyright (c) 2008-2010 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" @@ -66,7 +66,7 @@ /** unit testing */ virtual void CreateSbcInterfaceL(TUint32 aBlockLen, TUint32 aLastLba) = 0; - virtual void DoScsiReadyCheckEventL() = 0; + virtual TBool DoScsiReadyCheckEventL() = 0; /** Media change notification