1*76559068SAndroid Build Coastguard Worker //===-- primary32.h ---------------------------------------------*- C++ -*-===// 2*76559068SAndroid Build Coastguard Worker // 3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information. 5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*76559068SAndroid Build Coastguard Worker // 7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===// 8*76559068SAndroid Build Coastguard Worker 9*76559068SAndroid Build Coastguard Worker #ifndef SCUDO_PRIMARY32_H_ 10*76559068SAndroid Build Coastguard Worker #define SCUDO_PRIMARY32_H_ 11*76559068SAndroid Build Coastguard Worker 12*76559068SAndroid Build Coastguard Worker #include "allocator_common.h" 13*76559068SAndroid Build Coastguard Worker #include "bytemap.h" 14*76559068SAndroid Build Coastguard Worker #include "common.h" 15*76559068SAndroid Build Coastguard Worker #include "list.h" 16*76559068SAndroid Build Coastguard Worker #include "local_cache.h" 17*76559068SAndroid Build Coastguard Worker #include "options.h" 18*76559068SAndroid Build Coastguard Worker #include "release.h" 19*76559068SAndroid Build Coastguard Worker #include "report.h" 20*76559068SAndroid Build Coastguard Worker #include "stats.h" 21*76559068SAndroid Build Coastguard Worker #include "string_utils.h" 22*76559068SAndroid Build Coastguard Worker #include "thread_annotations.h" 23*76559068SAndroid Build Coastguard Worker 24*76559068SAndroid Build Coastguard Worker namespace scudo { 25*76559068SAndroid Build Coastguard Worker 26*76559068SAndroid Build Coastguard Worker // SizeClassAllocator32 is an allocator for 32 or 64-bit address space. 27*76559068SAndroid Build Coastguard Worker // 28*76559068SAndroid Build Coastguard Worker // It maps Regions of 2^RegionSizeLog bytes aligned on a 2^RegionSizeLog bytes 29*76559068SAndroid Build Coastguard Worker // boundary, and keeps a bytemap of the mappable address space to track the size 30*76559068SAndroid Build Coastguard Worker // class they are associated with. 31*76559068SAndroid Build Coastguard Worker // 32*76559068SAndroid Build Coastguard Worker // Mapped regions are split into equally sized Blocks according to the size 33*76559068SAndroid Build Coastguard Worker // class they belong to, and the associated pointers are shuffled to prevent any 34*76559068SAndroid Build Coastguard Worker // predictable address pattern (the predictability increases with the block 35*76559068SAndroid Build Coastguard Worker // size). 36*76559068SAndroid Build Coastguard Worker // 37*76559068SAndroid Build Coastguard Worker // Regions for size class 0 are special and used to hold TransferBatches, which 38*76559068SAndroid Build Coastguard Worker // allow to transfer arrays of pointers from the global size class freelist to 39*76559068SAndroid Build Coastguard Worker // the thread specific freelist for said class, and back. 40*76559068SAndroid Build Coastguard Worker // 41*76559068SAndroid Build Coastguard Worker // Memory used by this allocator is never unmapped but can be partially 42*76559068SAndroid Build Coastguard Worker // reclaimed if the platform allows for it. 43*76559068SAndroid Build Coastguard Worker 44*76559068SAndroid Build Coastguard Worker template <typename Config> class SizeClassAllocator32 { 45*76559068SAndroid Build Coastguard Worker public: 46*76559068SAndroid Build Coastguard Worker typedef typename Config::CompactPtrT CompactPtrT; 47*76559068SAndroid Build Coastguard Worker typedef typename Config::SizeClassMap SizeClassMap; 48*76559068SAndroid Build Coastguard Worker static const uptr GroupSizeLog = Config::getGroupSizeLog(); 49*76559068SAndroid Build Coastguard Worker // The bytemap can only track UINT8_MAX - 1 classes. 50*76559068SAndroid Build Coastguard Worker static_assert(SizeClassMap::LargestClassId <= (UINT8_MAX - 1), ""); 51*76559068SAndroid Build Coastguard Worker // Regions should be large enough to hold the largest Block. 52*76559068SAndroid Build Coastguard Worker static_assert((1UL << Config::getRegionSizeLog()) >= SizeClassMap::MaxSize, 53*76559068SAndroid Build Coastguard Worker ""); 54*76559068SAndroid Build Coastguard Worker typedef SizeClassAllocator32<Config> ThisT; 55*76559068SAndroid Build Coastguard Worker typedef SizeClassAllocatorLocalCache<ThisT> CacheT; 56*76559068SAndroid Build Coastguard Worker typedef TransferBatch<ThisT> TransferBatchT; 57*76559068SAndroid Build Coastguard Worker typedef BatchGroup<ThisT> BatchGroupT; 58*76559068SAndroid Build Coastguard Worker getSizeByClassId(uptr ClassId)59*76559068SAndroid Build Coastguard Worker static uptr getSizeByClassId(uptr ClassId) { 60*76559068SAndroid Build Coastguard Worker return (ClassId == SizeClassMap::BatchClassId) 61*76559068SAndroid Build Coastguard Worker ? Max(sizeof(BatchGroupT), sizeof(TransferBatchT)) 62*76559068SAndroid Build Coastguard Worker : SizeClassMap::getSizeByClassId(ClassId); 63*76559068SAndroid Build Coastguard Worker } 64*76559068SAndroid Build Coastguard Worker canAllocate(uptr Size)65*76559068SAndroid Build Coastguard Worker static bool canAllocate(uptr Size) { return Size <= SizeClassMap::MaxSize; } 66*76559068SAndroid Build Coastguard Worker init(s32 ReleaseToOsInterval)67*76559068SAndroid Build Coastguard Worker void init(s32 ReleaseToOsInterval) NO_THREAD_SAFETY_ANALYSIS { 68*76559068SAndroid Build Coastguard Worker if (SCUDO_FUCHSIA) 69*76559068SAndroid Build Coastguard Worker reportError("SizeClassAllocator32 is not supported on Fuchsia"); 70*76559068SAndroid Build Coastguard Worker 71*76559068SAndroid Build Coastguard Worker if (SCUDO_TRUSTY) 72*76559068SAndroid Build Coastguard Worker reportError("SizeClassAllocator32 is not supported on Trusty"); 73*76559068SAndroid Build Coastguard Worker 74*76559068SAndroid Build Coastguard Worker DCHECK(isAligned(reinterpret_cast<uptr>(this), alignof(ThisT))); 75*76559068SAndroid Build Coastguard Worker PossibleRegions.init(); 76*76559068SAndroid Build Coastguard Worker u32 Seed; 77*76559068SAndroid Build Coastguard Worker const u64 Time = getMonotonicTimeFast(); 78*76559068SAndroid Build Coastguard Worker if (!getRandom(reinterpret_cast<void *>(&Seed), sizeof(Seed))) 79*76559068SAndroid Build Coastguard Worker Seed = static_cast<u32>( 80*76559068SAndroid Build Coastguard Worker Time ^ (reinterpret_cast<uptr>(SizeClassInfoArray) >> 6)); 81*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 82*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 83*76559068SAndroid Build Coastguard Worker Sci->RandState = getRandomU32(&Seed); 84*76559068SAndroid Build Coastguard Worker // Sci->MaxRegionIndex is already initialized to 0. 85*76559068SAndroid Build Coastguard Worker Sci->MinRegionIndex = NumRegions; 86*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.LastReleaseAtNs = Time; 87*76559068SAndroid Build Coastguard Worker } 88*76559068SAndroid Build Coastguard Worker 89*76559068SAndroid Build Coastguard Worker // The default value in the primary config has the higher priority. 90*76559068SAndroid Build Coastguard Worker if (Config::getDefaultReleaseToOsIntervalMs() != INT32_MIN) 91*76559068SAndroid Build Coastguard Worker ReleaseToOsInterval = Config::getDefaultReleaseToOsIntervalMs(); 92*76559068SAndroid Build Coastguard Worker setOption(Option::ReleaseInterval, static_cast<sptr>(ReleaseToOsInterval)); 93*76559068SAndroid Build Coastguard Worker } 94*76559068SAndroid Build Coastguard Worker unmapTestOnly()95*76559068SAndroid Build Coastguard Worker void unmapTestOnly() { 96*76559068SAndroid Build Coastguard Worker { 97*76559068SAndroid Build Coastguard Worker ScopedLock L(RegionsStashMutex); 98*76559068SAndroid Build Coastguard Worker while (NumberOfStashedRegions > 0) { 99*76559068SAndroid Build Coastguard Worker unmap(reinterpret_cast<void *>(RegionsStash[--NumberOfStashedRegions]), 100*76559068SAndroid Build Coastguard Worker RegionSize); 101*76559068SAndroid Build Coastguard Worker } 102*76559068SAndroid Build Coastguard Worker } 103*76559068SAndroid Build Coastguard Worker 104*76559068SAndroid Build Coastguard Worker uptr MinRegionIndex = NumRegions, MaxRegionIndex = 0; 105*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 106*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 107*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 108*76559068SAndroid Build Coastguard Worker if (Sci->MinRegionIndex < MinRegionIndex) 109*76559068SAndroid Build Coastguard Worker MinRegionIndex = Sci->MinRegionIndex; 110*76559068SAndroid Build Coastguard Worker if (Sci->MaxRegionIndex > MaxRegionIndex) 111*76559068SAndroid Build Coastguard Worker MaxRegionIndex = Sci->MaxRegionIndex; 112*76559068SAndroid Build Coastguard Worker *Sci = {}; 113*76559068SAndroid Build Coastguard Worker } 114*76559068SAndroid Build Coastguard Worker 115*76559068SAndroid Build Coastguard Worker ScopedLock L(ByteMapMutex); 116*76559068SAndroid Build Coastguard Worker for (uptr I = MinRegionIndex; I <= MaxRegionIndex; I++) 117*76559068SAndroid Build Coastguard Worker if (PossibleRegions[I]) 118*76559068SAndroid Build Coastguard Worker unmap(reinterpret_cast<void *>(I * RegionSize), RegionSize); 119*76559068SAndroid Build Coastguard Worker PossibleRegions.unmapTestOnly(); 120*76559068SAndroid Build Coastguard Worker } 121*76559068SAndroid Build Coastguard Worker 122*76559068SAndroid Build Coastguard Worker // When all blocks are freed, it has to be the same size as `AllocatedUser`. verifyAllBlocksAreReleasedTestOnly()123*76559068SAndroid Build Coastguard Worker void verifyAllBlocksAreReleasedTestOnly() { 124*76559068SAndroid Build Coastguard Worker // `BatchGroup` and `TransferBatch` also use the blocks from BatchClass. 125*76559068SAndroid Build Coastguard Worker uptr BatchClassUsedInFreeLists = 0; 126*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 127*76559068SAndroid Build Coastguard Worker // We have to count BatchClassUsedInFreeLists in other regions first. 128*76559068SAndroid Build Coastguard Worker if (I == SizeClassMap::BatchClassId) 129*76559068SAndroid Build Coastguard Worker continue; 130*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 131*76559068SAndroid Build Coastguard Worker ScopedLock L1(Sci->Mutex); 132*76559068SAndroid Build Coastguard Worker uptr TotalBlocks = 0; 133*76559068SAndroid Build Coastguard Worker for (BatchGroupT &BG : Sci->FreeListInfo.BlockList) { 134*76559068SAndroid Build Coastguard Worker // `BG::Batches` are `TransferBatches`. +1 for `BatchGroup`. 135*76559068SAndroid Build Coastguard Worker BatchClassUsedInFreeLists += BG.Batches.size() + 1; 136*76559068SAndroid Build Coastguard Worker for (const auto &It : BG.Batches) 137*76559068SAndroid Build Coastguard Worker TotalBlocks += It.getCount(); 138*76559068SAndroid Build Coastguard Worker } 139*76559068SAndroid Build Coastguard Worker 140*76559068SAndroid Build Coastguard Worker const uptr BlockSize = getSizeByClassId(I); 141*76559068SAndroid Build Coastguard Worker DCHECK_EQ(TotalBlocks, Sci->AllocatedUser / BlockSize); 142*76559068SAndroid Build Coastguard Worker DCHECK_EQ(Sci->FreeListInfo.PushedBlocks, Sci->FreeListInfo.PoppedBlocks); 143*76559068SAndroid Build Coastguard Worker } 144*76559068SAndroid Build Coastguard Worker 145*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(SizeClassMap::BatchClassId); 146*76559068SAndroid Build Coastguard Worker ScopedLock L1(Sci->Mutex); 147*76559068SAndroid Build Coastguard Worker uptr TotalBlocks = 0; 148*76559068SAndroid Build Coastguard Worker for (BatchGroupT &BG : Sci->FreeListInfo.BlockList) { 149*76559068SAndroid Build Coastguard Worker if (LIKELY(!BG.Batches.empty())) { 150*76559068SAndroid Build Coastguard Worker for (const auto &It : BG.Batches) 151*76559068SAndroid Build Coastguard Worker TotalBlocks += It.getCount(); 152*76559068SAndroid Build Coastguard Worker } else { 153*76559068SAndroid Build Coastguard Worker // `BatchGroup` with empty freelist doesn't have `TransferBatch` record 154*76559068SAndroid Build Coastguard Worker // itself. 155*76559068SAndroid Build Coastguard Worker ++TotalBlocks; 156*76559068SAndroid Build Coastguard Worker } 157*76559068SAndroid Build Coastguard Worker } 158*76559068SAndroid Build Coastguard Worker 159*76559068SAndroid Build Coastguard Worker const uptr BlockSize = getSizeByClassId(SizeClassMap::BatchClassId); 160*76559068SAndroid Build Coastguard Worker DCHECK_EQ(TotalBlocks + BatchClassUsedInFreeLists, 161*76559068SAndroid Build Coastguard Worker Sci->AllocatedUser / BlockSize); 162*76559068SAndroid Build Coastguard Worker const uptr BlocksInUse = 163*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PoppedBlocks - Sci->FreeListInfo.PushedBlocks; 164*76559068SAndroid Build Coastguard Worker DCHECK_EQ(BlocksInUse, BatchClassUsedInFreeLists); 165*76559068SAndroid Build Coastguard Worker } 166*76559068SAndroid Build Coastguard Worker compactPtr(UNUSED uptr ClassId,uptr Ptr)167*76559068SAndroid Build Coastguard Worker CompactPtrT compactPtr(UNUSED uptr ClassId, uptr Ptr) const { 168*76559068SAndroid Build Coastguard Worker return static_cast<CompactPtrT>(Ptr); 169*76559068SAndroid Build Coastguard Worker } 170*76559068SAndroid Build Coastguard Worker decompactPtr(UNUSED uptr ClassId,CompactPtrT CompactPtr)171*76559068SAndroid Build Coastguard Worker void *decompactPtr(UNUSED uptr ClassId, CompactPtrT CompactPtr) const { 172*76559068SAndroid Build Coastguard Worker return reinterpret_cast<void *>(static_cast<uptr>(CompactPtr)); 173*76559068SAndroid Build Coastguard Worker } 174*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(CompactPtrT CompactPtr)175*76559068SAndroid Build Coastguard Worker uptr compactPtrGroupBase(CompactPtrT CompactPtr) { 176*76559068SAndroid Build Coastguard Worker const uptr Mask = (static_cast<uptr>(1) << GroupSizeLog) - 1; 177*76559068SAndroid Build Coastguard Worker return CompactPtr & ~Mask; 178*76559068SAndroid Build Coastguard Worker } 179*76559068SAndroid Build Coastguard Worker decompactGroupBase(uptr CompactPtrGroupBase)180*76559068SAndroid Build Coastguard Worker uptr decompactGroupBase(uptr CompactPtrGroupBase) { 181*76559068SAndroid Build Coastguard Worker return CompactPtrGroupBase; 182*76559068SAndroid Build Coastguard Worker } 183*76559068SAndroid Build Coastguard Worker isSmallBlock(uptr BlockSize)184*76559068SAndroid Build Coastguard Worker ALWAYS_INLINE static bool isSmallBlock(uptr BlockSize) { 185*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached(); 186*76559068SAndroid Build Coastguard Worker return BlockSize < PageSize / 16U; 187*76559068SAndroid Build Coastguard Worker } 188*76559068SAndroid Build Coastguard Worker isLargeBlock(uptr BlockSize)189*76559068SAndroid Build Coastguard Worker ALWAYS_INLINE static bool isLargeBlock(uptr BlockSize) { 190*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached(); 191*76559068SAndroid Build Coastguard Worker return BlockSize > PageSize; 192*76559068SAndroid Build Coastguard Worker } 193*76559068SAndroid Build Coastguard Worker popBlocks(CacheT * C,uptr ClassId,CompactPtrT * ToArray,const u16 MaxBlockCount)194*76559068SAndroid Build Coastguard Worker u16 popBlocks(CacheT *C, uptr ClassId, CompactPtrT *ToArray, 195*76559068SAndroid Build Coastguard Worker const u16 MaxBlockCount) { 196*76559068SAndroid Build Coastguard Worker DCHECK_LT(ClassId, NumClasses); 197*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(ClassId); 198*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 199*76559068SAndroid Build Coastguard Worker 200*76559068SAndroid Build Coastguard Worker u16 PopCount = popBlocksImpl(C, ClassId, Sci, ToArray, MaxBlockCount); 201*76559068SAndroid Build Coastguard Worker if (UNLIKELY(PopCount == 0)) { 202*76559068SAndroid Build Coastguard Worker if (UNLIKELY(!populateFreeList(C, ClassId, Sci))) 203*76559068SAndroid Build Coastguard Worker return 0U; 204*76559068SAndroid Build Coastguard Worker PopCount = popBlocksImpl(C, ClassId, Sci, ToArray, MaxBlockCount); 205*76559068SAndroid Build Coastguard Worker DCHECK_NE(PopCount, 0U); 206*76559068SAndroid Build Coastguard Worker } 207*76559068SAndroid Build Coastguard Worker 208*76559068SAndroid Build Coastguard Worker return PopCount; 209*76559068SAndroid Build Coastguard Worker } 210*76559068SAndroid Build Coastguard Worker 211*76559068SAndroid Build Coastguard Worker // Push the array of free blocks to the designated batch group. pushBlocks(CacheT * C,uptr ClassId,CompactPtrT * Array,u32 Size)212*76559068SAndroid Build Coastguard Worker void pushBlocks(CacheT *C, uptr ClassId, CompactPtrT *Array, u32 Size) { 213*76559068SAndroid Build Coastguard Worker DCHECK_LT(ClassId, NumClasses); 214*76559068SAndroid Build Coastguard Worker DCHECK_GT(Size, 0); 215*76559068SAndroid Build Coastguard Worker 216*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(ClassId); 217*76559068SAndroid Build Coastguard Worker if (ClassId == SizeClassMap::BatchClassId) { 218*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 219*76559068SAndroid Build Coastguard Worker pushBatchClassBlocks(Sci, Array, Size); 220*76559068SAndroid Build Coastguard Worker return; 221*76559068SAndroid Build Coastguard Worker } 222*76559068SAndroid Build Coastguard Worker 223*76559068SAndroid Build Coastguard Worker // TODO(chiahungduan): Consider not doing grouping if the group size is not 224*76559068SAndroid Build Coastguard Worker // greater than the block size with a certain scale. 225*76559068SAndroid Build Coastguard Worker 226*76559068SAndroid Build Coastguard Worker // Sort the blocks so that blocks belonging to the same group can be pushed 227*76559068SAndroid Build Coastguard Worker // together. 228*76559068SAndroid Build Coastguard Worker bool SameGroup = true; 229*76559068SAndroid Build Coastguard Worker for (u32 I = 1; I < Size; ++I) { 230*76559068SAndroid Build Coastguard Worker if (compactPtrGroupBase(Array[I - 1]) != compactPtrGroupBase(Array[I])) 231*76559068SAndroid Build Coastguard Worker SameGroup = false; 232*76559068SAndroid Build Coastguard Worker CompactPtrT Cur = Array[I]; 233*76559068SAndroid Build Coastguard Worker u32 J = I; 234*76559068SAndroid Build Coastguard Worker while (J > 0 && 235*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(Cur) < compactPtrGroupBase(Array[J - 1])) { 236*76559068SAndroid Build Coastguard Worker Array[J] = Array[J - 1]; 237*76559068SAndroid Build Coastguard Worker --J; 238*76559068SAndroid Build Coastguard Worker } 239*76559068SAndroid Build Coastguard Worker Array[J] = Cur; 240*76559068SAndroid Build Coastguard Worker } 241*76559068SAndroid Build Coastguard Worker 242*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 243*76559068SAndroid Build Coastguard Worker pushBlocksImpl(C, ClassId, Sci, Array, Size, SameGroup); 244*76559068SAndroid Build Coastguard Worker } 245*76559068SAndroid Build Coastguard Worker disable()246*76559068SAndroid Build Coastguard Worker void disable() NO_THREAD_SAFETY_ANALYSIS { 247*76559068SAndroid Build Coastguard Worker // The BatchClassId must be locked last since other classes can use it. 248*76559068SAndroid Build Coastguard Worker for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--) { 249*76559068SAndroid Build Coastguard Worker if (static_cast<uptr>(I) == SizeClassMap::BatchClassId) 250*76559068SAndroid Build Coastguard Worker continue; 251*76559068SAndroid Build Coastguard Worker getSizeClassInfo(static_cast<uptr>(I))->Mutex.lock(); 252*76559068SAndroid Build Coastguard Worker } 253*76559068SAndroid Build Coastguard Worker getSizeClassInfo(SizeClassMap::BatchClassId)->Mutex.lock(); 254*76559068SAndroid Build Coastguard Worker RegionsStashMutex.lock(); 255*76559068SAndroid Build Coastguard Worker ByteMapMutex.lock(); 256*76559068SAndroid Build Coastguard Worker } 257*76559068SAndroid Build Coastguard Worker enable()258*76559068SAndroid Build Coastguard Worker void enable() NO_THREAD_SAFETY_ANALYSIS { 259*76559068SAndroid Build Coastguard Worker ByteMapMutex.unlock(); 260*76559068SAndroid Build Coastguard Worker RegionsStashMutex.unlock(); 261*76559068SAndroid Build Coastguard Worker getSizeClassInfo(SizeClassMap::BatchClassId)->Mutex.unlock(); 262*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 263*76559068SAndroid Build Coastguard Worker if (I == SizeClassMap::BatchClassId) 264*76559068SAndroid Build Coastguard Worker continue; 265*76559068SAndroid Build Coastguard Worker getSizeClassInfo(I)->Mutex.unlock(); 266*76559068SAndroid Build Coastguard Worker } 267*76559068SAndroid Build Coastguard Worker } 268*76559068SAndroid Build Coastguard Worker iterateOverBlocks(F Callback)269*76559068SAndroid Build Coastguard Worker template <typename F> void iterateOverBlocks(F Callback) { 270*76559068SAndroid Build Coastguard Worker uptr MinRegionIndex = NumRegions, MaxRegionIndex = 0; 271*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 272*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 273*76559068SAndroid Build Coastguard Worker // TODO: The call of `iterateOverBlocks` requires disabling 274*76559068SAndroid Build Coastguard Worker // SizeClassAllocator32. We may consider locking each region on demand 275*76559068SAndroid Build Coastguard Worker // only. 276*76559068SAndroid Build Coastguard Worker Sci->Mutex.assertHeld(); 277*76559068SAndroid Build Coastguard Worker if (Sci->MinRegionIndex < MinRegionIndex) 278*76559068SAndroid Build Coastguard Worker MinRegionIndex = Sci->MinRegionIndex; 279*76559068SAndroid Build Coastguard Worker if (Sci->MaxRegionIndex > MaxRegionIndex) 280*76559068SAndroid Build Coastguard Worker MaxRegionIndex = Sci->MaxRegionIndex; 281*76559068SAndroid Build Coastguard Worker } 282*76559068SAndroid Build Coastguard Worker 283*76559068SAndroid Build Coastguard Worker // SizeClassAllocator32 is disabled, i.e., ByteMapMutex is held. 284*76559068SAndroid Build Coastguard Worker ByteMapMutex.assertHeld(); 285*76559068SAndroid Build Coastguard Worker 286*76559068SAndroid Build Coastguard Worker for (uptr I = MinRegionIndex; I <= MaxRegionIndex; I++) { 287*76559068SAndroid Build Coastguard Worker if (PossibleRegions[I] && 288*76559068SAndroid Build Coastguard Worker (PossibleRegions[I] - 1U) != SizeClassMap::BatchClassId) { 289*76559068SAndroid Build Coastguard Worker const uptr BlockSize = getSizeByClassId(PossibleRegions[I] - 1U); 290*76559068SAndroid Build Coastguard Worker const uptr From = I * RegionSize; 291*76559068SAndroid Build Coastguard Worker const uptr To = From + (RegionSize / BlockSize) * BlockSize; 292*76559068SAndroid Build Coastguard Worker for (uptr Block = From; Block < To; Block += BlockSize) 293*76559068SAndroid Build Coastguard Worker Callback(Block); 294*76559068SAndroid Build Coastguard Worker } 295*76559068SAndroid Build Coastguard Worker } 296*76559068SAndroid Build Coastguard Worker } 297*76559068SAndroid Build Coastguard Worker getStats(ScopedString * Str)298*76559068SAndroid Build Coastguard Worker void getStats(ScopedString *Str) { 299*76559068SAndroid Build Coastguard Worker // TODO(kostyak): get the RSS per region. 300*76559068SAndroid Build Coastguard Worker uptr TotalMapped = 0; 301*76559068SAndroid Build Coastguard Worker uptr PoppedBlocks = 0; 302*76559068SAndroid Build Coastguard Worker uptr PushedBlocks = 0; 303*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 304*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 305*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 306*76559068SAndroid Build Coastguard Worker TotalMapped += Sci->AllocatedUser; 307*76559068SAndroid Build Coastguard Worker PoppedBlocks += Sci->FreeListInfo.PoppedBlocks; 308*76559068SAndroid Build Coastguard Worker PushedBlocks += Sci->FreeListInfo.PushedBlocks; 309*76559068SAndroid Build Coastguard Worker } 310*76559068SAndroid Build Coastguard Worker Str->append("Stats: SizeClassAllocator32: %zuM mapped in %zu allocations; " 311*76559068SAndroid Build Coastguard Worker "remains %zu\n", 312*76559068SAndroid Build Coastguard Worker TotalMapped >> 20, PoppedBlocks, PoppedBlocks - PushedBlocks); 313*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 314*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 315*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 316*76559068SAndroid Build Coastguard Worker getStats(Str, I, Sci); 317*76559068SAndroid Build Coastguard Worker } 318*76559068SAndroid Build Coastguard Worker } 319*76559068SAndroid Build Coastguard Worker getFragmentationInfo(ScopedString * Str)320*76559068SAndroid Build Coastguard Worker void getFragmentationInfo(ScopedString *Str) { 321*76559068SAndroid Build Coastguard Worker Str->append( 322*76559068SAndroid Build Coastguard Worker "Fragmentation Stats: SizeClassAllocator32: page size = %zu bytes\n", 323*76559068SAndroid Build Coastguard Worker getPageSizeCached()); 324*76559068SAndroid Build Coastguard Worker 325*76559068SAndroid Build Coastguard Worker for (uptr I = 1; I < NumClasses; I++) { 326*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 327*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 328*76559068SAndroid Build Coastguard Worker getSizeClassFragmentationInfo(Sci, I, Str); 329*76559068SAndroid Build Coastguard Worker } 330*76559068SAndroid Build Coastguard Worker } 331*76559068SAndroid Build Coastguard Worker getMemoryGroupFragmentationInfo(ScopedString * Str)332*76559068SAndroid Build Coastguard Worker void getMemoryGroupFragmentationInfo(ScopedString *Str) { 333*76559068SAndroid Build Coastguard Worker // Each region is also a memory group because region size is the same as 334*76559068SAndroid Build Coastguard Worker // group size. 335*76559068SAndroid Build Coastguard Worker getFragmentationInfo(Str); 336*76559068SAndroid Build Coastguard Worker } 337*76559068SAndroid Build Coastguard Worker setOption(Option O,sptr Value)338*76559068SAndroid Build Coastguard Worker bool setOption(Option O, sptr Value) { 339*76559068SAndroid Build Coastguard Worker if (O == Option::ReleaseInterval) { 340*76559068SAndroid Build Coastguard Worker const s32 Interval = Max( 341*76559068SAndroid Build Coastguard Worker Min(static_cast<s32>(Value), Config::getMaxReleaseToOsIntervalMs()), 342*76559068SAndroid Build Coastguard Worker Config::getMinReleaseToOsIntervalMs()); 343*76559068SAndroid Build Coastguard Worker atomic_store_relaxed(&ReleaseToOsIntervalMs, Interval); 344*76559068SAndroid Build Coastguard Worker return true; 345*76559068SAndroid Build Coastguard Worker } 346*76559068SAndroid Build Coastguard Worker // Not supported by the Primary, but not an error either. 347*76559068SAndroid Build Coastguard Worker return true; 348*76559068SAndroid Build Coastguard Worker } 349*76559068SAndroid Build Coastguard Worker tryReleaseToOS(uptr ClassId,ReleaseToOS ReleaseType)350*76559068SAndroid Build Coastguard Worker uptr tryReleaseToOS(uptr ClassId, ReleaseToOS ReleaseType) { 351*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(ClassId); 352*76559068SAndroid Build Coastguard Worker // TODO: Once we have separate locks like primary64, we may consider using 353*76559068SAndroid Build Coastguard Worker // tryLock() as well. 354*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 355*76559068SAndroid Build Coastguard Worker return releaseToOSMaybe(Sci, ClassId, ReleaseType); 356*76559068SAndroid Build Coastguard Worker } 357*76559068SAndroid Build Coastguard Worker releaseToOS(ReleaseToOS ReleaseType)358*76559068SAndroid Build Coastguard Worker uptr releaseToOS(ReleaseToOS ReleaseType) { 359*76559068SAndroid Build Coastguard Worker uptr TotalReleasedBytes = 0; 360*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 361*76559068SAndroid Build Coastguard Worker if (I == SizeClassMap::BatchClassId) 362*76559068SAndroid Build Coastguard Worker continue; 363*76559068SAndroid Build Coastguard Worker SizeClassInfo *Sci = getSizeClassInfo(I); 364*76559068SAndroid Build Coastguard Worker ScopedLock L(Sci->Mutex); 365*76559068SAndroid Build Coastguard Worker TotalReleasedBytes += releaseToOSMaybe(Sci, I, ReleaseType); 366*76559068SAndroid Build Coastguard Worker } 367*76559068SAndroid Build Coastguard Worker return TotalReleasedBytes; 368*76559068SAndroid Build Coastguard Worker } 369*76559068SAndroid Build Coastguard Worker getRegionInfoArrayAddress()370*76559068SAndroid Build Coastguard Worker const char *getRegionInfoArrayAddress() const { return nullptr; } getRegionInfoArraySize()371*76559068SAndroid Build Coastguard Worker static uptr getRegionInfoArraySize() { return 0; } 372*76559068SAndroid Build Coastguard Worker findNearestBlock(UNUSED const char * RegionInfoData,UNUSED uptr Ptr)373*76559068SAndroid Build Coastguard Worker static BlockInfo findNearestBlock(UNUSED const char *RegionInfoData, 374*76559068SAndroid Build Coastguard Worker UNUSED uptr Ptr) { 375*76559068SAndroid Build Coastguard Worker return {}; 376*76559068SAndroid Build Coastguard Worker } 377*76559068SAndroid Build Coastguard Worker 378*76559068SAndroid Build Coastguard Worker AtomicOptions Options; 379*76559068SAndroid Build Coastguard Worker 380*76559068SAndroid Build Coastguard Worker private: 381*76559068SAndroid Build Coastguard Worker static const uptr NumClasses = SizeClassMap::NumClasses; 382*76559068SAndroid Build Coastguard Worker static const uptr RegionSize = 1UL << Config::getRegionSizeLog(); 383*76559068SAndroid Build Coastguard Worker static const uptr NumRegions = SCUDO_MMAP_RANGE_SIZE >> 384*76559068SAndroid Build Coastguard Worker Config::getRegionSizeLog(); 385*76559068SAndroid Build Coastguard Worker static const u32 MaxNumBatches = SCUDO_ANDROID ? 4U : 8U; 386*76559068SAndroid Build Coastguard Worker typedef FlatByteMap<NumRegions> ByteMap; 387*76559068SAndroid Build Coastguard Worker 388*76559068SAndroid Build Coastguard Worker struct ReleaseToOsInfo { 389*76559068SAndroid Build Coastguard Worker uptr BytesInFreeListAtLastCheckpoint; 390*76559068SAndroid Build Coastguard Worker uptr RangesReleased; 391*76559068SAndroid Build Coastguard Worker uptr LastReleasedBytes; 392*76559068SAndroid Build Coastguard Worker u64 LastReleaseAtNs; 393*76559068SAndroid Build Coastguard Worker }; 394*76559068SAndroid Build Coastguard Worker 395*76559068SAndroid Build Coastguard Worker struct BlocksInfo { 396*76559068SAndroid Build Coastguard Worker SinglyLinkedList<BatchGroupT> BlockList = {}; 397*76559068SAndroid Build Coastguard Worker uptr PoppedBlocks = 0; 398*76559068SAndroid Build Coastguard Worker uptr PushedBlocks = 0; 399*76559068SAndroid Build Coastguard Worker }; 400*76559068SAndroid Build Coastguard Worker 401*76559068SAndroid Build Coastguard Worker struct alignas(SCUDO_CACHE_LINE_SIZE) SizeClassInfo { 402*76559068SAndroid Build Coastguard Worker HybridMutex Mutex; 403*76559068SAndroid Build Coastguard Worker BlocksInfo FreeListInfo GUARDED_BY(Mutex); 404*76559068SAndroid Build Coastguard Worker uptr CurrentRegion GUARDED_BY(Mutex); 405*76559068SAndroid Build Coastguard Worker uptr CurrentRegionAllocated GUARDED_BY(Mutex); 406*76559068SAndroid Build Coastguard Worker u32 RandState; 407*76559068SAndroid Build Coastguard Worker uptr AllocatedUser GUARDED_BY(Mutex); 408*76559068SAndroid Build Coastguard Worker // Lowest & highest region index allocated for this size class, to avoid 409*76559068SAndroid Build Coastguard Worker // looping through the whole NumRegions. 410*76559068SAndroid Build Coastguard Worker uptr MinRegionIndex GUARDED_BY(Mutex); 411*76559068SAndroid Build Coastguard Worker uptr MaxRegionIndex GUARDED_BY(Mutex); 412*76559068SAndroid Build Coastguard Worker ReleaseToOsInfo ReleaseInfo GUARDED_BY(Mutex); 413*76559068SAndroid Build Coastguard Worker }; 414*76559068SAndroid Build Coastguard Worker static_assert(sizeof(SizeClassInfo) % SCUDO_CACHE_LINE_SIZE == 0, ""); 415*76559068SAndroid Build Coastguard Worker computeRegionId(uptr Mem)416*76559068SAndroid Build Coastguard Worker uptr computeRegionId(uptr Mem) { 417*76559068SAndroid Build Coastguard Worker const uptr Id = Mem >> Config::getRegionSizeLog(); 418*76559068SAndroid Build Coastguard Worker CHECK_LT(Id, NumRegions); 419*76559068SAndroid Build Coastguard Worker return Id; 420*76559068SAndroid Build Coastguard Worker } 421*76559068SAndroid Build Coastguard Worker allocateRegionSlow()422*76559068SAndroid Build Coastguard Worker uptr allocateRegionSlow() { 423*76559068SAndroid Build Coastguard Worker uptr MapSize = 2 * RegionSize; 424*76559068SAndroid Build Coastguard Worker const uptr MapBase = reinterpret_cast<uptr>( 425*76559068SAndroid Build Coastguard Worker map(nullptr, MapSize, "scudo:primary", MAP_ALLOWNOMEM)); 426*76559068SAndroid Build Coastguard Worker if (!MapBase) 427*76559068SAndroid Build Coastguard Worker return 0; 428*76559068SAndroid Build Coastguard Worker const uptr MapEnd = MapBase + MapSize; 429*76559068SAndroid Build Coastguard Worker uptr Region = MapBase; 430*76559068SAndroid Build Coastguard Worker if (isAligned(Region, RegionSize)) { 431*76559068SAndroid Build Coastguard Worker ScopedLock L(RegionsStashMutex); 432*76559068SAndroid Build Coastguard Worker if (NumberOfStashedRegions < MaxStashedRegions) 433*76559068SAndroid Build Coastguard Worker RegionsStash[NumberOfStashedRegions++] = MapBase + RegionSize; 434*76559068SAndroid Build Coastguard Worker else 435*76559068SAndroid Build Coastguard Worker MapSize = RegionSize; 436*76559068SAndroid Build Coastguard Worker } else { 437*76559068SAndroid Build Coastguard Worker Region = roundUp(MapBase, RegionSize); 438*76559068SAndroid Build Coastguard Worker unmap(reinterpret_cast<void *>(MapBase), Region - MapBase); 439*76559068SAndroid Build Coastguard Worker MapSize = RegionSize; 440*76559068SAndroid Build Coastguard Worker } 441*76559068SAndroid Build Coastguard Worker const uptr End = Region + MapSize; 442*76559068SAndroid Build Coastguard Worker if (End != MapEnd) 443*76559068SAndroid Build Coastguard Worker unmap(reinterpret_cast<void *>(End), MapEnd - End); 444*76559068SAndroid Build Coastguard Worker 445*76559068SAndroid Build Coastguard Worker DCHECK_EQ(Region % RegionSize, 0U); 446*76559068SAndroid Build Coastguard Worker static_assert(Config::getRegionSizeLog() == GroupSizeLog, 447*76559068SAndroid Build Coastguard Worker "Memory group should be the same size as Region"); 448*76559068SAndroid Build Coastguard Worker 449*76559068SAndroid Build Coastguard Worker return Region; 450*76559068SAndroid Build Coastguard Worker } 451*76559068SAndroid Build Coastguard Worker allocateRegion(SizeClassInfo * Sci,uptr ClassId)452*76559068SAndroid Build Coastguard Worker uptr allocateRegion(SizeClassInfo *Sci, uptr ClassId) REQUIRES(Sci->Mutex) { 453*76559068SAndroid Build Coastguard Worker DCHECK_LT(ClassId, NumClasses); 454*76559068SAndroid Build Coastguard Worker uptr Region = 0; 455*76559068SAndroid Build Coastguard Worker { 456*76559068SAndroid Build Coastguard Worker ScopedLock L(RegionsStashMutex); 457*76559068SAndroid Build Coastguard Worker if (NumberOfStashedRegions > 0) 458*76559068SAndroid Build Coastguard Worker Region = RegionsStash[--NumberOfStashedRegions]; 459*76559068SAndroid Build Coastguard Worker } 460*76559068SAndroid Build Coastguard Worker if (!Region) 461*76559068SAndroid Build Coastguard Worker Region = allocateRegionSlow(); 462*76559068SAndroid Build Coastguard Worker if (LIKELY(Region)) { 463*76559068SAndroid Build Coastguard Worker // Sci->Mutex is held by the caller, updating the Min/Max is safe. 464*76559068SAndroid Build Coastguard Worker const uptr RegionIndex = computeRegionId(Region); 465*76559068SAndroid Build Coastguard Worker if (RegionIndex < Sci->MinRegionIndex) 466*76559068SAndroid Build Coastguard Worker Sci->MinRegionIndex = RegionIndex; 467*76559068SAndroid Build Coastguard Worker if (RegionIndex > Sci->MaxRegionIndex) 468*76559068SAndroid Build Coastguard Worker Sci->MaxRegionIndex = RegionIndex; 469*76559068SAndroid Build Coastguard Worker ScopedLock L(ByteMapMutex); 470*76559068SAndroid Build Coastguard Worker PossibleRegions.set(RegionIndex, static_cast<u8>(ClassId + 1U)); 471*76559068SAndroid Build Coastguard Worker } 472*76559068SAndroid Build Coastguard Worker return Region; 473*76559068SAndroid Build Coastguard Worker } 474*76559068SAndroid Build Coastguard Worker getSizeClassInfo(uptr ClassId)475*76559068SAndroid Build Coastguard Worker SizeClassInfo *getSizeClassInfo(uptr ClassId) { 476*76559068SAndroid Build Coastguard Worker DCHECK_LT(ClassId, NumClasses); 477*76559068SAndroid Build Coastguard Worker return &SizeClassInfoArray[ClassId]; 478*76559068SAndroid Build Coastguard Worker } 479*76559068SAndroid Build Coastguard Worker pushBatchClassBlocks(SizeClassInfo * Sci,CompactPtrT * Array,u32 Size)480*76559068SAndroid Build Coastguard Worker void pushBatchClassBlocks(SizeClassInfo *Sci, CompactPtrT *Array, u32 Size) 481*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 482*76559068SAndroid Build Coastguard Worker DCHECK_EQ(Sci, getSizeClassInfo(SizeClassMap::BatchClassId)); 483*76559068SAndroid Build Coastguard Worker 484*76559068SAndroid Build Coastguard Worker // Free blocks are recorded by TransferBatch in freelist for all 485*76559068SAndroid Build Coastguard Worker // size-classes. In addition, TransferBatch is allocated from BatchClassId. 486*76559068SAndroid Build Coastguard Worker // In order not to use additional block to record the free blocks in 487*76559068SAndroid Build Coastguard Worker // BatchClassId, they are self-contained. I.e., A TransferBatch records the 488*76559068SAndroid Build Coastguard Worker // block address of itself. See the figure below: 489*76559068SAndroid Build Coastguard Worker // 490*76559068SAndroid Build Coastguard Worker // TransferBatch at 0xABCD 491*76559068SAndroid Build Coastguard Worker // +----------------------------+ 492*76559068SAndroid Build Coastguard Worker // | Free blocks' addr | 493*76559068SAndroid Build Coastguard Worker // | +------+------+------+ | 494*76559068SAndroid Build Coastguard Worker // | |0xABCD|... |... | | 495*76559068SAndroid Build Coastguard Worker // | +------+------+------+ | 496*76559068SAndroid Build Coastguard Worker // +----------------------------+ 497*76559068SAndroid Build Coastguard Worker // 498*76559068SAndroid Build Coastguard Worker // When we allocate all the free blocks in the TransferBatch, the block used 499*76559068SAndroid Build Coastguard Worker // by TransferBatch is also free for use. We don't need to recycle the 500*76559068SAndroid Build Coastguard Worker // TransferBatch. Note that the correctness is maintained by the invariant, 501*76559068SAndroid Build Coastguard Worker // 502*76559068SAndroid Build Coastguard Worker // Each popBlocks() request returns the entire TransferBatch. Returning 503*76559068SAndroid Build Coastguard Worker // part of the blocks in a TransferBatch is invalid. 504*76559068SAndroid Build Coastguard Worker // 505*76559068SAndroid Build Coastguard Worker // This ensures that TransferBatch won't leak the address itself while it's 506*76559068SAndroid Build Coastguard Worker // still holding other valid data. 507*76559068SAndroid Build Coastguard Worker // 508*76559068SAndroid Build Coastguard Worker // Besides, BatchGroup is also allocated from BatchClassId and has its 509*76559068SAndroid Build Coastguard Worker // address recorded in the TransferBatch too. To maintain the correctness, 510*76559068SAndroid Build Coastguard Worker // 511*76559068SAndroid Build Coastguard Worker // The address of BatchGroup is always recorded in the last TransferBatch 512*76559068SAndroid Build Coastguard Worker // in the freelist (also imply that the freelist should only be 513*76559068SAndroid Build Coastguard Worker // updated with push_front). Once the last TransferBatch is popped, 514*76559068SAndroid Build Coastguard Worker // the block used by BatchGroup is also free for use. 515*76559068SAndroid Build Coastguard Worker // 516*76559068SAndroid Build Coastguard Worker // With this approach, the blocks used by BatchGroup and TransferBatch are 517*76559068SAndroid Build Coastguard Worker // reusable and don't need additional space for them. 518*76559068SAndroid Build Coastguard Worker 519*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PushedBlocks += Size; 520*76559068SAndroid Build Coastguard Worker BatchGroupT *BG = Sci->FreeListInfo.BlockList.front(); 521*76559068SAndroid Build Coastguard Worker 522*76559068SAndroid Build Coastguard Worker if (BG == nullptr) { 523*76559068SAndroid Build Coastguard Worker // Construct `BatchGroup` on the last element. 524*76559068SAndroid Build Coastguard Worker BG = reinterpret_cast<BatchGroupT *>( 525*76559068SAndroid Build Coastguard Worker decompactPtr(SizeClassMap::BatchClassId, Array[Size - 1])); 526*76559068SAndroid Build Coastguard Worker --Size; 527*76559068SAndroid Build Coastguard Worker BG->Batches.clear(); 528*76559068SAndroid Build Coastguard Worker // BatchClass hasn't enabled memory group. Use `0` to indicate there's no 529*76559068SAndroid Build Coastguard Worker // memory group here. 530*76559068SAndroid Build Coastguard Worker BG->CompactPtrGroupBase = 0; 531*76559068SAndroid Build Coastguard Worker BG->BytesInBGAtLastCheckpoint = 0; 532*76559068SAndroid Build Coastguard Worker BG->MaxCachedPerBatch = 533*76559068SAndroid Build Coastguard Worker CacheT::getMaxCached(getSizeByClassId(SizeClassMap::BatchClassId)); 534*76559068SAndroid Build Coastguard Worker 535*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.push_front(BG); 536*76559068SAndroid Build Coastguard Worker } 537*76559068SAndroid Build Coastguard Worker 538*76559068SAndroid Build Coastguard Worker if (UNLIKELY(Size == 0)) 539*76559068SAndroid Build Coastguard Worker return; 540*76559068SAndroid Build Coastguard Worker 541*76559068SAndroid Build Coastguard Worker // This happens under 2 cases. 542*76559068SAndroid Build Coastguard Worker // 1. just allocated a new `BatchGroup`. 543*76559068SAndroid Build Coastguard Worker // 2. Only 1 block is pushed when the freelist is empty. 544*76559068SAndroid Build Coastguard Worker if (BG->Batches.empty()) { 545*76559068SAndroid Build Coastguard Worker // Construct the `TransferBatch` on the last element. 546*76559068SAndroid Build Coastguard Worker TransferBatchT *TB = reinterpret_cast<TransferBatchT *>( 547*76559068SAndroid Build Coastguard Worker decompactPtr(SizeClassMap::BatchClassId, Array[Size - 1])); 548*76559068SAndroid Build Coastguard Worker TB->clear(); 549*76559068SAndroid Build Coastguard Worker // As mentioned above, addresses of `TransferBatch` and `BatchGroup` are 550*76559068SAndroid Build Coastguard Worker // recorded in the TransferBatch. 551*76559068SAndroid Build Coastguard Worker TB->add(Array[Size - 1]); 552*76559068SAndroid Build Coastguard Worker TB->add( 553*76559068SAndroid Build Coastguard Worker compactPtr(SizeClassMap::BatchClassId, reinterpret_cast<uptr>(BG))); 554*76559068SAndroid Build Coastguard Worker --Size; 555*76559068SAndroid Build Coastguard Worker BG->Batches.push_front(TB); 556*76559068SAndroid Build Coastguard Worker } 557*76559068SAndroid Build Coastguard Worker 558*76559068SAndroid Build Coastguard Worker TransferBatchT *CurBatch = BG->Batches.front(); 559*76559068SAndroid Build Coastguard Worker DCHECK_NE(CurBatch, nullptr); 560*76559068SAndroid Build Coastguard Worker 561*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < Size;) { 562*76559068SAndroid Build Coastguard Worker u16 UnusedSlots = 563*76559068SAndroid Build Coastguard Worker static_cast<u16>(BG->MaxCachedPerBatch - CurBatch->getCount()); 564*76559068SAndroid Build Coastguard Worker if (UnusedSlots == 0) { 565*76559068SAndroid Build Coastguard Worker CurBatch = reinterpret_cast<TransferBatchT *>( 566*76559068SAndroid Build Coastguard Worker decompactPtr(SizeClassMap::BatchClassId, Array[I])); 567*76559068SAndroid Build Coastguard Worker CurBatch->clear(); 568*76559068SAndroid Build Coastguard Worker // Self-contained 569*76559068SAndroid Build Coastguard Worker CurBatch->add(Array[I]); 570*76559068SAndroid Build Coastguard Worker ++I; 571*76559068SAndroid Build Coastguard Worker // TODO(chiahungduan): Avoid the use of push_back() in `Batches` of 572*76559068SAndroid Build Coastguard Worker // BatchClassId. 573*76559068SAndroid Build Coastguard Worker BG->Batches.push_front(CurBatch); 574*76559068SAndroid Build Coastguard Worker UnusedSlots = static_cast<u16>(BG->MaxCachedPerBatch - 1); 575*76559068SAndroid Build Coastguard Worker } 576*76559068SAndroid Build Coastguard Worker // `UnusedSlots` is u16 so the result will be also fit in u16. 577*76559068SAndroid Build Coastguard Worker const u16 AppendSize = static_cast<u16>(Min<u32>(UnusedSlots, Size - I)); 578*76559068SAndroid Build Coastguard Worker CurBatch->appendFromArray(&Array[I], AppendSize); 579*76559068SAndroid Build Coastguard Worker I += AppendSize; 580*76559068SAndroid Build Coastguard Worker } 581*76559068SAndroid Build Coastguard Worker } 582*76559068SAndroid Build Coastguard Worker // Push the blocks to their batch group. The layout will be like, 583*76559068SAndroid Build Coastguard Worker // 584*76559068SAndroid Build Coastguard Worker // FreeListInfo.BlockList - > BG -> BG -> BG 585*76559068SAndroid Build Coastguard Worker // | | | 586*76559068SAndroid Build Coastguard Worker // v v v 587*76559068SAndroid Build Coastguard Worker // TB TB TB 588*76559068SAndroid Build Coastguard Worker // | 589*76559068SAndroid Build Coastguard Worker // v 590*76559068SAndroid Build Coastguard Worker // TB 591*76559068SAndroid Build Coastguard Worker // 592*76559068SAndroid Build Coastguard Worker // Each BlockGroup(BG) will associate with unique group id and the free blocks 593*76559068SAndroid Build Coastguard Worker // are managed by a list of TransferBatch(TB). To reduce the time of inserting 594*76559068SAndroid Build Coastguard Worker // blocks, BGs are sorted and the input `Array` are supposed to be sorted so 595*76559068SAndroid Build Coastguard Worker // that we can get better performance of maintaining sorted property. 596*76559068SAndroid Build Coastguard Worker // Use `SameGroup=true` to indicate that all blocks in the array are from the 597*76559068SAndroid Build Coastguard Worker // same group then we will skip checking the group id of each block. 598*76559068SAndroid Build Coastguard Worker // 599*76559068SAndroid Build Coastguard Worker // The region mutex needs to be held while calling this method. 600*76559068SAndroid Build Coastguard Worker void pushBlocksImpl(CacheT *C, uptr ClassId, SizeClassInfo *Sci, 601*76559068SAndroid Build Coastguard Worker CompactPtrT *Array, u32 Size, bool SameGroup = false) 602*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 603*76559068SAndroid Build Coastguard Worker DCHECK_NE(ClassId, SizeClassMap::BatchClassId); 604*76559068SAndroid Build Coastguard Worker DCHECK_GT(Size, 0U); 605*76559068SAndroid Build Coastguard Worker 606*76559068SAndroid Build Coastguard Worker auto CreateGroup = [&](uptr CompactPtrGroupBase) { 607*76559068SAndroid Build Coastguard Worker BatchGroupT *BG = 608*76559068SAndroid Build Coastguard Worker reinterpret_cast<BatchGroupT *>(C->getBatchClassBlock()); 609*76559068SAndroid Build Coastguard Worker BG->Batches.clear(); 610*76559068SAndroid Build Coastguard Worker TransferBatchT *TB = 611*76559068SAndroid Build Coastguard Worker reinterpret_cast<TransferBatchT *>(C->getBatchClassBlock()); 612*76559068SAndroid Build Coastguard Worker TB->clear(); 613*76559068SAndroid Build Coastguard Worker 614*76559068SAndroid Build Coastguard Worker BG->CompactPtrGroupBase = CompactPtrGroupBase; 615*76559068SAndroid Build Coastguard Worker BG->Batches.push_front(TB); 616*76559068SAndroid Build Coastguard Worker BG->BytesInBGAtLastCheckpoint = 0; 617*76559068SAndroid Build Coastguard Worker BG->MaxCachedPerBatch = TransferBatchT::MaxNumCached; 618*76559068SAndroid Build Coastguard Worker 619*76559068SAndroid Build Coastguard Worker return BG; 620*76559068SAndroid Build Coastguard Worker }; 621*76559068SAndroid Build Coastguard Worker 622*76559068SAndroid Build Coastguard Worker auto InsertBlocks = [&](BatchGroupT *BG, CompactPtrT *Array, u32 Size) { 623*76559068SAndroid Build Coastguard Worker SinglyLinkedList<TransferBatchT> &Batches = BG->Batches; 624*76559068SAndroid Build Coastguard Worker TransferBatchT *CurBatch = Batches.front(); 625*76559068SAndroid Build Coastguard Worker DCHECK_NE(CurBatch, nullptr); 626*76559068SAndroid Build Coastguard Worker 627*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < Size;) { 628*76559068SAndroid Build Coastguard Worker DCHECK_GE(BG->MaxCachedPerBatch, CurBatch->getCount()); 629*76559068SAndroid Build Coastguard Worker u16 UnusedSlots = 630*76559068SAndroid Build Coastguard Worker static_cast<u16>(BG->MaxCachedPerBatch - CurBatch->getCount()); 631*76559068SAndroid Build Coastguard Worker if (UnusedSlots == 0) { 632*76559068SAndroid Build Coastguard Worker CurBatch = 633*76559068SAndroid Build Coastguard Worker reinterpret_cast<TransferBatchT *>(C->getBatchClassBlock()); 634*76559068SAndroid Build Coastguard Worker CurBatch->clear(); 635*76559068SAndroid Build Coastguard Worker Batches.push_front(CurBatch); 636*76559068SAndroid Build Coastguard Worker UnusedSlots = BG->MaxCachedPerBatch; 637*76559068SAndroid Build Coastguard Worker } 638*76559068SAndroid Build Coastguard Worker // `UnusedSlots` is u16 so the result will be also fit in u16. 639*76559068SAndroid Build Coastguard Worker u16 AppendSize = static_cast<u16>(Min<u32>(UnusedSlots, Size - I)); 640*76559068SAndroid Build Coastguard Worker CurBatch->appendFromArray(&Array[I], AppendSize); 641*76559068SAndroid Build Coastguard Worker I += AppendSize; 642*76559068SAndroid Build Coastguard Worker } 643*76559068SAndroid Build Coastguard Worker }; 644*76559068SAndroid Build Coastguard Worker 645*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PushedBlocks += Size; 646*76559068SAndroid Build Coastguard Worker BatchGroupT *Cur = Sci->FreeListInfo.BlockList.front(); 647*76559068SAndroid Build Coastguard Worker 648*76559068SAndroid Build Coastguard Worker // In the following, `Cur` always points to the BatchGroup for blocks that 649*76559068SAndroid Build Coastguard Worker // will be pushed next. `Prev` is the element right before `Cur`. 650*76559068SAndroid Build Coastguard Worker BatchGroupT *Prev = nullptr; 651*76559068SAndroid Build Coastguard Worker 652*76559068SAndroid Build Coastguard Worker while (Cur != nullptr && 653*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(Array[0]) > Cur->CompactPtrGroupBase) { 654*76559068SAndroid Build Coastguard Worker Prev = Cur; 655*76559068SAndroid Build Coastguard Worker Cur = Cur->Next; 656*76559068SAndroid Build Coastguard Worker } 657*76559068SAndroid Build Coastguard Worker 658*76559068SAndroid Build Coastguard Worker if (Cur == nullptr || 659*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(Array[0]) != Cur->CompactPtrGroupBase) { 660*76559068SAndroid Build Coastguard Worker Cur = CreateGroup(compactPtrGroupBase(Array[0])); 661*76559068SAndroid Build Coastguard Worker if (Prev == nullptr) 662*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.push_front(Cur); 663*76559068SAndroid Build Coastguard Worker else 664*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.insert(Prev, Cur); 665*76559068SAndroid Build Coastguard Worker } 666*76559068SAndroid Build Coastguard Worker 667*76559068SAndroid Build Coastguard Worker // All the blocks are from the same group, just push without checking group 668*76559068SAndroid Build Coastguard Worker // id. 669*76559068SAndroid Build Coastguard Worker if (SameGroup) { 670*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < Size; ++I) 671*76559068SAndroid Build Coastguard Worker DCHECK_EQ(compactPtrGroupBase(Array[I]), Cur->CompactPtrGroupBase); 672*76559068SAndroid Build Coastguard Worker 673*76559068SAndroid Build Coastguard Worker InsertBlocks(Cur, Array, Size); 674*76559068SAndroid Build Coastguard Worker return; 675*76559068SAndroid Build Coastguard Worker } 676*76559068SAndroid Build Coastguard Worker 677*76559068SAndroid Build Coastguard Worker // The blocks are sorted by group id. Determine the segment of group and 678*76559068SAndroid Build Coastguard Worker // push them to their group together. 679*76559068SAndroid Build Coastguard Worker u32 Count = 1; 680*76559068SAndroid Build Coastguard Worker for (u32 I = 1; I < Size; ++I) { 681*76559068SAndroid Build Coastguard Worker if (compactPtrGroupBase(Array[I - 1]) != compactPtrGroupBase(Array[I])) { 682*76559068SAndroid Build Coastguard Worker DCHECK_EQ(compactPtrGroupBase(Array[I - 1]), Cur->CompactPtrGroupBase); 683*76559068SAndroid Build Coastguard Worker InsertBlocks(Cur, Array + I - Count, Count); 684*76559068SAndroid Build Coastguard Worker 685*76559068SAndroid Build Coastguard Worker while (Cur != nullptr && 686*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(Array[I]) > Cur->CompactPtrGroupBase) { 687*76559068SAndroid Build Coastguard Worker Prev = Cur; 688*76559068SAndroid Build Coastguard Worker Cur = Cur->Next; 689*76559068SAndroid Build Coastguard Worker } 690*76559068SAndroid Build Coastguard Worker 691*76559068SAndroid Build Coastguard Worker if (Cur == nullptr || 692*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(Array[I]) != Cur->CompactPtrGroupBase) { 693*76559068SAndroid Build Coastguard Worker Cur = CreateGroup(compactPtrGroupBase(Array[I])); 694*76559068SAndroid Build Coastguard Worker DCHECK_NE(Prev, nullptr); 695*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.insert(Prev, Cur); 696*76559068SAndroid Build Coastguard Worker } 697*76559068SAndroid Build Coastguard Worker 698*76559068SAndroid Build Coastguard Worker Count = 1; 699*76559068SAndroid Build Coastguard Worker } else { 700*76559068SAndroid Build Coastguard Worker ++Count; 701*76559068SAndroid Build Coastguard Worker } 702*76559068SAndroid Build Coastguard Worker } 703*76559068SAndroid Build Coastguard Worker 704*76559068SAndroid Build Coastguard Worker InsertBlocks(Cur, Array + Size - Count, Count); 705*76559068SAndroid Build Coastguard Worker } 706*76559068SAndroid Build Coastguard Worker popBlocksImpl(CacheT * C,uptr ClassId,SizeClassInfo * Sci,CompactPtrT * ToArray,const u16 MaxBlockCount)707*76559068SAndroid Build Coastguard Worker u16 popBlocksImpl(CacheT *C, uptr ClassId, SizeClassInfo *Sci, 708*76559068SAndroid Build Coastguard Worker CompactPtrT *ToArray, const u16 MaxBlockCount) 709*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 710*76559068SAndroid Build Coastguard Worker if (Sci->FreeListInfo.BlockList.empty()) 711*76559068SAndroid Build Coastguard Worker return 0U; 712*76559068SAndroid Build Coastguard Worker 713*76559068SAndroid Build Coastguard Worker SinglyLinkedList<TransferBatchT> &Batches = 714*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.front()->Batches; 715*76559068SAndroid Build Coastguard Worker 716*76559068SAndroid Build Coastguard Worker if (Batches.empty()) { 717*76559068SAndroid Build Coastguard Worker DCHECK_EQ(ClassId, SizeClassMap::BatchClassId); 718*76559068SAndroid Build Coastguard Worker BatchGroupT *BG = Sci->FreeListInfo.BlockList.front(); 719*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.pop_front(); 720*76559068SAndroid Build Coastguard Worker 721*76559068SAndroid Build Coastguard Worker // Block used by `BatchGroup` is from BatchClassId. Turn the block into 722*76559068SAndroid Build Coastguard Worker // `TransferBatch` with single block. 723*76559068SAndroid Build Coastguard Worker TransferBatchT *TB = reinterpret_cast<TransferBatchT *>(BG); 724*76559068SAndroid Build Coastguard Worker ToArray[0] = 725*76559068SAndroid Build Coastguard Worker compactPtr(SizeClassMap::BatchClassId, reinterpret_cast<uptr>(TB)); 726*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PoppedBlocks += 1; 727*76559068SAndroid Build Coastguard Worker return 1U; 728*76559068SAndroid Build Coastguard Worker } 729*76559068SAndroid Build Coastguard Worker 730*76559068SAndroid Build Coastguard Worker // So far, instead of always filling the blocks to `MaxBlockCount`, we only 731*76559068SAndroid Build Coastguard Worker // examine single `TransferBatch` to minimize the time spent on the primary 732*76559068SAndroid Build Coastguard Worker // allocator. Besides, the sizes of `TransferBatch` and 733*76559068SAndroid Build Coastguard Worker // `CacheT::getMaxCached()` may also impact the time spent on accessing the 734*76559068SAndroid Build Coastguard Worker // primary allocator. 735*76559068SAndroid Build Coastguard Worker // TODO(chiahungduan): Evaluate if we want to always prepare `MaxBlockCount` 736*76559068SAndroid Build Coastguard Worker // blocks and/or adjust the size of `TransferBatch` according to 737*76559068SAndroid Build Coastguard Worker // `CacheT::getMaxCached()`. 738*76559068SAndroid Build Coastguard Worker TransferBatchT *B = Batches.front(); 739*76559068SAndroid Build Coastguard Worker DCHECK_NE(B, nullptr); 740*76559068SAndroid Build Coastguard Worker DCHECK_GT(B->getCount(), 0U); 741*76559068SAndroid Build Coastguard Worker 742*76559068SAndroid Build Coastguard Worker // BachClassId should always take all blocks in the TransferBatch. Read the 743*76559068SAndroid Build Coastguard Worker // comment in `pushBatchClassBlocks()` for more details. 744*76559068SAndroid Build Coastguard Worker const u16 PopCount = ClassId == SizeClassMap::BatchClassId 745*76559068SAndroid Build Coastguard Worker ? B->getCount() 746*76559068SAndroid Build Coastguard Worker : Min(MaxBlockCount, B->getCount()); 747*76559068SAndroid Build Coastguard Worker B->moveNToArray(ToArray, PopCount); 748*76559068SAndroid Build Coastguard Worker 749*76559068SAndroid Build Coastguard Worker // TODO(chiahungduan): The deallocation of unused BatchClassId blocks can be 750*76559068SAndroid Build Coastguard Worker // done without holding `Mutex`. 751*76559068SAndroid Build Coastguard Worker if (B->empty()) { 752*76559068SAndroid Build Coastguard Worker Batches.pop_front(); 753*76559068SAndroid Build Coastguard Worker // `TransferBatch` of BatchClassId is self-contained, no need to 754*76559068SAndroid Build Coastguard Worker // deallocate. Read the comment in `pushBatchClassBlocks()` for more 755*76559068SAndroid Build Coastguard Worker // details. 756*76559068SAndroid Build Coastguard Worker if (ClassId != SizeClassMap::BatchClassId) 757*76559068SAndroid Build Coastguard Worker C->deallocate(SizeClassMap::BatchClassId, B); 758*76559068SAndroid Build Coastguard Worker 759*76559068SAndroid Build Coastguard Worker if (Batches.empty()) { 760*76559068SAndroid Build Coastguard Worker BatchGroupT *BG = Sci->FreeListInfo.BlockList.front(); 761*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.BlockList.pop_front(); 762*76559068SAndroid Build Coastguard Worker 763*76559068SAndroid Build Coastguard Worker // We don't keep BatchGroup with zero blocks to avoid empty-checking 764*76559068SAndroid Build Coastguard Worker // while allocating. Note that block used for constructing BatchGroup is 765*76559068SAndroid Build Coastguard Worker // recorded as free blocks in the last element of BatchGroup::Batches. 766*76559068SAndroid Build Coastguard Worker // Which means, once we pop the last TransferBatch, the block is 767*76559068SAndroid Build Coastguard Worker // implicitly deallocated. 768*76559068SAndroid Build Coastguard Worker if (ClassId != SizeClassMap::BatchClassId) 769*76559068SAndroid Build Coastguard Worker C->deallocate(SizeClassMap::BatchClassId, BG); 770*76559068SAndroid Build Coastguard Worker } 771*76559068SAndroid Build Coastguard Worker } 772*76559068SAndroid Build Coastguard Worker 773*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PoppedBlocks += PopCount; 774*76559068SAndroid Build Coastguard Worker return PopCount; 775*76559068SAndroid Build Coastguard Worker } 776*76559068SAndroid Build Coastguard Worker populateFreeList(CacheT * C,uptr ClassId,SizeClassInfo * Sci)777*76559068SAndroid Build Coastguard Worker NOINLINE bool populateFreeList(CacheT *C, uptr ClassId, SizeClassInfo *Sci) 778*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 779*76559068SAndroid Build Coastguard Worker uptr Region; 780*76559068SAndroid Build Coastguard Worker uptr Offset; 781*76559068SAndroid Build Coastguard Worker // If the size-class currently has a region associated to it, use it. The 782*76559068SAndroid Build Coastguard Worker // newly created blocks will be located after the currently allocated memory 783*76559068SAndroid Build Coastguard Worker // for that region (up to RegionSize). Otherwise, create a new region, where 784*76559068SAndroid Build Coastguard Worker // the new blocks will be carved from the beginning. 785*76559068SAndroid Build Coastguard Worker if (Sci->CurrentRegion) { 786*76559068SAndroid Build Coastguard Worker Region = Sci->CurrentRegion; 787*76559068SAndroid Build Coastguard Worker DCHECK_GT(Sci->CurrentRegionAllocated, 0U); 788*76559068SAndroid Build Coastguard Worker Offset = Sci->CurrentRegionAllocated; 789*76559068SAndroid Build Coastguard Worker } else { 790*76559068SAndroid Build Coastguard Worker DCHECK_EQ(Sci->CurrentRegionAllocated, 0U); 791*76559068SAndroid Build Coastguard Worker Region = allocateRegion(Sci, ClassId); 792*76559068SAndroid Build Coastguard Worker if (UNLIKELY(!Region)) 793*76559068SAndroid Build Coastguard Worker return false; 794*76559068SAndroid Build Coastguard Worker C->getStats().add(StatMapped, RegionSize); 795*76559068SAndroid Build Coastguard Worker Sci->CurrentRegion = Region; 796*76559068SAndroid Build Coastguard Worker Offset = 0; 797*76559068SAndroid Build Coastguard Worker } 798*76559068SAndroid Build Coastguard Worker 799*76559068SAndroid Build Coastguard Worker const uptr Size = getSizeByClassId(ClassId); 800*76559068SAndroid Build Coastguard Worker const u16 MaxCount = CacheT::getMaxCached(Size); 801*76559068SAndroid Build Coastguard Worker DCHECK_GT(MaxCount, 0U); 802*76559068SAndroid Build Coastguard Worker // The maximum number of blocks we should carve in the region is dictated 803*76559068SAndroid Build Coastguard Worker // by the maximum number of batches we want to fill, and the amount of 804*76559068SAndroid Build Coastguard Worker // memory left in the current region (we use the lowest of the two). This 805*76559068SAndroid Build Coastguard Worker // will not be 0 as we ensure that a region can at least hold one block (via 806*76559068SAndroid Build Coastguard Worker // static_assert and at the end of this function). 807*76559068SAndroid Build Coastguard Worker const u32 NumberOfBlocks = 808*76559068SAndroid Build Coastguard Worker Min(MaxNumBatches * MaxCount, 809*76559068SAndroid Build Coastguard Worker static_cast<u32>((RegionSize - Offset) / Size)); 810*76559068SAndroid Build Coastguard Worker DCHECK_GT(NumberOfBlocks, 0U); 811*76559068SAndroid Build Coastguard Worker 812*76559068SAndroid Build Coastguard Worker constexpr u32 ShuffleArraySize = 813*76559068SAndroid Build Coastguard Worker MaxNumBatches * TransferBatchT::MaxNumCached; 814*76559068SAndroid Build Coastguard Worker // Fill the transfer batches and put them in the size-class freelist. We 815*76559068SAndroid Build Coastguard Worker // need to randomize the blocks for security purposes, so we first fill a 816*76559068SAndroid Build Coastguard Worker // local array that we then shuffle before populating the batches. 817*76559068SAndroid Build Coastguard Worker CompactPtrT ShuffleArray[ShuffleArraySize]; 818*76559068SAndroid Build Coastguard Worker DCHECK_LE(NumberOfBlocks, ShuffleArraySize); 819*76559068SAndroid Build Coastguard Worker 820*76559068SAndroid Build Coastguard Worker uptr P = Region + Offset; 821*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < NumberOfBlocks; I++, P += Size) 822*76559068SAndroid Build Coastguard Worker ShuffleArray[I] = reinterpret_cast<CompactPtrT>(P); 823*76559068SAndroid Build Coastguard Worker 824*76559068SAndroid Build Coastguard Worker if (ClassId != SizeClassMap::BatchClassId) { 825*76559068SAndroid Build Coastguard Worker u32 N = 1; 826*76559068SAndroid Build Coastguard Worker uptr CurGroup = compactPtrGroupBase(ShuffleArray[0]); 827*76559068SAndroid Build Coastguard Worker for (u32 I = 1; I < NumberOfBlocks; I++) { 828*76559068SAndroid Build Coastguard Worker if (UNLIKELY(compactPtrGroupBase(ShuffleArray[I]) != CurGroup)) { 829*76559068SAndroid Build Coastguard Worker shuffle(ShuffleArray + I - N, N, &Sci->RandState); 830*76559068SAndroid Build Coastguard Worker pushBlocksImpl(C, ClassId, Sci, ShuffleArray + I - N, N, 831*76559068SAndroid Build Coastguard Worker /*SameGroup=*/true); 832*76559068SAndroid Build Coastguard Worker N = 1; 833*76559068SAndroid Build Coastguard Worker CurGroup = compactPtrGroupBase(ShuffleArray[I]); 834*76559068SAndroid Build Coastguard Worker } else { 835*76559068SAndroid Build Coastguard Worker ++N; 836*76559068SAndroid Build Coastguard Worker } 837*76559068SAndroid Build Coastguard Worker } 838*76559068SAndroid Build Coastguard Worker 839*76559068SAndroid Build Coastguard Worker shuffle(ShuffleArray + NumberOfBlocks - N, N, &Sci->RandState); 840*76559068SAndroid Build Coastguard Worker pushBlocksImpl(C, ClassId, Sci, &ShuffleArray[NumberOfBlocks - N], N, 841*76559068SAndroid Build Coastguard Worker /*SameGroup=*/true); 842*76559068SAndroid Build Coastguard Worker } else { 843*76559068SAndroid Build Coastguard Worker pushBatchClassBlocks(Sci, ShuffleArray, NumberOfBlocks); 844*76559068SAndroid Build Coastguard Worker } 845*76559068SAndroid Build Coastguard Worker 846*76559068SAndroid Build Coastguard Worker // Note that `PushedBlocks` and `PoppedBlocks` are supposed to only record 847*76559068SAndroid Build Coastguard Worker // the requests from `PushBlocks` and `PopBatch` which are external 848*76559068SAndroid Build Coastguard Worker // interfaces. `populateFreeList` is the internal interface so we should set 849*76559068SAndroid Build Coastguard Worker // the values back to avoid incorrectly setting the stats. 850*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PushedBlocks -= NumberOfBlocks; 851*76559068SAndroid Build Coastguard Worker 852*76559068SAndroid Build Coastguard Worker const uptr AllocatedUser = Size * NumberOfBlocks; 853*76559068SAndroid Build Coastguard Worker C->getStats().add(StatFree, AllocatedUser); 854*76559068SAndroid Build Coastguard Worker DCHECK_LE(Sci->CurrentRegionAllocated + AllocatedUser, RegionSize); 855*76559068SAndroid Build Coastguard Worker // If there is not enough room in the region currently associated to fit 856*76559068SAndroid Build Coastguard Worker // more blocks, we deassociate the region by resetting CurrentRegion and 857*76559068SAndroid Build Coastguard Worker // CurrentRegionAllocated. Otherwise, update the allocated amount. 858*76559068SAndroid Build Coastguard Worker if (RegionSize - (Sci->CurrentRegionAllocated + AllocatedUser) < Size) { 859*76559068SAndroid Build Coastguard Worker Sci->CurrentRegion = 0; 860*76559068SAndroid Build Coastguard Worker Sci->CurrentRegionAllocated = 0; 861*76559068SAndroid Build Coastguard Worker } else { 862*76559068SAndroid Build Coastguard Worker Sci->CurrentRegionAllocated += AllocatedUser; 863*76559068SAndroid Build Coastguard Worker } 864*76559068SAndroid Build Coastguard Worker Sci->AllocatedUser += AllocatedUser; 865*76559068SAndroid Build Coastguard Worker 866*76559068SAndroid Build Coastguard Worker return true; 867*76559068SAndroid Build Coastguard Worker } 868*76559068SAndroid Build Coastguard Worker getStats(ScopedString * Str,uptr ClassId,SizeClassInfo * Sci)869*76559068SAndroid Build Coastguard Worker void getStats(ScopedString *Str, uptr ClassId, SizeClassInfo *Sci) 870*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 871*76559068SAndroid Build Coastguard Worker if (Sci->AllocatedUser == 0) 872*76559068SAndroid Build Coastguard Worker return; 873*76559068SAndroid Build Coastguard Worker const uptr BlockSize = getSizeByClassId(ClassId); 874*76559068SAndroid Build Coastguard Worker const uptr InUse = 875*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PoppedBlocks - Sci->FreeListInfo.PushedBlocks; 876*76559068SAndroid Build Coastguard Worker const uptr BytesInFreeList = Sci->AllocatedUser - InUse * BlockSize; 877*76559068SAndroid Build Coastguard Worker uptr PushedBytesDelta = 0; 878*76559068SAndroid Build Coastguard Worker if (BytesInFreeList >= Sci->ReleaseInfo.BytesInFreeListAtLastCheckpoint) { 879*76559068SAndroid Build Coastguard Worker PushedBytesDelta = 880*76559068SAndroid Build Coastguard Worker BytesInFreeList - Sci->ReleaseInfo.BytesInFreeListAtLastCheckpoint; 881*76559068SAndroid Build Coastguard Worker } 882*76559068SAndroid Build Coastguard Worker const uptr AvailableChunks = Sci->AllocatedUser / BlockSize; 883*76559068SAndroid Build Coastguard Worker Str->append(" %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu " 884*76559068SAndroid Build Coastguard Worker "inuse: %6zu avail: %6zu releases: %6zu last released: %6zuK " 885*76559068SAndroid Build Coastguard Worker "latest pushed bytes: %6zuK\n", 886*76559068SAndroid Build Coastguard Worker ClassId, getSizeByClassId(ClassId), Sci->AllocatedUser >> 10, 887*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PoppedBlocks, Sci->FreeListInfo.PushedBlocks, 888*76559068SAndroid Build Coastguard Worker InUse, AvailableChunks, Sci->ReleaseInfo.RangesReleased, 889*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.LastReleasedBytes >> 10, 890*76559068SAndroid Build Coastguard Worker PushedBytesDelta >> 10); 891*76559068SAndroid Build Coastguard Worker } 892*76559068SAndroid Build Coastguard Worker getSizeClassFragmentationInfo(SizeClassInfo * Sci,uptr ClassId,ScopedString * Str)893*76559068SAndroid Build Coastguard Worker void getSizeClassFragmentationInfo(SizeClassInfo *Sci, uptr ClassId, 894*76559068SAndroid Build Coastguard Worker ScopedString *Str) REQUIRES(Sci->Mutex) { 895*76559068SAndroid Build Coastguard Worker const uptr BlockSize = getSizeByClassId(ClassId); 896*76559068SAndroid Build Coastguard Worker const uptr First = Sci->MinRegionIndex; 897*76559068SAndroid Build Coastguard Worker const uptr Last = Sci->MaxRegionIndex; 898*76559068SAndroid Build Coastguard Worker const uptr Base = First * RegionSize; 899*76559068SAndroid Build Coastguard Worker const uptr NumberOfRegions = Last - First + 1U; 900*76559068SAndroid Build Coastguard Worker auto SkipRegion = [this, First, ClassId](uptr RegionIndex) { 901*76559068SAndroid Build Coastguard Worker ScopedLock L(ByteMapMutex); 902*76559068SAndroid Build Coastguard Worker return (PossibleRegions[First + RegionIndex] - 1U) != ClassId; 903*76559068SAndroid Build Coastguard Worker }; 904*76559068SAndroid Build Coastguard Worker 905*76559068SAndroid Build Coastguard Worker FragmentationRecorder Recorder; 906*76559068SAndroid Build Coastguard Worker if (!Sci->FreeListInfo.BlockList.empty()) { 907*76559068SAndroid Build Coastguard Worker PageReleaseContext Context = 908*76559068SAndroid Build Coastguard Worker markFreeBlocks(Sci, ClassId, BlockSize, Base, NumberOfRegions, 909*76559068SAndroid Build Coastguard Worker ReleaseToOS::ForceAll); 910*76559068SAndroid Build Coastguard Worker releaseFreeMemoryToOS(Context, Recorder, SkipRegion); 911*76559068SAndroid Build Coastguard Worker } 912*76559068SAndroid Build Coastguard Worker 913*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached(); 914*76559068SAndroid Build Coastguard Worker const uptr TotalBlocks = Sci->AllocatedUser / BlockSize; 915*76559068SAndroid Build Coastguard Worker const uptr InUseBlocks = 916*76559068SAndroid Build Coastguard Worker Sci->FreeListInfo.PoppedBlocks - Sci->FreeListInfo.PushedBlocks; 917*76559068SAndroid Build Coastguard Worker uptr AllocatedPagesCount = 0; 918*76559068SAndroid Build Coastguard Worker if (TotalBlocks != 0U) { 919*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumberOfRegions; ++I) { 920*76559068SAndroid Build Coastguard Worker if (SkipRegion(I)) 921*76559068SAndroid Build Coastguard Worker continue; 922*76559068SAndroid Build Coastguard Worker AllocatedPagesCount += RegionSize / PageSize; 923*76559068SAndroid Build Coastguard Worker } 924*76559068SAndroid Build Coastguard Worker 925*76559068SAndroid Build Coastguard Worker DCHECK_NE(AllocatedPagesCount, 0U); 926*76559068SAndroid Build Coastguard Worker } 927*76559068SAndroid Build Coastguard Worker 928*76559068SAndroid Build Coastguard Worker DCHECK_GE(AllocatedPagesCount, Recorder.getReleasedPagesCount()); 929*76559068SAndroid Build Coastguard Worker const uptr InUsePages = 930*76559068SAndroid Build Coastguard Worker AllocatedPagesCount - Recorder.getReleasedPagesCount(); 931*76559068SAndroid Build Coastguard Worker const uptr InUseBytes = InUsePages * PageSize; 932*76559068SAndroid Build Coastguard Worker 933*76559068SAndroid Build Coastguard Worker uptr Integral; 934*76559068SAndroid Build Coastguard Worker uptr Fractional; 935*76559068SAndroid Build Coastguard Worker computePercentage(BlockSize * InUseBlocks, InUseBytes, &Integral, 936*76559068SAndroid Build Coastguard Worker &Fractional); 937*76559068SAndroid Build Coastguard Worker Str->append(" %02zu (%6zu): inuse/total blocks: %6zu/%6zu inuse/total " 938*76559068SAndroid Build Coastguard Worker "pages: %6zu/%6zu inuse bytes: %6zuK util: %3zu.%02zu%%\n", 939*76559068SAndroid Build Coastguard Worker ClassId, BlockSize, InUseBlocks, TotalBlocks, InUsePages, 940*76559068SAndroid Build Coastguard Worker AllocatedPagesCount, InUseBytes >> 10, Integral, Fractional); 941*76559068SAndroid Build Coastguard Worker } 942*76559068SAndroid Build Coastguard Worker 943*76559068SAndroid Build Coastguard Worker NOINLINE uptr releaseToOSMaybe(SizeClassInfo *Sci, uptr ClassId, 944*76559068SAndroid Build Coastguard Worker ReleaseToOS ReleaseType = ReleaseToOS::Normal) 945*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 946*76559068SAndroid Build Coastguard Worker const uptr BlockSize = getSizeByClassId(ClassId); 947*76559068SAndroid Build Coastguard Worker 948*76559068SAndroid Build Coastguard Worker DCHECK_GE(Sci->FreeListInfo.PoppedBlocks, Sci->FreeListInfo.PushedBlocks); 949*76559068SAndroid Build Coastguard Worker const uptr BytesInFreeList = 950*76559068SAndroid Build Coastguard Worker Sci->AllocatedUser - 951*76559068SAndroid Build Coastguard Worker (Sci->FreeListInfo.PoppedBlocks - Sci->FreeListInfo.PushedBlocks) * 952*76559068SAndroid Build Coastguard Worker BlockSize; 953*76559068SAndroid Build Coastguard Worker 954*76559068SAndroid Build Coastguard Worker if (UNLIKELY(BytesInFreeList == 0)) 955*76559068SAndroid Build Coastguard Worker return 0; 956*76559068SAndroid Build Coastguard Worker 957*76559068SAndroid Build Coastguard Worker // ====================================================================== // 958*76559068SAndroid Build Coastguard Worker // 1. Check if we have enough free blocks and if it's worth doing a page 959*76559068SAndroid Build Coastguard Worker // release. 960*76559068SAndroid Build Coastguard Worker // ====================================================================== // 961*76559068SAndroid Build Coastguard Worker if (ReleaseType != ReleaseToOS::ForceAll && 962*76559068SAndroid Build Coastguard Worker !hasChanceToReleasePages(Sci, BlockSize, BytesInFreeList, 963*76559068SAndroid Build Coastguard Worker ReleaseType)) { 964*76559068SAndroid Build Coastguard Worker return 0; 965*76559068SAndroid Build Coastguard Worker } 966*76559068SAndroid Build Coastguard Worker 967*76559068SAndroid Build Coastguard Worker const uptr First = Sci->MinRegionIndex; 968*76559068SAndroid Build Coastguard Worker const uptr Last = Sci->MaxRegionIndex; 969*76559068SAndroid Build Coastguard Worker DCHECK_NE(Last, 0U); 970*76559068SAndroid Build Coastguard Worker DCHECK_LE(First, Last); 971*76559068SAndroid Build Coastguard Worker uptr TotalReleasedBytes = 0; 972*76559068SAndroid Build Coastguard Worker const uptr Base = First * RegionSize; 973*76559068SAndroid Build Coastguard Worker const uptr NumberOfRegions = Last - First + 1U; 974*76559068SAndroid Build Coastguard Worker 975*76559068SAndroid Build Coastguard Worker // ==================================================================== // 976*76559068SAndroid Build Coastguard Worker // 2. Mark the free blocks and we can tell which pages are in-use by 977*76559068SAndroid Build Coastguard Worker // querying `PageReleaseContext`. 978*76559068SAndroid Build Coastguard Worker // ==================================================================== // 979*76559068SAndroid Build Coastguard Worker PageReleaseContext Context = markFreeBlocks(Sci, ClassId, BlockSize, Base, 980*76559068SAndroid Build Coastguard Worker NumberOfRegions, ReleaseType); 981*76559068SAndroid Build Coastguard Worker if (!Context.hasBlockMarked()) 982*76559068SAndroid Build Coastguard Worker return 0; 983*76559068SAndroid Build Coastguard Worker 984*76559068SAndroid Build Coastguard Worker // ==================================================================== // 985*76559068SAndroid Build Coastguard Worker // 3. Release the unused physical pages back to the OS. 986*76559068SAndroid Build Coastguard Worker // ==================================================================== // 987*76559068SAndroid Build Coastguard Worker ReleaseRecorder Recorder(Base); 988*76559068SAndroid Build Coastguard Worker auto SkipRegion = [this, First, ClassId](uptr RegionIndex) { 989*76559068SAndroid Build Coastguard Worker ScopedLock L(ByteMapMutex); 990*76559068SAndroid Build Coastguard Worker return (PossibleRegions[First + RegionIndex] - 1U) != ClassId; 991*76559068SAndroid Build Coastguard Worker }; 992*76559068SAndroid Build Coastguard Worker releaseFreeMemoryToOS(Context, Recorder, SkipRegion); 993*76559068SAndroid Build Coastguard Worker 994*76559068SAndroid Build Coastguard Worker if (Recorder.getReleasedRangesCount() > 0) { 995*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.BytesInFreeListAtLastCheckpoint = BytesInFreeList; 996*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.RangesReleased += Recorder.getReleasedRangesCount(); 997*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.LastReleasedBytes = Recorder.getReleasedBytes(); 998*76559068SAndroid Build Coastguard Worker TotalReleasedBytes += Sci->ReleaseInfo.LastReleasedBytes; 999*76559068SAndroid Build Coastguard Worker } 1000*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.LastReleaseAtNs = getMonotonicTimeFast(); 1001*76559068SAndroid Build Coastguard Worker 1002*76559068SAndroid Build Coastguard Worker return TotalReleasedBytes; 1003*76559068SAndroid Build Coastguard Worker } 1004*76559068SAndroid Build Coastguard Worker hasChanceToReleasePages(SizeClassInfo * Sci,uptr BlockSize,uptr BytesInFreeList,ReleaseToOS ReleaseType)1005*76559068SAndroid Build Coastguard Worker bool hasChanceToReleasePages(SizeClassInfo *Sci, uptr BlockSize, 1006*76559068SAndroid Build Coastguard Worker uptr BytesInFreeList, ReleaseToOS ReleaseType) 1007*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 1008*76559068SAndroid Build Coastguard Worker DCHECK_GE(Sci->FreeListInfo.PoppedBlocks, Sci->FreeListInfo.PushedBlocks); 1009*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached(); 1010*76559068SAndroid Build Coastguard Worker 1011*76559068SAndroid Build Coastguard Worker if (BytesInFreeList <= Sci->ReleaseInfo.BytesInFreeListAtLastCheckpoint) 1012*76559068SAndroid Build Coastguard Worker Sci->ReleaseInfo.BytesInFreeListAtLastCheckpoint = BytesInFreeList; 1013*76559068SAndroid Build Coastguard Worker 1014*76559068SAndroid Build Coastguard Worker // Always update `BytesInFreeListAtLastCheckpoint` with the smallest value 1015*76559068SAndroid Build Coastguard Worker // so that we won't underestimate the releasable pages. For example, the 1016*76559068SAndroid Build Coastguard Worker // following is the region usage, 1017*76559068SAndroid Build Coastguard Worker // 1018*76559068SAndroid Build Coastguard Worker // BytesInFreeListAtLastCheckpoint AllocatedUser 1019*76559068SAndroid Build Coastguard Worker // v v 1020*76559068SAndroid Build Coastguard Worker // |---------------------------------------> 1021*76559068SAndroid Build Coastguard Worker // ^ ^ 1022*76559068SAndroid Build Coastguard Worker // BytesInFreeList ReleaseThreshold 1023*76559068SAndroid Build Coastguard Worker // 1024*76559068SAndroid Build Coastguard Worker // In general, if we have collected enough bytes and the amount of free 1025*76559068SAndroid Build Coastguard Worker // bytes meets the ReleaseThreshold, we will try to do page release. If we 1026*76559068SAndroid Build Coastguard Worker // don't update `BytesInFreeListAtLastCheckpoint` when the current 1027*76559068SAndroid Build Coastguard Worker // `BytesInFreeList` is smaller, we may take longer time to wait for enough 1028*76559068SAndroid Build Coastguard Worker // freed blocks because we miss the bytes between 1029*76559068SAndroid Build Coastguard Worker // (BytesInFreeListAtLastCheckpoint - BytesInFreeList). 1030*76559068SAndroid Build Coastguard Worker const uptr PushedBytesDelta = 1031*76559068SAndroid Build Coastguard Worker BytesInFreeList - Sci->ReleaseInfo.BytesInFreeListAtLastCheckpoint; 1032*76559068SAndroid Build Coastguard Worker if (PushedBytesDelta < PageSize) 1033*76559068SAndroid Build Coastguard Worker return false; 1034*76559068SAndroid Build Coastguard Worker 1035*76559068SAndroid Build Coastguard Worker // Releasing smaller blocks is expensive, so we want to make sure that a 1036*76559068SAndroid Build Coastguard Worker // significant amount of bytes are free, and that there has been a good 1037*76559068SAndroid Build Coastguard Worker // amount of batches pushed to the freelist before attempting to release. 1038*76559068SAndroid Build Coastguard Worker if (isSmallBlock(BlockSize) && ReleaseType == ReleaseToOS::Normal) 1039*76559068SAndroid Build Coastguard Worker if (PushedBytesDelta < Sci->AllocatedUser / 16U) 1040*76559068SAndroid Build Coastguard Worker return false; 1041*76559068SAndroid Build Coastguard Worker 1042*76559068SAndroid Build Coastguard Worker if (ReleaseType == ReleaseToOS::Normal) { 1043*76559068SAndroid Build Coastguard Worker const s32 IntervalMs = atomic_load_relaxed(&ReleaseToOsIntervalMs); 1044*76559068SAndroid Build Coastguard Worker if (IntervalMs < 0) 1045*76559068SAndroid Build Coastguard Worker return false; 1046*76559068SAndroid Build Coastguard Worker 1047*76559068SAndroid Build Coastguard Worker // The constant 8 here is selected from profiling some apps and the number 1048*76559068SAndroid Build Coastguard Worker // of unreleased pages in the large size classes is around 16 pages or 1049*76559068SAndroid Build Coastguard Worker // more. Choose half of it as a heuristic and which also avoids page 1050*76559068SAndroid Build Coastguard Worker // release every time for every pushBlocks() attempt by large blocks. 1051*76559068SAndroid Build Coastguard Worker const bool ByPassReleaseInterval = 1052*76559068SAndroid Build Coastguard Worker isLargeBlock(BlockSize) && PushedBytesDelta > 8 * PageSize; 1053*76559068SAndroid Build Coastguard Worker if (!ByPassReleaseInterval) { 1054*76559068SAndroid Build Coastguard Worker if (Sci->ReleaseInfo.LastReleaseAtNs + 1055*76559068SAndroid Build Coastguard Worker static_cast<u64>(IntervalMs) * 1000000 > 1056*76559068SAndroid Build Coastguard Worker getMonotonicTimeFast()) { 1057*76559068SAndroid Build Coastguard Worker // Memory was returned recently. 1058*76559068SAndroid Build Coastguard Worker return false; 1059*76559068SAndroid Build Coastguard Worker } 1060*76559068SAndroid Build Coastguard Worker } 1061*76559068SAndroid Build Coastguard Worker } // if (ReleaseType == ReleaseToOS::Normal) 1062*76559068SAndroid Build Coastguard Worker 1063*76559068SAndroid Build Coastguard Worker return true; 1064*76559068SAndroid Build Coastguard Worker } 1065*76559068SAndroid Build Coastguard Worker markFreeBlocks(SizeClassInfo * Sci,const uptr ClassId,const uptr BlockSize,const uptr Base,const uptr NumberOfRegions,ReleaseToOS ReleaseType)1066*76559068SAndroid Build Coastguard Worker PageReleaseContext markFreeBlocks(SizeClassInfo *Sci, const uptr ClassId, 1067*76559068SAndroid Build Coastguard Worker const uptr BlockSize, const uptr Base, 1068*76559068SAndroid Build Coastguard Worker const uptr NumberOfRegions, 1069*76559068SAndroid Build Coastguard Worker ReleaseToOS ReleaseType) 1070*76559068SAndroid Build Coastguard Worker REQUIRES(Sci->Mutex) { 1071*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached(); 1072*76559068SAndroid Build Coastguard Worker const uptr GroupSize = (1UL << GroupSizeLog); 1073*76559068SAndroid Build Coastguard Worker const uptr CurGroupBase = 1074*76559068SAndroid Build Coastguard Worker compactPtrGroupBase(compactPtr(ClassId, Sci->CurrentRegion)); 1075*76559068SAndroid Build Coastguard Worker 1076*76559068SAndroid Build Coastguard Worker PageReleaseContext Context(BlockSize, NumberOfRegions, 1077*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize); 1078*76559068SAndroid Build Coastguard Worker 1079*76559068SAndroid Build Coastguard Worker auto DecompactPtr = [](CompactPtrT CompactPtr) { 1080*76559068SAndroid Build Coastguard Worker return reinterpret_cast<uptr>(CompactPtr); 1081*76559068SAndroid Build Coastguard Worker }; 1082*76559068SAndroid Build Coastguard Worker for (BatchGroupT &BG : Sci->FreeListInfo.BlockList) { 1083*76559068SAndroid Build Coastguard Worker const uptr GroupBase = decompactGroupBase(BG.CompactPtrGroupBase); 1084*76559068SAndroid Build Coastguard Worker // The `GroupSize` may not be divided by `BlockSize`, which means there is 1085*76559068SAndroid Build Coastguard Worker // an unused space at the end of Region. Exclude that space to avoid 1086*76559068SAndroid Build Coastguard Worker // unused page map entry. 1087*76559068SAndroid Build Coastguard Worker uptr AllocatedGroupSize = GroupBase == CurGroupBase 1088*76559068SAndroid Build Coastguard Worker ? Sci->CurrentRegionAllocated 1089*76559068SAndroid Build Coastguard Worker : roundDownSlow(GroupSize, BlockSize); 1090*76559068SAndroid Build Coastguard Worker if (AllocatedGroupSize == 0) 1091*76559068SAndroid Build Coastguard Worker continue; 1092*76559068SAndroid Build Coastguard Worker 1093*76559068SAndroid Build Coastguard Worker // TransferBatches are pushed in front of BG.Batches. The first one may 1094*76559068SAndroid Build Coastguard Worker // not have all caches used. 1095*76559068SAndroid Build Coastguard Worker const uptr NumBlocks = (BG.Batches.size() - 1) * BG.MaxCachedPerBatch + 1096*76559068SAndroid Build Coastguard Worker BG.Batches.front()->getCount(); 1097*76559068SAndroid Build Coastguard Worker const uptr BytesInBG = NumBlocks * BlockSize; 1098*76559068SAndroid Build Coastguard Worker 1099*76559068SAndroid Build Coastguard Worker if (ReleaseType != ReleaseToOS::ForceAll) { 1100*76559068SAndroid Build Coastguard Worker if (BytesInBG <= BG.BytesInBGAtLastCheckpoint) { 1101*76559068SAndroid Build Coastguard Worker BG.BytesInBGAtLastCheckpoint = BytesInBG; 1102*76559068SAndroid Build Coastguard Worker continue; 1103*76559068SAndroid Build Coastguard Worker } 1104*76559068SAndroid Build Coastguard Worker 1105*76559068SAndroid Build Coastguard Worker const uptr PushedBytesDelta = BytesInBG - BG.BytesInBGAtLastCheckpoint; 1106*76559068SAndroid Build Coastguard Worker if (PushedBytesDelta < PageSize) 1107*76559068SAndroid Build Coastguard Worker continue; 1108*76559068SAndroid Build Coastguard Worker 1109*76559068SAndroid Build Coastguard Worker // Given the randomness property, we try to release the pages only if 1110*76559068SAndroid Build Coastguard Worker // the bytes used by free blocks exceed certain proportion of allocated 1111*76559068SAndroid Build Coastguard Worker // spaces. 1112*76559068SAndroid Build Coastguard Worker if (isSmallBlock(BlockSize) && (BytesInBG * 100U) / AllocatedGroupSize < 1113*76559068SAndroid Build Coastguard Worker (100U - 1U - BlockSize / 16U)) { 1114*76559068SAndroid Build Coastguard Worker continue; 1115*76559068SAndroid Build Coastguard Worker } 1116*76559068SAndroid Build Coastguard Worker } 1117*76559068SAndroid Build Coastguard Worker 1118*76559068SAndroid Build Coastguard Worker // TODO: Consider updating this after page release if `ReleaseRecorder` 1119*76559068SAndroid Build Coastguard Worker // can tell the released bytes in each group. 1120*76559068SAndroid Build Coastguard Worker BG.BytesInBGAtLastCheckpoint = BytesInBG; 1121*76559068SAndroid Build Coastguard Worker 1122*76559068SAndroid Build Coastguard Worker const uptr MaxContainedBlocks = AllocatedGroupSize / BlockSize; 1123*76559068SAndroid Build Coastguard Worker const uptr RegionIndex = (GroupBase - Base) / RegionSize; 1124*76559068SAndroid Build Coastguard Worker 1125*76559068SAndroid Build Coastguard Worker if (NumBlocks == MaxContainedBlocks) { 1126*76559068SAndroid Build Coastguard Worker for (const auto &It : BG.Batches) 1127*76559068SAndroid Build Coastguard Worker for (u16 I = 0; I < It.getCount(); ++I) 1128*76559068SAndroid Build Coastguard Worker DCHECK_EQ(compactPtrGroupBase(It.get(I)), BG.CompactPtrGroupBase); 1129*76559068SAndroid Build Coastguard Worker 1130*76559068SAndroid Build Coastguard Worker const uptr To = GroupBase + AllocatedGroupSize; 1131*76559068SAndroid Build Coastguard Worker Context.markRangeAsAllCounted(GroupBase, To, GroupBase, RegionIndex, 1132*76559068SAndroid Build Coastguard Worker AllocatedGroupSize); 1133*76559068SAndroid Build Coastguard Worker } else { 1134*76559068SAndroid Build Coastguard Worker DCHECK_LT(NumBlocks, MaxContainedBlocks); 1135*76559068SAndroid Build Coastguard Worker 1136*76559068SAndroid Build Coastguard Worker // Note that we don't always visit blocks in each BatchGroup so that we 1137*76559068SAndroid Build Coastguard Worker // may miss the chance of releasing certain pages that cross 1138*76559068SAndroid Build Coastguard Worker // BatchGroups. 1139*76559068SAndroid Build Coastguard Worker Context.markFreeBlocksInRegion(BG.Batches, DecompactPtr, GroupBase, 1140*76559068SAndroid Build Coastguard Worker RegionIndex, AllocatedGroupSize, 1141*76559068SAndroid Build Coastguard Worker /*MayContainLastBlockInRegion=*/true); 1142*76559068SAndroid Build Coastguard Worker } 1143*76559068SAndroid Build Coastguard Worker 1144*76559068SAndroid Build Coastguard Worker // We may not be able to do the page release In a rare case that we may 1145*76559068SAndroid Build Coastguard Worker // fail on PageMap allocation. 1146*76559068SAndroid Build Coastguard Worker if (UNLIKELY(!Context.hasBlockMarked())) 1147*76559068SAndroid Build Coastguard Worker break; 1148*76559068SAndroid Build Coastguard Worker } 1149*76559068SAndroid Build Coastguard Worker 1150*76559068SAndroid Build Coastguard Worker return Context; 1151*76559068SAndroid Build Coastguard Worker } 1152*76559068SAndroid Build Coastguard Worker 1153*76559068SAndroid Build Coastguard Worker SizeClassInfo SizeClassInfoArray[NumClasses] = {}; 1154*76559068SAndroid Build Coastguard Worker 1155*76559068SAndroid Build Coastguard Worker HybridMutex ByteMapMutex; 1156*76559068SAndroid Build Coastguard Worker // Track the regions in use, 0 is unused, otherwise store ClassId + 1. 1157*76559068SAndroid Build Coastguard Worker ByteMap PossibleRegions GUARDED_BY(ByteMapMutex) = {}; 1158*76559068SAndroid Build Coastguard Worker atomic_s32 ReleaseToOsIntervalMs = {}; 1159*76559068SAndroid Build Coastguard Worker // Unless several threads request regions simultaneously from different size 1160*76559068SAndroid Build Coastguard Worker // classes, the stash rarely contains more than 1 entry. 1161*76559068SAndroid Build Coastguard Worker static constexpr uptr MaxStashedRegions = 4; 1162*76559068SAndroid Build Coastguard Worker HybridMutex RegionsStashMutex; 1163*76559068SAndroid Build Coastguard Worker uptr NumberOfStashedRegions GUARDED_BY(RegionsStashMutex) = 0; 1164*76559068SAndroid Build Coastguard Worker uptr RegionsStash[MaxStashedRegions] GUARDED_BY(RegionsStashMutex) = {}; 1165*76559068SAndroid Build Coastguard Worker }; 1166*76559068SAndroid Build Coastguard Worker 1167*76559068SAndroid Build Coastguard Worker } // namespace scudo 1168*76559068SAndroid Build Coastguard Worker 1169*76559068SAndroid Build Coastguard Worker #endif // SCUDO_PRIMARY32_H_ 1170