1*76559068SAndroid Build Coastguard Worker //===-- release.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_RELEASE_H_
10*76559068SAndroid Build Coastguard Worker #define SCUDO_RELEASE_H_
11*76559068SAndroid Build Coastguard Worker
12*76559068SAndroid Build Coastguard Worker #include "common.h"
13*76559068SAndroid Build Coastguard Worker #include "list.h"
14*76559068SAndroid Build Coastguard Worker #include "mem_map.h"
15*76559068SAndroid Build Coastguard Worker #include "mutex.h"
16*76559068SAndroid Build Coastguard Worker #include "thread_annotations.h"
17*76559068SAndroid Build Coastguard Worker
18*76559068SAndroid Build Coastguard Worker namespace scudo {
19*76559068SAndroid Build Coastguard Worker
20*76559068SAndroid Build Coastguard Worker template <typename MemMapT> class RegionReleaseRecorder {
21*76559068SAndroid Build Coastguard Worker public:
22*76559068SAndroid Build Coastguard Worker RegionReleaseRecorder(MemMapT *RegionMemMap, uptr Base, uptr Offset = 0)
RegionMemMap(RegionMemMap)23*76559068SAndroid Build Coastguard Worker : RegionMemMap(RegionMemMap), Base(Base), Offset(Offset) {}
24*76559068SAndroid Build Coastguard Worker
getReleasedRangesCount()25*76559068SAndroid Build Coastguard Worker uptr getReleasedRangesCount() const { return ReleasedRangesCount; }
26*76559068SAndroid Build Coastguard Worker
getReleasedBytes()27*76559068SAndroid Build Coastguard Worker uptr getReleasedBytes() const { return ReleasedBytes; }
28*76559068SAndroid Build Coastguard Worker
getBase()29*76559068SAndroid Build Coastguard Worker uptr getBase() const { return Base; }
30*76559068SAndroid Build Coastguard Worker
31*76559068SAndroid Build Coastguard Worker // Releases [From, To) range of pages back to OS. Note that `From` and `To`
32*76559068SAndroid Build Coastguard Worker // are offseted from `Base` + Offset.
releasePageRangeToOS(uptr From,uptr To)33*76559068SAndroid Build Coastguard Worker void releasePageRangeToOS(uptr From, uptr To) {
34*76559068SAndroid Build Coastguard Worker const uptr Size = To - From;
35*76559068SAndroid Build Coastguard Worker RegionMemMap->releasePagesToOS(getBase() + Offset + From, Size);
36*76559068SAndroid Build Coastguard Worker ReleasedRangesCount++;
37*76559068SAndroid Build Coastguard Worker ReleasedBytes += Size;
38*76559068SAndroid Build Coastguard Worker }
39*76559068SAndroid Build Coastguard Worker
40*76559068SAndroid Build Coastguard Worker private:
41*76559068SAndroid Build Coastguard Worker uptr ReleasedRangesCount = 0;
42*76559068SAndroid Build Coastguard Worker uptr ReleasedBytes = 0;
43*76559068SAndroid Build Coastguard Worker MemMapT *RegionMemMap = nullptr;
44*76559068SAndroid Build Coastguard Worker uptr Base = 0;
45*76559068SAndroid Build Coastguard Worker // The release offset from Base. This is used when we know a given range after
46*76559068SAndroid Build Coastguard Worker // Base will not be released.
47*76559068SAndroid Build Coastguard Worker uptr Offset = 0;
48*76559068SAndroid Build Coastguard Worker };
49*76559068SAndroid Build Coastguard Worker
50*76559068SAndroid Build Coastguard Worker class ReleaseRecorder {
51*76559068SAndroid Build Coastguard Worker public:
52*76559068SAndroid Build Coastguard Worker ReleaseRecorder(uptr Base, uptr Offset = 0, MapPlatformData *Data = nullptr)
Base(Base)53*76559068SAndroid Build Coastguard Worker : Base(Base), Offset(Offset), Data(Data) {}
54*76559068SAndroid Build Coastguard Worker
getReleasedRangesCount()55*76559068SAndroid Build Coastguard Worker uptr getReleasedRangesCount() const { return ReleasedRangesCount; }
56*76559068SAndroid Build Coastguard Worker
getReleasedBytes()57*76559068SAndroid Build Coastguard Worker uptr getReleasedBytes() const { return ReleasedBytes; }
58*76559068SAndroid Build Coastguard Worker
getBase()59*76559068SAndroid Build Coastguard Worker uptr getBase() const { return Base; }
60*76559068SAndroid Build Coastguard Worker
61*76559068SAndroid Build Coastguard Worker // Releases [From, To) range of pages back to OS.
releasePageRangeToOS(uptr From,uptr To)62*76559068SAndroid Build Coastguard Worker void releasePageRangeToOS(uptr From, uptr To) {
63*76559068SAndroid Build Coastguard Worker const uptr Size = To - From;
64*76559068SAndroid Build Coastguard Worker releasePagesToOS(Base, From + Offset, Size, Data);
65*76559068SAndroid Build Coastguard Worker ReleasedRangesCount++;
66*76559068SAndroid Build Coastguard Worker ReleasedBytes += Size;
67*76559068SAndroid Build Coastguard Worker }
68*76559068SAndroid Build Coastguard Worker
69*76559068SAndroid Build Coastguard Worker private:
70*76559068SAndroid Build Coastguard Worker uptr ReleasedRangesCount = 0;
71*76559068SAndroid Build Coastguard Worker uptr ReleasedBytes = 0;
72*76559068SAndroid Build Coastguard Worker // The starting address to release. Note that we may want to combine (Base +
73*76559068SAndroid Build Coastguard Worker // Offset) as a new Base. However, the Base is retrieved from
74*76559068SAndroid Build Coastguard Worker // `MapPlatformData` on Fuchsia, which means the offset won't be aware.
75*76559068SAndroid Build Coastguard Worker // Therefore, store them separately to make it work on all the platforms.
76*76559068SAndroid Build Coastguard Worker uptr Base = 0;
77*76559068SAndroid Build Coastguard Worker // The release offset from Base. This is used when we know a given range after
78*76559068SAndroid Build Coastguard Worker // Base will not be released.
79*76559068SAndroid Build Coastguard Worker uptr Offset = 0;
80*76559068SAndroid Build Coastguard Worker MapPlatformData *Data = nullptr;
81*76559068SAndroid Build Coastguard Worker };
82*76559068SAndroid Build Coastguard Worker
83*76559068SAndroid Build Coastguard Worker class FragmentationRecorder {
84*76559068SAndroid Build Coastguard Worker public:
85*76559068SAndroid Build Coastguard Worker FragmentationRecorder() = default;
86*76559068SAndroid Build Coastguard Worker
getReleasedPagesCount()87*76559068SAndroid Build Coastguard Worker uptr getReleasedPagesCount() const { return ReleasedPagesCount; }
88*76559068SAndroid Build Coastguard Worker
releasePageRangeToOS(uptr From,uptr To)89*76559068SAndroid Build Coastguard Worker void releasePageRangeToOS(uptr From, uptr To) {
90*76559068SAndroid Build Coastguard Worker DCHECK_EQ((To - From) % getPageSizeCached(), 0U);
91*76559068SAndroid Build Coastguard Worker ReleasedPagesCount += (To - From) >> getPageSizeLogCached();
92*76559068SAndroid Build Coastguard Worker }
93*76559068SAndroid Build Coastguard Worker
94*76559068SAndroid Build Coastguard Worker private:
95*76559068SAndroid Build Coastguard Worker uptr ReleasedPagesCount = 0;
96*76559068SAndroid Build Coastguard Worker };
97*76559068SAndroid Build Coastguard Worker
98*76559068SAndroid Build Coastguard Worker template <uptr GroupSize, uptr NumGroups>
99*76559068SAndroid Build Coastguard Worker class MemoryGroupFragmentationRecorder {
100*76559068SAndroid Build Coastguard Worker public:
101*76559068SAndroid Build Coastguard Worker const uptr NumPagesInOneGroup = GroupSize / getPageSizeCached();
102*76559068SAndroid Build Coastguard Worker
releasePageRangeToOS(uptr From,uptr To)103*76559068SAndroid Build Coastguard Worker void releasePageRangeToOS(uptr From, uptr To) {
104*76559068SAndroid Build Coastguard Worker for (uptr I = From / getPageSizeCached(); I < To / getPageSizeCached(); ++I)
105*76559068SAndroid Build Coastguard Worker ++FreePagesCount[I / NumPagesInOneGroup];
106*76559068SAndroid Build Coastguard Worker }
107*76559068SAndroid Build Coastguard Worker
getNumFreePages(uptr GroupId)108*76559068SAndroid Build Coastguard Worker uptr getNumFreePages(uptr GroupId) { return FreePagesCount[GroupId]; }
109*76559068SAndroid Build Coastguard Worker
110*76559068SAndroid Build Coastguard Worker private:
111*76559068SAndroid Build Coastguard Worker uptr FreePagesCount[NumGroups] = {};
112*76559068SAndroid Build Coastguard Worker };
113*76559068SAndroid Build Coastguard Worker
114*76559068SAndroid Build Coastguard Worker // A buffer pool which holds a fixed number of static buffers of `uptr` elements
115*76559068SAndroid Build Coastguard Worker // for fast buffer allocation. If the request size is greater than
116*76559068SAndroid Build Coastguard Worker // `StaticBufferNumElements` or if all the static buffers are in use, it'll
117*76559068SAndroid Build Coastguard Worker // delegate the allocation to map().
118*76559068SAndroid Build Coastguard Worker template <uptr StaticBufferCount, uptr StaticBufferNumElements>
119*76559068SAndroid Build Coastguard Worker class BufferPool {
120*76559068SAndroid Build Coastguard Worker public:
121*76559068SAndroid Build Coastguard Worker // Preserve 1 bit in the `Mask` so that we don't need to do zero-check while
122*76559068SAndroid Build Coastguard Worker // extracting the least significant bit from the `Mask`.
123*76559068SAndroid Build Coastguard Worker static_assert(StaticBufferCount < SCUDO_WORDSIZE, "");
124*76559068SAndroid Build Coastguard Worker static_assert(isAligned(StaticBufferNumElements * sizeof(uptr),
125*76559068SAndroid Build Coastguard Worker SCUDO_CACHE_LINE_SIZE),
126*76559068SAndroid Build Coastguard Worker "");
127*76559068SAndroid Build Coastguard Worker
128*76559068SAndroid Build Coastguard Worker struct Buffer {
129*76559068SAndroid Build Coastguard Worker // Pointer to the buffer's memory, or nullptr if no buffer was allocated.
130*76559068SAndroid Build Coastguard Worker uptr *Data = nullptr;
131*76559068SAndroid Build Coastguard Worker
132*76559068SAndroid Build Coastguard Worker // The index of the underlying static buffer, or StaticBufferCount if this
133*76559068SAndroid Build Coastguard Worker // buffer was dynamically allocated. This value is initially set to a poison
134*76559068SAndroid Build Coastguard Worker // value to aid debugging.
135*76559068SAndroid Build Coastguard Worker uptr BufferIndex = ~static_cast<uptr>(0);
136*76559068SAndroid Build Coastguard Worker
137*76559068SAndroid Build Coastguard Worker // Only valid if BufferIndex == StaticBufferCount.
138*76559068SAndroid Build Coastguard Worker MemMapT MemMap = {};
139*76559068SAndroid Build Coastguard Worker };
140*76559068SAndroid Build Coastguard Worker
141*76559068SAndroid Build Coastguard Worker // Return a zero-initialized buffer which can contain at least the given
142*76559068SAndroid Build Coastguard Worker // number of elements, or nullptr on failure.
getBuffer(const uptr NumElements)143*76559068SAndroid Build Coastguard Worker Buffer getBuffer(const uptr NumElements) {
144*76559068SAndroid Build Coastguard Worker if (UNLIKELY(NumElements > StaticBufferNumElements))
145*76559068SAndroid Build Coastguard Worker return getDynamicBuffer(NumElements);
146*76559068SAndroid Build Coastguard Worker
147*76559068SAndroid Build Coastguard Worker uptr index;
148*76559068SAndroid Build Coastguard Worker {
149*76559068SAndroid Build Coastguard Worker // TODO: In general, we expect this operation should be fast so the
150*76559068SAndroid Build Coastguard Worker // waiting thread won't be put into sleep. The HybridMutex does implement
151*76559068SAndroid Build Coastguard Worker // the busy-waiting but we may want to review the performance and see if
152*76559068SAndroid Build Coastguard Worker // we need an explict spin lock here.
153*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex);
154*76559068SAndroid Build Coastguard Worker index = getLeastSignificantSetBitIndex(Mask);
155*76559068SAndroid Build Coastguard Worker if (index < StaticBufferCount)
156*76559068SAndroid Build Coastguard Worker Mask ^= static_cast<uptr>(1) << index;
157*76559068SAndroid Build Coastguard Worker }
158*76559068SAndroid Build Coastguard Worker
159*76559068SAndroid Build Coastguard Worker if (index >= StaticBufferCount)
160*76559068SAndroid Build Coastguard Worker return getDynamicBuffer(NumElements);
161*76559068SAndroid Build Coastguard Worker
162*76559068SAndroid Build Coastguard Worker Buffer Buf;
163*76559068SAndroid Build Coastguard Worker Buf.Data = &RawBuffer[index * StaticBufferNumElements];
164*76559068SAndroid Build Coastguard Worker Buf.BufferIndex = index;
165*76559068SAndroid Build Coastguard Worker memset(Buf.Data, 0, StaticBufferNumElements * sizeof(uptr));
166*76559068SAndroid Build Coastguard Worker return Buf;
167*76559068SAndroid Build Coastguard Worker }
168*76559068SAndroid Build Coastguard Worker
releaseBuffer(Buffer Buf)169*76559068SAndroid Build Coastguard Worker void releaseBuffer(Buffer Buf) {
170*76559068SAndroid Build Coastguard Worker DCHECK_NE(Buf.Data, nullptr);
171*76559068SAndroid Build Coastguard Worker DCHECK_LE(Buf.BufferIndex, StaticBufferCount);
172*76559068SAndroid Build Coastguard Worker if (Buf.BufferIndex != StaticBufferCount) {
173*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex);
174*76559068SAndroid Build Coastguard Worker DCHECK_EQ((Mask & (static_cast<uptr>(1) << Buf.BufferIndex)), 0U);
175*76559068SAndroid Build Coastguard Worker Mask |= static_cast<uptr>(1) << Buf.BufferIndex;
176*76559068SAndroid Build Coastguard Worker } else {
177*76559068SAndroid Build Coastguard Worker Buf.MemMap.unmap();
178*76559068SAndroid Build Coastguard Worker }
179*76559068SAndroid Build Coastguard Worker }
180*76559068SAndroid Build Coastguard Worker
isStaticBufferTestOnly(const Buffer & Buf)181*76559068SAndroid Build Coastguard Worker bool isStaticBufferTestOnly(const Buffer &Buf) {
182*76559068SAndroid Build Coastguard Worker DCHECK_NE(Buf.Data, nullptr);
183*76559068SAndroid Build Coastguard Worker DCHECK_LE(Buf.BufferIndex, StaticBufferCount);
184*76559068SAndroid Build Coastguard Worker return Buf.BufferIndex != StaticBufferCount;
185*76559068SAndroid Build Coastguard Worker }
186*76559068SAndroid Build Coastguard Worker
187*76559068SAndroid Build Coastguard Worker private:
getDynamicBuffer(const uptr NumElements)188*76559068SAndroid Build Coastguard Worker Buffer getDynamicBuffer(const uptr NumElements) {
189*76559068SAndroid Build Coastguard Worker // When using a heap-based buffer, precommit the pages backing the
190*76559068SAndroid Build Coastguard Worker // Vmar by passing |MAP_PRECOMMIT| flag. This allows an optimization
191*76559068SAndroid Build Coastguard Worker // where page fault exceptions are skipped as the allocated memory
192*76559068SAndroid Build Coastguard Worker // is accessed. So far, this is only enabled on Fuchsia. It hasn't proven a
193*76559068SAndroid Build Coastguard Worker // performance benefit on other platforms.
194*76559068SAndroid Build Coastguard Worker const uptr MmapFlags = MAP_ALLOWNOMEM | (SCUDO_FUCHSIA ? MAP_PRECOMMIT : 0);
195*76559068SAndroid Build Coastguard Worker const uptr MappedSize =
196*76559068SAndroid Build Coastguard Worker roundUp(NumElements * sizeof(uptr), getPageSizeCached());
197*76559068SAndroid Build Coastguard Worker Buffer Buf;
198*76559068SAndroid Build Coastguard Worker if (Buf.MemMap.map(/*Addr=*/0, MappedSize, "scudo:counters", MmapFlags)) {
199*76559068SAndroid Build Coastguard Worker Buf.Data = reinterpret_cast<uptr *>(Buf.MemMap.getBase());
200*76559068SAndroid Build Coastguard Worker Buf.BufferIndex = StaticBufferCount;
201*76559068SAndroid Build Coastguard Worker }
202*76559068SAndroid Build Coastguard Worker return Buf;
203*76559068SAndroid Build Coastguard Worker }
204*76559068SAndroid Build Coastguard Worker
205*76559068SAndroid Build Coastguard Worker HybridMutex Mutex;
206*76559068SAndroid Build Coastguard Worker // '1' means that buffer index is not used. '0' means the buffer is in use.
207*76559068SAndroid Build Coastguard Worker uptr Mask GUARDED_BY(Mutex) = ~static_cast<uptr>(0);
208*76559068SAndroid Build Coastguard Worker uptr RawBuffer[StaticBufferCount * StaticBufferNumElements] GUARDED_BY(Mutex);
209*76559068SAndroid Build Coastguard Worker };
210*76559068SAndroid Build Coastguard Worker
211*76559068SAndroid Build Coastguard Worker // A Region page map is used to record the usage of pages in the regions. It
212*76559068SAndroid Build Coastguard Worker // implements a packed array of Counters. Each counter occupies 2^N bits, enough
213*76559068SAndroid Build Coastguard Worker // to store counter's MaxValue. Ctor will try to use a static buffer first, and
214*76559068SAndroid Build Coastguard Worker // if that fails (the buffer is too small or already locked), will allocate the
215*76559068SAndroid Build Coastguard Worker // required Buffer via map(). The caller is expected to check whether the
216*76559068SAndroid Build Coastguard Worker // initialization was successful by checking isAllocated() result. For
217*76559068SAndroid Build Coastguard Worker // performance sake, none of the accessors check the validity of the arguments,
218*76559068SAndroid Build Coastguard Worker // It is assumed that Index is always in [0, N) range and the value is not
219*76559068SAndroid Build Coastguard Worker // incremented past MaxValue.
220*76559068SAndroid Build Coastguard Worker class RegionPageMap {
221*76559068SAndroid Build Coastguard Worker public:
RegionPageMap()222*76559068SAndroid Build Coastguard Worker RegionPageMap()
223*76559068SAndroid Build Coastguard Worker : Regions(0), NumCounters(0), CounterSizeBitsLog(0), CounterMask(0),
224*76559068SAndroid Build Coastguard Worker PackingRatioLog(0), BitOffsetMask(0), SizePerRegion(0),
225*76559068SAndroid Build Coastguard Worker BufferNumElements(0) {}
RegionPageMap(uptr NumberOfRegions,uptr CountersPerRegion,uptr MaxValue)226*76559068SAndroid Build Coastguard Worker RegionPageMap(uptr NumberOfRegions, uptr CountersPerRegion, uptr MaxValue) {
227*76559068SAndroid Build Coastguard Worker reset(NumberOfRegions, CountersPerRegion, MaxValue);
228*76559068SAndroid Build Coastguard Worker }
~RegionPageMap()229*76559068SAndroid Build Coastguard Worker ~RegionPageMap() {
230*76559068SAndroid Build Coastguard Worker if (!isAllocated())
231*76559068SAndroid Build Coastguard Worker return;
232*76559068SAndroid Build Coastguard Worker Buffers.releaseBuffer(Buffer);
233*76559068SAndroid Build Coastguard Worker Buffer = {};
234*76559068SAndroid Build Coastguard Worker }
235*76559068SAndroid Build Coastguard Worker
236*76559068SAndroid Build Coastguard Worker // Lock of `StaticBuffer` is acquired conditionally and there's no easy way to
237*76559068SAndroid Build Coastguard Worker // specify the thread-safety attribute properly in current code structure.
238*76559068SAndroid Build Coastguard Worker // Besides, it's the only place we may want to check thread safety. Therefore,
239*76559068SAndroid Build Coastguard Worker // it's fine to bypass the thread-safety analysis now.
reset(uptr NumberOfRegion,uptr CountersPerRegion,uptr MaxValue)240*76559068SAndroid Build Coastguard Worker void reset(uptr NumberOfRegion, uptr CountersPerRegion, uptr MaxValue) {
241*76559068SAndroid Build Coastguard Worker DCHECK_GT(NumberOfRegion, 0);
242*76559068SAndroid Build Coastguard Worker DCHECK_GT(CountersPerRegion, 0);
243*76559068SAndroid Build Coastguard Worker DCHECK_GT(MaxValue, 0);
244*76559068SAndroid Build Coastguard Worker
245*76559068SAndroid Build Coastguard Worker Regions = NumberOfRegion;
246*76559068SAndroid Build Coastguard Worker NumCounters = CountersPerRegion;
247*76559068SAndroid Build Coastguard Worker
248*76559068SAndroid Build Coastguard Worker constexpr uptr MaxCounterBits = sizeof(*Buffer.Data) * 8UL;
249*76559068SAndroid Build Coastguard Worker // Rounding counter storage size up to the power of two allows for using
250*76559068SAndroid Build Coastguard Worker // bit shifts calculating particular counter's Index and offset.
251*76559068SAndroid Build Coastguard Worker const uptr CounterSizeBits =
252*76559068SAndroid Build Coastguard Worker roundUpPowerOfTwo(getMostSignificantSetBitIndex(MaxValue) + 1);
253*76559068SAndroid Build Coastguard Worker DCHECK_LE(CounterSizeBits, MaxCounterBits);
254*76559068SAndroid Build Coastguard Worker CounterSizeBitsLog = getLog2(CounterSizeBits);
255*76559068SAndroid Build Coastguard Worker CounterMask = ~(static_cast<uptr>(0)) >> (MaxCounterBits - CounterSizeBits);
256*76559068SAndroid Build Coastguard Worker
257*76559068SAndroid Build Coastguard Worker const uptr PackingRatio = MaxCounterBits >> CounterSizeBitsLog;
258*76559068SAndroid Build Coastguard Worker DCHECK_GT(PackingRatio, 0);
259*76559068SAndroid Build Coastguard Worker PackingRatioLog = getLog2(PackingRatio);
260*76559068SAndroid Build Coastguard Worker BitOffsetMask = PackingRatio - 1;
261*76559068SAndroid Build Coastguard Worker
262*76559068SAndroid Build Coastguard Worker SizePerRegion =
263*76559068SAndroid Build Coastguard Worker roundUp(NumCounters, static_cast<uptr>(1U) << PackingRatioLog) >>
264*76559068SAndroid Build Coastguard Worker PackingRatioLog;
265*76559068SAndroid Build Coastguard Worker BufferNumElements = SizePerRegion * Regions;
266*76559068SAndroid Build Coastguard Worker Buffer = Buffers.getBuffer(BufferNumElements);
267*76559068SAndroid Build Coastguard Worker }
268*76559068SAndroid Build Coastguard Worker
isAllocated()269*76559068SAndroid Build Coastguard Worker bool isAllocated() const { return Buffer.Data != nullptr; }
270*76559068SAndroid Build Coastguard Worker
getCount()271*76559068SAndroid Build Coastguard Worker uptr getCount() const { return NumCounters; }
272*76559068SAndroid Build Coastguard Worker
get(uptr Region,uptr I)273*76559068SAndroid Build Coastguard Worker uptr get(uptr Region, uptr I) const {
274*76559068SAndroid Build Coastguard Worker DCHECK_LT(Region, Regions);
275*76559068SAndroid Build Coastguard Worker DCHECK_LT(I, NumCounters);
276*76559068SAndroid Build Coastguard Worker const uptr Index = I >> PackingRatioLog;
277*76559068SAndroid Build Coastguard Worker const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
278*76559068SAndroid Build Coastguard Worker return (Buffer.Data[Region * SizePerRegion + Index] >> BitOffset) &
279*76559068SAndroid Build Coastguard Worker CounterMask;
280*76559068SAndroid Build Coastguard Worker }
281*76559068SAndroid Build Coastguard Worker
inc(uptr Region,uptr I)282*76559068SAndroid Build Coastguard Worker void inc(uptr Region, uptr I) const {
283*76559068SAndroid Build Coastguard Worker DCHECK_LT(get(Region, I), CounterMask);
284*76559068SAndroid Build Coastguard Worker const uptr Index = I >> PackingRatioLog;
285*76559068SAndroid Build Coastguard Worker const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
286*76559068SAndroid Build Coastguard Worker DCHECK_LT(BitOffset, SCUDO_WORDSIZE);
287*76559068SAndroid Build Coastguard Worker DCHECK_EQ(isAllCounted(Region, I), false);
288*76559068SAndroid Build Coastguard Worker Buffer.Data[Region * SizePerRegion + Index] += static_cast<uptr>(1U)
289*76559068SAndroid Build Coastguard Worker << BitOffset;
290*76559068SAndroid Build Coastguard Worker }
291*76559068SAndroid Build Coastguard Worker
incN(uptr Region,uptr I,uptr N)292*76559068SAndroid Build Coastguard Worker void incN(uptr Region, uptr I, uptr N) const {
293*76559068SAndroid Build Coastguard Worker DCHECK_GT(N, 0U);
294*76559068SAndroid Build Coastguard Worker DCHECK_LE(N, CounterMask);
295*76559068SAndroid Build Coastguard Worker DCHECK_LE(get(Region, I), CounterMask - N);
296*76559068SAndroid Build Coastguard Worker const uptr Index = I >> PackingRatioLog;
297*76559068SAndroid Build Coastguard Worker const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
298*76559068SAndroid Build Coastguard Worker DCHECK_LT(BitOffset, SCUDO_WORDSIZE);
299*76559068SAndroid Build Coastguard Worker DCHECK_EQ(isAllCounted(Region, I), false);
300*76559068SAndroid Build Coastguard Worker Buffer.Data[Region * SizePerRegion + Index] += N << BitOffset;
301*76559068SAndroid Build Coastguard Worker }
302*76559068SAndroid Build Coastguard Worker
incRange(uptr Region,uptr From,uptr To)303*76559068SAndroid Build Coastguard Worker void incRange(uptr Region, uptr From, uptr To) const {
304*76559068SAndroid Build Coastguard Worker DCHECK_LE(From, To);
305*76559068SAndroid Build Coastguard Worker const uptr Top = Min(To + 1, NumCounters);
306*76559068SAndroid Build Coastguard Worker for (uptr I = From; I < Top; I++)
307*76559068SAndroid Build Coastguard Worker inc(Region, I);
308*76559068SAndroid Build Coastguard Worker }
309*76559068SAndroid Build Coastguard Worker
310*76559068SAndroid Build Coastguard Worker // Set the counter to the max value. Note that the max number of blocks in a
311*76559068SAndroid Build Coastguard Worker // page may vary. To provide an easier way to tell if all the blocks are
312*76559068SAndroid Build Coastguard Worker // counted for different pages, set to the same max value to denote the
313*76559068SAndroid Build Coastguard Worker // all-counted status.
setAsAllCounted(uptr Region,uptr I)314*76559068SAndroid Build Coastguard Worker void setAsAllCounted(uptr Region, uptr I) const {
315*76559068SAndroid Build Coastguard Worker DCHECK_LE(get(Region, I), CounterMask);
316*76559068SAndroid Build Coastguard Worker const uptr Index = I >> PackingRatioLog;
317*76559068SAndroid Build Coastguard Worker const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
318*76559068SAndroid Build Coastguard Worker DCHECK_LT(BitOffset, SCUDO_WORDSIZE);
319*76559068SAndroid Build Coastguard Worker Buffer.Data[Region * SizePerRegion + Index] |= CounterMask << BitOffset;
320*76559068SAndroid Build Coastguard Worker }
setAsAllCountedRange(uptr Region,uptr From,uptr To)321*76559068SAndroid Build Coastguard Worker void setAsAllCountedRange(uptr Region, uptr From, uptr To) const {
322*76559068SAndroid Build Coastguard Worker DCHECK_LE(From, To);
323*76559068SAndroid Build Coastguard Worker const uptr Top = Min(To + 1, NumCounters);
324*76559068SAndroid Build Coastguard Worker for (uptr I = From; I < Top; I++)
325*76559068SAndroid Build Coastguard Worker setAsAllCounted(Region, I);
326*76559068SAndroid Build Coastguard Worker }
327*76559068SAndroid Build Coastguard Worker
updateAsAllCountedIf(uptr Region,uptr I,uptr MaxCount)328*76559068SAndroid Build Coastguard Worker bool updateAsAllCountedIf(uptr Region, uptr I, uptr MaxCount) {
329*76559068SAndroid Build Coastguard Worker const uptr Count = get(Region, I);
330*76559068SAndroid Build Coastguard Worker if (Count == CounterMask)
331*76559068SAndroid Build Coastguard Worker return true;
332*76559068SAndroid Build Coastguard Worker if (Count == MaxCount) {
333*76559068SAndroid Build Coastguard Worker setAsAllCounted(Region, I);
334*76559068SAndroid Build Coastguard Worker return true;
335*76559068SAndroid Build Coastguard Worker }
336*76559068SAndroid Build Coastguard Worker return false;
337*76559068SAndroid Build Coastguard Worker }
isAllCounted(uptr Region,uptr I)338*76559068SAndroid Build Coastguard Worker bool isAllCounted(uptr Region, uptr I) const {
339*76559068SAndroid Build Coastguard Worker return get(Region, I) == CounterMask;
340*76559068SAndroid Build Coastguard Worker }
341*76559068SAndroid Build Coastguard Worker
getBufferNumElements()342*76559068SAndroid Build Coastguard Worker uptr getBufferNumElements() const { return BufferNumElements; }
343*76559068SAndroid Build Coastguard Worker
344*76559068SAndroid Build Coastguard Worker private:
345*76559068SAndroid Build Coastguard Worker // We may consider making this configurable if there are cases which may
346*76559068SAndroid Build Coastguard Worker // benefit from this.
347*76559068SAndroid Build Coastguard Worker static const uptr StaticBufferCount = 2U;
348*76559068SAndroid Build Coastguard Worker static const uptr StaticBufferNumElements = 512U;
349*76559068SAndroid Build Coastguard Worker using BufferPoolT = BufferPool<StaticBufferCount, StaticBufferNumElements>;
350*76559068SAndroid Build Coastguard Worker static BufferPoolT Buffers;
351*76559068SAndroid Build Coastguard Worker
352*76559068SAndroid Build Coastguard Worker uptr Regions;
353*76559068SAndroid Build Coastguard Worker uptr NumCounters;
354*76559068SAndroid Build Coastguard Worker uptr CounterSizeBitsLog;
355*76559068SAndroid Build Coastguard Worker uptr CounterMask;
356*76559068SAndroid Build Coastguard Worker uptr PackingRatioLog;
357*76559068SAndroid Build Coastguard Worker uptr BitOffsetMask;
358*76559068SAndroid Build Coastguard Worker
359*76559068SAndroid Build Coastguard Worker uptr SizePerRegion;
360*76559068SAndroid Build Coastguard Worker uptr BufferNumElements;
361*76559068SAndroid Build Coastguard Worker BufferPoolT::Buffer Buffer;
362*76559068SAndroid Build Coastguard Worker };
363*76559068SAndroid Build Coastguard Worker
364*76559068SAndroid Build Coastguard Worker template <class ReleaseRecorderT> class FreePagesRangeTracker {
365*76559068SAndroid Build Coastguard Worker public:
FreePagesRangeTracker(ReleaseRecorderT & Recorder)366*76559068SAndroid Build Coastguard Worker explicit FreePagesRangeTracker(ReleaseRecorderT &Recorder)
367*76559068SAndroid Build Coastguard Worker : Recorder(Recorder) {}
368*76559068SAndroid Build Coastguard Worker
processNextPage(bool Released)369*76559068SAndroid Build Coastguard Worker void processNextPage(bool Released) {
370*76559068SAndroid Build Coastguard Worker if (Released) {
371*76559068SAndroid Build Coastguard Worker if (!InRange) {
372*76559068SAndroid Build Coastguard Worker CurrentRangeStatePage = CurrentPage;
373*76559068SAndroid Build Coastguard Worker InRange = true;
374*76559068SAndroid Build Coastguard Worker }
375*76559068SAndroid Build Coastguard Worker } else {
376*76559068SAndroid Build Coastguard Worker closeOpenedRange();
377*76559068SAndroid Build Coastguard Worker }
378*76559068SAndroid Build Coastguard Worker CurrentPage++;
379*76559068SAndroid Build Coastguard Worker }
380*76559068SAndroid Build Coastguard Worker
skipPages(uptr N)381*76559068SAndroid Build Coastguard Worker void skipPages(uptr N) {
382*76559068SAndroid Build Coastguard Worker closeOpenedRange();
383*76559068SAndroid Build Coastguard Worker CurrentPage += N;
384*76559068SAndroid Build Coastguard Worker }
385*76559068SAndroid Build Coastguard Worker
finish()386*76559068SAndroid Build Coastguard Worker void finish() { closeOpenedRange(); }
387*76559068SAndroid Build Coastguard Worker
388*76559068SAndroid Build Coastguard Worker private:
closeOpenedRange()389*76559068SAndroid Build Coastguard Worker void closeOpenedRange() {
390*76559068SAndroid Build Coastguard Worker if (InRange) {
391*76559068SAndroid Build Coastguard Worker const uptr PageSizeLog = getPageSizeLogCached();
392*76559068SAndroid Build Coastguard Worker Recorder.releasePageRangeToOS((CurrentRangeStatePage << PageSizeLog),
393*76559068SAndroid Build Coastguard Worker (CurrentPage << PageSizeLog));
394*76559068SAndroid Build Coastguard Worker InRange = false;
395*76559068SAndroid Build Coastguard Worker }
396*76559068SAndroid Build Coastguard Worker }
397*76559068SAndroid Build Coastguard Worker
398*76559068SAndroid Build Coastguard Worker ReleaseRecorderT &Recorder;
399*76559068SAndroid Build Coastguard Worker bool InRange = false;
400*76559068SAndroid Build Coastguard Worker uptr CurrentPage = 0;
401*76559068SAndroid Build Coastguard Worker uptr CurrentRangeStatePage = 0;
402*76559068SAndroid Build Coastguard Worker };
403*76559068SAndroid Build Coastguard Worker
404*76559068SAndroid Build Coastguard Worker struct PageReleaseContext {
405*76559068SAndroid Build Coastguard Worker PageReleaseContext(uptr BlockSize, uptr NumberOfRegions, uptr ReleaseSize,
406*76559068SAndroid Build Coastguard Worker uptr ReleaseOffset = 0)
BlockSizePageReleaseContext407*76559068SAndroid Build Coastguard Worker : BlockSize(BlockSize), NumberOfRegions(NumberOfRegions) {
408*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached();
409*76559068SAndroid Build Coastguard Worker if (BlockSize <= PageSize) {
410*76559068SAndroid Build Coastguard Worker if (PageSize % BlockSize == 0) {
411*76559068SAndroid Build Coastguard Worker // Same number of chunks per page, no cross overs.
412*76559068SAndroid Build Coastguard Worker FullPagesBlockCountMax = PageSize / BlockSize;
413*76559068SAndroid Build Coastguard Worker SameBlockCountPerPage = true;
414*76559068SAndroid Build Coastguard Worker } else if (BlockSize % (PageSize % BlockSize) == 0) {
415*76559068SAndroid Build Coastguard Worker // Some chunks are crossing page boundaries, which means that the page
416*76559068SAndroid Build Coastguard Worker // contains one or two partial chunks, but all pages contain the same
417*76559068SAndroid Build Coastguard Worker // number of chunks.
418*76559068SAndroid Build Coastguard Worker FullPagesBlockCountMax = PageSize / BlockSize + 1;
419*76559068SAndroid Build Coastguard Worker SameBlockCountPerPage = true;
420*76559068SAndroid Build Coastguard Worker } else {
421*76559068SAndroid Build Coastguard Worker // Some chunks are crossing page boundaries, which means that the page
422*76559068SAndroid Build Coastguard Worker // contains one or two partial chunks.
423*76559068SAndroid Build Coastguard Worker FullPagesBlockCountMax = PageSize / BlockSize + 2;
424*76559068SAndroid Build Coastguard Worker SameBlockCountPerPage = false;
425*76559068SAndroid Build Coastguard Worker }
426*76559068SAndroid Build Coastguard Worker } else {
427*76559068SAndroid Build Coastguard Worker if ((BlockSize & (PageSize - 1)) == 0) {
428*76559068SAndroid Build Coastguard Worker // One chunk covers multiple pages, no cross overs.
429*76559068SAndroid Build Coastguard Worker FullPagesBlockCountMax = 1;
430*76559068SAndroid Build Coastguard Worker SameBlockCountPerPage = true;
431*76559068SAndroid Build Coastguard Worker } else {
432*76559068SAndroid Build Coastguard Worker // One chunk covers multiple pages, Some chunks are crossing page
433*76559068SAndroid Build Coastguard Worker // boundaries. Some pages contain one chunk, some contain two.
434*76559068SAndroid Build Coastguard Worker FullPagesBlockCountMax = 2;
435*76559068SAndroid Build Coastguard Worker SameBlockCountPerPage = false;
436*76559068SAndroid Build Coastguard Worker }
437*76559068SAndroid Build Coastguard Worker }
438*76559068SAndroid Build Coastguard Worker
439*76559068SAndroid Build Coastguard Worker // TODO: For multiple regions, it's more complicated to support partial
440*76559068SAndroid Build Coastguard Worker // region marking (which includes the complexity of how to handle the last
441*76559068SAndroid Build Coastguard Worker // block in a region). We may consider this after markFreeBlocks() accepts
442*76559068SAndroid Build Coastguard Worker // only free blocks from the same region.
443*76559068SAndroid Build Coastguard Worker if (NumberOfRegions != 1)
444*76559068SAndroid Build Coastguard Worker DCHECK_EQ(ReleaseOffset, 0U);
445*76559068SAndroid Build Coastguard Worker
446*76559068SAndroid Build Coastguard Worker const uptr PageSizeLog = getPageSizeLogCached();
447*76559068SAndroid Build Coastguard Worker PagesCount = roundUp(ReleaseSize, PageSize) >> PageSizeLog;
448*76559068SAndroid Build Coastguard Worker ReleasePageOffset = ReleaseOffset >> PageSizeLog;
449*76559068SAndroid Build Coastguard Worker }
450*76559068SAndroid Build Coastguard Worker
451*76559068SAndroid Build Coastguard Worker // PageMap is lazily allocated when markFreeBlocks() is invoked.
hasBlockMarkedPageReleaseContext452*76559068SAndroid Build Coastguard Worker bool hasBlockMarked() const {
453*76559068SAndroid Build Coastguard Worker return PageMap.isAllocated();
454*76559068SAndroid Build Coastguard Worker }
455*76559068SAndroid Build Coastguard Worker
ensurePageMapAllocatedPageReleaseContext456*76559068SAndroid Build Coastguard Worker bool ensurePageMapAllocated() {
457*76559068SAndroid Build Coastguard Worker if (PageMap.isAllocated())
458*76559068SAndroid Build Coastguard Worker return true;
459*76559068SAndroid Build Coastguard Worker PageMap.reset(NumberOfRegions, PagesCount, FullPagesBlockCountMax);
460*76559068SAndroid Build Coastguard Worker // TODO: Log some message when we fail on PageMap allocation.
461*76559068SAndroid Build Coastguard Worker return PageMap.isAllocated();
462*76559068SAndroid Build Coastguard Worker }
463*76559068SAndroid Build Coastguard Worker
464*76559068SAndroid Build Coastguard Worker // Mark all the blocks in the given range [From, to). Instead of visiting all
465*76559068SAndroid Build Coastguard Worker // the blocks, we will just mark the page as all counted. Note the `From` and
466*76559068SAndroid Build Coastguard Worker // `To` has to be page aligned but with one exception, if `To` is equal to the
467*76559068SAndroid Build Coastguard Worker // RegionSize, it's not necessary to be aligned with page size.
markRangeAsAllCountedPageReleaseContext468*76559068SAndroid Build Coastguard Worker bool markRangeAsAllCounted(uptr From, uptr To, uptr Base,
469*76559068SAndroid Build Coastguard Worker const uptr RegionIndex, const uptr RegionSize) {
470*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached();
471*76559068SAndroid Build Coastguard Worker DCHECK_LT(From, To);
472*76559068SAndroid Build Coastguard Worker DCHECK_LE(To, Base + RegionSize);
473*76559068SAndroid Build Coastguard Worker DCHECK_EQ(From % PageSize, 0U);
474*76559068SAndroid Build Coastguard Worker DCHECK_LE(To - From, RegionSize);
475*76559068SAndroid Build Coastguard Worker
476*76559068SAndroid Build Coastguard Worker if (!ensurePageMapAllocated())
477*76559068SAndroid Build Coastguard Worker return false;
478*76559068SAndroid Build Coastguard Worker
479*76559068SAndroid Build Coastguard Worker uptr FromInRegion = From - Base;
480*76559068SAndroid Build Coastguard Worker uptr ToInRegion = To - Base;
481*76559068SAndroid Build Coastguard Worker uptr FirstBlockInRange = roundUpSlow(FromInRegion, BlockSize);
482*76559068SAndroid Build Coastguard Worker
483*76559068SAndroid Build Coastguard Worker // The straddling block sits across entire range.
484*76559068SAndroid Build Coastguard Worker if (FirstBlockInRange >= ToInRegion)
485*76559068SAndroid Build Coastguard Worker return true;
486*76559068SAndroid Build Coastguard Worker
487*76559068SAndroid Build Coastguard Worker // First block may not sit at the first pape in the range, move
488*76559068SAndroid Build Coastguard Worker // `FromInRegion` to the first block page.
489*76559068SAndroid Build Coastguard Worker FromInRegion = roundDown(FirstBlockInRange, PageSize);
490*76559068SAndroid Build Coastguard Worker
491*76559068SAndroid Build Coastguard Worker // When The first block is not aligned to the range boundary, which means
492*76559068SAndroid Build Coastguard Worker // there is a block sitting acorss `From`, that looks like,
493*76559068SAndroid Build Coastguard Worker //
494*76559068SAndroid Build Coastguard Worker // From To
495*76559068SAndroid Build Coastguard Worker // V V
496*76559068SAndroid Build Coastguard Worker // +-----------------------------------------------+
497*76559068SAndroid Build Coastguard Worker // +-----+-----+-----+-----+
498*76559068SAndroid Build Coastguard Worker // | | | | | ...
499*76559068SAndroid Build Coastguard Worker // +-----+-----+-----+-----+
500*76559068SAndroid Build Coastguard Worker // |- first page -||- second page -||- ...
501*76559068SAndroid Build Coastguard Worker //
502*76559068SAndroid Build Coastguard Worker // Therefore, we can't just mark the first page as all counted. Instead, we
503*76559068SAndroid Build Coastguard Worker // increment the number of blocks in the first page in the page map and
504*76559068SAndroid Build Coastguard Worker // then round up the `From` to the next page.
505*76559068SAndroid Build Coastguard Worker if (FirstBlockInRange != FromInRegion) {
506*76559068SAndroid Build Coastguard Worker DCHECK_GT(FromInRegion + PageSize, FirstBlockInRange);
507*76559068SAndroid Build Coastguard Worker uptr NumBlocksInFirstPage =
508*76559068SAndroid Build Coastguard Worker (FromInRegion + PageSize - FirstBlockInRange + BlockSize - 1) /
509*76559068SAndroid Build Coastguard Worker BlockSize;
510*76559068SAndroid Build Coastguard Worker PageMap.incN(RegionIndex, getPageIndex(FromInRegion),
511*76559068SAndroid Build Coastguard Worker NumBlocksInFirstPage);
512*76559068SAndroid Build Coastguard Worker FromInRegion = roundUp(FromInRegion + 1, PageSize);
513*76559068SAndroid Build Coastguard Worker }
514*76559068SAndroid Build Coastguard Worker
515*76559068SAndroid Build Coastguard Worker uptr LastBlockInRange = roundDownSlow(ToInRegion - 1, BlockSize);
516*76559068SAndroid Build Coastguard Worker
517*76559068SAndroid Build Coastguard Worker // Note that LastBlockInRange may be smaller than `FromInRegion` at this
518*76559068SAndroid Build Coastguard Worker // point because it may contain only one block in the range.
519*76559068SAndroid Build Coastguard Worker
520*76559068SAndroid Build Coastguard Worker // When the last block sits across `To`, we can't just mark the pages
521*76559068SAndroid Build Coastguard Worker // occupied by the last block as all counted. Instead, we increment the
522*76559068SAndroid Build Coastguard Worker // counters of those pages by 1. The exception is that if it's the last
523*76559068SAndroid Build Coastguard Worker // block in the region, it's fine to mark those pages as all counted.
524*76559068SAndroid Build Coastguard Worker if (LastBlockInRange + BlockSize != RegionSize) {
525*76559068SAndroid Build Coastguard Worker DCHECK_EQ(ToInRegion % PageSize, 0U);
526*76559068SAndroid Build Coastguard Worker // The case below is like,
527*76559068SAndroid Build Coastguard Worker //
528*76559068SAndroid Build Coastguard Worker // From To
529*76559068SAndroid Build Coastguard Worker // V V
530*76559068SAndroid Build Coastguard Worker // +----------------------------------------+
531*76559068SAndroid Build Coastguard Worker // +-----+-----+-----+-----+
532*76559068SAndroid Build Coastguard Worker // | | | | | ...
533*76559068SAndroid Build Coastguard Worker // +-----+-----+-----+-----+
534*76559068SAndroid Build Coastguard Worker // ... -||- last page -||- next page -|
535*76559068SAndroid Build Coastguard Worker //
536*76559068SAndroid Build Coastguard Worker // The last block is not aligned to `To`, we need to increment the
537*76559068SAndroid Build Coastguard Worker // counter of `next page` by 1.
538*76559068SAndroid Build Coastguard Worker if (LastBlockInRange + BlockSize != ToInRegion) {
539*76559068SAndroid Build Coastguard Worker PageMap.incRange(RegionIndex, getPageIndex(ToInRegion),
540*76559068SAndroid Build Coastguard Worker getPageIndex(LastBlockInRange + BlockSize - 1));
541*76559068SAndroid Build Coastguard Worker }
542*76559068SAndroid Build Coastguard Worker } else {
543*76559068SAndroid Build Coastguard Worker ToInRegion = RegionSize;
544*76559068SAndroid Build Coastguard Worker }
545*76559068SAndroid Build Coastguard Worker
546*76559068SAndroid Build Coastguard Worker // After handling the first page and the last block, it's safe to mark any
547*76559068SAndroid Build Coastguard Worker // page in between the range [From, To).
548*76559068SAndroid Build Coastguard Worker if (FromInRegion < ToInRegion) {
549*76559068SAndroid Build Coastguard Worker PageMap.setAsAllCountedRange(RegionIndex, getPageIndex(FromInRegion),
550*76559068SAndroid Build Coastguard Worker getPageIndex(ToInRegion - 1));
551*76559068SAndroid Build Coastguard Worker }
552*76559068SAndroid Build Coastguard Worker
553*76559068SAndroid Build Coastguard Worker return true;
554*76559068SAndroid Build Coastguard Worker }
555*76559068SAndroid Build Coastguard Worker
556*76559068SAndroid Build Coastguard Worker template <class TransferBatchT, typename DecompactPtrT>
markFreeBlocksInRegionPageReleaseContext557*76559068SAndroid Build Coastguard Worker bool markFreeBlocksInRegion(const IntrusiveList<TransferBatchT> &FreeList,
558*76559068SAndroid Build Coastguard Worker DecompactPtrT DecompactPtr, const uptr Base,
559*76559068SAndroid Build Coastguard Worker const uptr RegionIndex, const uptr RegionSize,
560*76559068SAndroid Build Coastguard Worker bool MayContainLastBlockInRegion) {
561*76559068SAndroid Build Coastguard Worker if (!ensurePageMapAllocated())
562*76559068SAndroid Build Coastguard Worker return false;
563*76559068SAndroid Build Coastguard Worker
564*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached();
565*76559068SAndroid Build Coastguard Worker if (MayContainLastBlockInRegion) {
566*76559068SAndroid Build Coastguard Worker const uptr LastBlockInRegion =
567*76559068SAndroid Build Coastguard Worker ((RegionSize / BlockSize) - 1U) * BlockSize;
568*76559068SAndroid Build Coastguard Worker // The last block in a region may not use the entire page, we mark the
569*76559068SAndroid Build Coastguard Worker // following "pretend" memory block(s) as free in advance.
570*76559068SAndroid Build Coastguard Worker //
571*76559068SAndroid Build Coastguard Worker // Region Boundary
572*76559068SAndroid Build Coastguard Worker // v
573*76559068SAndroid Build Coastguard Worker // -----+-----------------------+
574*76559068SAndroid Build Coastguard Worker // | Last Page | <- Rounded Region Boundary
575*76559068SAndroid Build Coastguard Worker // -----+-----------------------+
576*76559068SAndroid Build Coastguard Worker // |-----||- trailing blocks -|
577*76559068SAndroid Build Coastguard Worker // ^
578*76559068SAndroid Build Coastguard Worker // last block
579*76559068SAndroid Build Coastguard Worker const uptr RoundedRegionSize = roundUp(RegionSize, PageSize);
580*76559068SAndroid Build Coastguard Worker const uptr TrailingBlockBase = LastBlockInRegion + BlockSize;
581*76559068SAndroid Build Coastguard Worker // If the difference between `RoundedRegionSize` and
582*76559068SAndroid Build Coastguard Worker // `TrailingBlockBase` is larger than a page, that implies the reported
583*76559068SAndroid Build Coastguard Worker // `RegionSize` may not be accurate.
584*76559068SAndroid Build Coastguard Worker DCHECK_LT(RoundedRegionSize - TrailingBlockBase, PageSize);
585*76559068SAndroid Build Coastguard Worker
586*76559068SAndroid Build Coastguard Worker // Only the last page touched by the last block needs to mark the trailing
587*76559068SAndroid Build Coastguard Worker // blocks. Note that if the last "pretend" block straddles the boundary,
588*76559068SAndroid Build Coastguard Worker // we still have to count it in so that the logic of counting the number
589*76559068SAndroid Build Coastguard Worker // of blocks on a page is consistent.
590*76559068SAndroid Build Coastguard Worker uptr NumTrailingBlocks =
591*76559068SAndroid Build Coastguard Worker (roundUpSlow(RoundedRegionSize - TrailingBlockBase, BlockSize) +
592*76559068SAndroid Build Coastguard Worker BlockSize - 1) /
593*76559068SAndroid Build Coastguard Worker BlockSize;
594*76559068SAndroid Build Coastguard Worker if (NumTrailingBlocks > 0) {
595*76559068SAndroid Build Coastguard Worker PageMap.incN(RegionIndex, getPageIndex(TrailingBlockBase),
596*76559068SAndroid Build Coastguard Worker NumTrailingBlocks);
597*76559068SAndroid Build Coastguard Worker }
598*76559068SAndroid Build Coastguard Worker }
599*76559068SAndroid Build Coastguard Worker
600*76559068SAndroid Build Coastguard Worker // Iterate over free chunks and count how many free chunks affect each
601*76559068SAndroid Build Coastguard Worker // allocated page.
602*76559068SAndroid Build Coastguard Worker if (BlockSize <= PageSize && PageSize % BlockSize == 0) {
603*76559068SAndroid Build Coastguard Worker // Each chunk affects one page only.
604*76559068SAndroid Build Coastguard Worker for (const auto &It : FreeList) {
605*76559068SAndroid Build Coastguard Worker for (u16 I = 0; I < It.getCount(); I++) {
606*76559068SAndroid Build Coastguard Worker const uptr PInRegion = DecompactPtr(It.get(I)) - Base;
607*76559068SAndroid Build Coastguard Worker DCHECK_LT(PInRegion, RegionSize);
608*76559068SAndroid Build Coastguard Worker PageMap.inc(RegionIndex, getPageIndex(PInRegion));
609*76559068SAndroid Build Coastguard Worker }
610*76559068SAndroid Build Coastguard Worker }
611*76559068SAndroid Build Coastguard Worker } else {
612*76559068SAndroid Build Coastguard Worker // In all other cases chunks might affect more than one page.
613*76559068SAndroid Build Coastguard Worker DCHECK_GE(RegionSize, BlockSize);
614*76559068SAndroid Build Coastguard Worker for (const auto &It : FreeList) {
615*76559068SAndroid Build Coastguard Worker for (u16 I = 0; I < It.getCount(); I++) {
616*76559068SAndroid Build Coastguard Worker const uptr PInRegion = DecompactPtr(It.get(I)) - Base;
617*76559068SAndroid Build Coastguard Worker PageMap.incRange(RegionIndex, getPageIndex(PInRegion),
618*76559068SAndroid Build Coastguard Worker getPageIndex(PInRegion + BlockSize - 1));
619*76559068SAndroid Build Coastguard Worker }
620*76559068SAndroid Build Coastguard Worker }
621*76559068SAndroid Build Coastguard Worker }
622*76559068SAndroid Build Coastguard Worker
623*76559068SAndroid Build Coastguard Worker return true;
624*76559068SAndroid Build Coastguard Worker }
625*76559068SAndroid Build Coastguard Worker
getPageIndexPageReleaseContext626*76559068SAndroid Build Coastguard Worker uptr getPageIndex(uptr P) {
627*76559068SAndroid Build Coastguard Worker return (P >> getPageSizeLogCached()) - ReleasePageOffset;
628*76559068SAndroid Build Coastguard Worker }
getReleaseOffsetPageReleaseContext629*76559068SAndroid Build Coastguard Worker uptr getReleaseOffset() {
630*76559068SAndroid Build Coastguard Worker return ReleasePageOffset << getPageSizeLogCached();
631*76559068SAndroid Build Coastguard Worker }
632*76559068SAndroid Build Coastguard Worker
633*76559068SAndroid Build Coastguard Worker uptr BlockSize;
634*76559068SAndroid Build Coastguard Worker uptr NumberOfRegions;
635*76559068SAndroid Build Coastguard Worker // For partial region marking, some pages in front are not needed to be
636*76559068SAndroid Build Coastguard Worker // counted.
637*76559068SAndroid Build Coastguard Worker uptr ReleasePageOffset;
638*76559068SAndroid Build Coastguard Worker uptr PagesCount;
639*76559068SAndroid Build Coastguard Worker uptr FullPagesBlockCountMax;
640*76559068SAndroid Build Coastguard Worker bool SameBlockCountPerPage;
641*76559068SAndroid Build Coastguard Worker RegionPageMap PageMap;
642*76559068SAndroid Build Coastguard Worker };
643*76559068SAndroid Build Coastguard Worker
644*76559068SAndroid Build Coastguard Worker // Try to release the page which doesn't have any in-used block, i.e., they are
645*76559068SAndroid Build Coastguard Worker // all free blocks. The `PageMap` will record the number of free blocks in each
646*76559068SAndroid Build Coastguard Worker // page.
647*76559068SAndroid Build Coastguard Worker template <class ReleaseRecorderT, typename SkipRegionT>
648*76559068SAndroid Build Coastguard Worker NOINLINE void
releaseFreeMemoryToOS(PageReleaseContext & Context,ReleaseRecorderT & Recorder,SkipRegionT SkipRegion)649*76559068SAndroid Build Coastguard Worker releaseFreeMemoryToOS(PageReleaseContext &Context,
650*76559068SAndroid Build Coastguard Worker ReleaseRecorderT &Recorder, SkipRegionT SkipRegion) {
651*76559068SAndroid Build Coastguard Worker const uptr PageSize = getPageSizeCached();
652*76559068SAndroid Build Coastguard Worker const uptr BlockSize = Context.BlockSize;
653*76559068SAndroid Build Coastguard Worker const uptr PagesCount = Context.PagesCount;
654*76559068SAndroid Build Coastguard Worker const uptr NumberOfRegions = Context.NumberOfRegions;
655*76559068SAndroid Build Coastguard Worker const uptr ReleasePageOffset = Context.ReleasePageOffset;
656*76559068SAndroid Build Coastguard Worker const uptr FullPagesBlockCountMax = Context.FullPagesBlockCountMax;
657*76559068SAndroid Build Coastguard Worker const bool SameBlockCountPerPage = Context.SameBlockCountPerPage;
658*76559068SAndroid Build Coastguard Worker RegionPageMap &PageMap = Context.PageMap;
659*76559068SAndroid Build Coastguard Worker
660*76559068SAndroid Build Coastguard Worker // Iterate over pages detecting ranges of pages with chunk Counters equal
661*76559068SAndroid Build Coastguard Worker // to the expected number of chunks for the particular page.
662*76559068SAndroid Build Coastguard Worker FreePagesRangeTracker<ReleaseRecorderT> RangeTracker(Recorder);
663*76559068SAndroid Build Coastguard Worker if (SameBlockCountPerPage) {
664*76559068SAndroid Build Coastguard Worker // Fast path, every page has the same number of chunks affecting it.
665*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumberOfRegions; I++) {
666*76559068SAndroid Build Coastguard Worker if (SkipRegion(I)) {
667*76559068SAndroid Build Coastguard Worker RangeTracker.skipPages(PagesCount);
668*76559068SAndroid Build Coastguard Worker continue;
669*76559068SAndroid Build Coastguard Worker }
670*76559068SAndroid Build Coastguard Worker for (uptr J = 0; J < PagesCount; J++) {
671*76559068SAndroid Build Coastguard Worker const bool CanRelease =
672*76559068SAndroid Build Coastguard Worker PageMap.updateAsAllCountedIf(I, J, FullPagesBlockCountMax);
673*76559068SAndroid Build Coastguard Worker RangeTracker.processNextPage(CanRelease);
674*76559068SAndroid Build Coastguard Worker }
675*76559068SAndroid Build Coastguard Worker }
676*76559068SAndroid Build Coastguard Worker } else {
677*76559068SAndroid Build Coastguard Worker // Slow path, go through the pages keeping count how many chunks affect
678*76559068SAndroid Build Coastguard Worker // each page.
679*76559068SAndroid Build Coastguard Worker const uptr Pn = BlockSize < PageSize ? PageSize / BlockSize : 1;
680*76559068SAndroid Build Coastguard Worker const uptr Pnc = Pn * BlockSize;
681*76559068SAndroid Build Coastguard Worker // The idea is to increment the current page pointer by the first chunk
682*76559068SAndroid Build Coastguard Worker // size, middle portion size (the portion of the page covered by chunks
683*76559068SAndroid Build Coastguard Worker // except the first and the last one) and then the last chunk size, adding
684*76559068SAndroid Build Coastguard Worker // up the number of chunks on the current page and checking on every step
685*76559068SAndroid Build Coastguard Worker // whether the page boundary was crossed.
686*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumberOfRegions; I++) {
687*76559068SAndroid Build Coastguard Worker if (SkipRegion(I)) {
688*76559068SAndroid Build Coastguard Worker RangeTracker.skipPages(PagesCount);
689*76559068SAndroid Build Coastguard Worker continue;
690*76559068SAndroid Build Coastguard Worker }
691*76559068SAndroid Build Coastguard Worker uptr PrevPageBoundary = 0;
692*76559068SAndroid Build Coastguard Worker uptr CurrentBoundary = 0;
693*76559068SAndroid Build Coastguard Worker if (ReleasePageOffset > 0) {
694*76559068SAndroid Build Coastguard Worker PrevPageBoundary = ReleasePageOffset << getPageSizeLogCached();
695*76559068SAndroid Build Coastguard Worker CurrentBoundary = roundUpSlow(PrevPageBoundary, BlockSize);
696*76559068SAndroid Build Coastguard Worker }
697*76559068SAndroid Build Coastguard Worker for (uptr J = 0; J < PagesCount; J++) {
698*76559068SAndroid Build Coastguard Worker const uptr PageBoundary = PrevPageBoundary + PageSize;
699*76559068SAndroid Build Coastguard Worker uptr BlocksPerPage = Pn;
700*76559068SAndroid Build Coastguard Worker if (CurrentBoundary < PageBoundary) {
701*76559068SAndroid Build Coastguard Worker if (CurrentBoundary > PrevPageBoundary)
702*76559068SAndroid Build Coastguard Worker BlocksPerPage++;
703*76559068SAndroid Build Coastguard Worker CurrentBoundary += Pnc;
704*76559068SAndroid Build Coastguard Worker if (CurrentBoundary < PageBoundary) {
705*76559068SAndroid Build Coastguard Worker BlocksPerPage++;
706*76559068SAndroid Build Coastguard Worker CurrentBoundary += BlockSize;
707*76559068SAndroid Build Coastguard Worker }
708*76559068SAndroid Build Coastguard Worker }
709*76559068SAndroid Build Coastguard Worker PrevPageBoundary = PageBoundary;
710*76559068SAndroid Build Coastguard Worker const bool CanRelease =
711*76559068SAndroid Build Coastguard Worker PageMap.updateAsAllCountedIf(I, J, BlocksPerPage);
712*76559068SAndroid Build Coastguard Worker RangeTracker.processNextPage(CanRelease);
713*76559068SAndroid Build Coastguard Worker }
714*76559068SAndroid Build Coastguard Worker }
715*76559068SAndroid Build Coastguard Worker }
716*76559068SAndroid Build Coastguard Worker RangeTracker.finish();
717*76559068SAndroid Build Coastguard Worker }
718*76559068SAndroid Build Coastguard Worker
719*76559068SAndroid Build Coastguard Worker } // namespace scudo
720*76559068SAndroid Build Coastguard Worker
721*76559068SAndroid Build Coastguard Worker #endif // SCUDO_RELEASE_H_
722