xref: /aosp_15_r20/external/scudo/standalone/primary32.h (revision 76559068c068bd27e82aff38fac3bfc865233bca)
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