13cab2bb3Spatrick //===-- release.h -----------------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick
93cab2bb3Spatrick #ifndef SCUDO_RELEASE_H_
103cab2bb3Spatrick #define SCUDO_RELEASE_H_
113cab2bb3Spatrick
123cab2bb3Spatrick #include "common.h"
133cab2bb3Spatrick #include "list.h"
141f9cb04fSpatrick #include "mutex.h"
153cab2bb3Spatrick
163cab2bb3Spatrick namespace scudo {
173cab2bb3Spatrick
183cab2bb3Spatrick class ReleaseRecorder {
193cab2bb3Spatrick public:
20d89ec533Spatrick ReleaseRecorder(uptr Base, MapPlatformData *Data = nullptr)
Base(Base)21d89ec533Spatrick : Base(Base), Data(Data) {}
223cab2bb3Spatrick
getReleasedRangesCount()233cab2bb3Spatrick uptr getReleasedRangesCount() const { return ReleasedRangesCount; }
243cab2bb3Spatrick
getReleasedBytes()253cab2bb3Spatrick uptr getReleasedBytes() const { return ReleasedBytes; }
263cab2bb3Spatrick
getBase()27d89ec533Spatrick uptr getBase() const { return Base; }
28d89ec533Spatrick
293cab2bb3Spatrick // Releases [From, To) range of pages back to OS.
releasePageRangeToOS(uptr From,uptr To)303cab2bb3Spatrick void releasePageRangeToOS(uptr From, uptr To) {
313cab2bb3Spatrick const uptr Size = To - From;
32d89ec533Spatrick releasePagesToOS(Base, From, Size, Data);
333cab2bb3Spatrick ReleasedRangesCount++;
343cab2bb3Spatrick ReleasedBytes += Size;
353cab2bb3Spatrick }
363cab2bb3Spatrick
373cab2bb3Spatrick private:
383cab2bb3Spatrick uptr ReleasedRangesCount = 0;
393cab2bb3Spatrick uptr ReleasedBytes = 0;
40d89ec533Spatrick uptr Base = 0;
413cab2bb3Spatrick MapPlatformData *Data = nullptr;
423cab2bb3Spatrick };
433cab2bb3Spatrick
44*810390e3Srobert // A Region page map is used to record the usage of pages in the regions. It
45*810390e3Srobert // implements a packed array of Counters. Each counter occupies 2^N bits, enough
46*810390e3Srobert // to store counter's MaxValue. Ctor will try to use a static buffer first, and
47*810390e3Srobert // if that fails (the buffer is too small or already locked), will allocate the
481f9cb04fSpatrick // required Buffer via map(). The caller is expected to check whether the
491f9cb04fSpatrick // initialization was successful by checking isAllocated() result. For
501f9cb04fSpatrick // performance sake, none of the accessors check the validity of the arguments,
511f9cb04fSpatrick // It is assumed that Index is always in [0, N) range and the value is not
521f9cb04fSpatrick // incremented past MaxValue.
53*810390e3Srobert class RegionPageMap {
543cab2bb3Spatrick public:
RegionPageMap()55*810390e3Srobert RegionPageMap()
56*810390e3Srobert : Regions(0),
57*810390e3Srobert NumCounters(0),
58*810390e3Srobert CounterSizeBitsLog(0),
59*810390e3Srobert CounterMask(0),
60*810390e3Srobert PackingRatioLog(0),
61*810390e3Srobert BitOffsetMask(0),
62*810390e3Srobert SizePerRegion(0),
63*810390e3Srobert BufferSize(0),
64*810390e3Srobert Buffer(nullptr) {}
RegionPageMap(uptr NumberOfRegions,uptr CountersPerRegion,uptr MaxValue)65*810390e3Srobert RegionPageMap(uptr NumberOfRegions, uptr CountersPerRegion, uptr MaxValue) {
66*810390e3Srobert reset(NumberOfRegions, CountersPerRegion, MaxValue);
67*810390e3Srobert }
~RegionPageMap()68*810390e3Srobert ~RegionPageMap() {
69*810390e3Srobert if (!isAllocated())
70*810390e3Srobert return;
71*810390e3Srobert if (Buffer == &StaticBuffer[0])
72*810390e3Srobert Mutex.unlock();
73*810390e3Srobert else
74*810390e3Srobert unmap(reinterpret_cast<void *>(Buffer),
75*810390e3Srobert roundUpTo(BufferSize, getPageSizeCached()));
76*810390e3Srobert Buffer = nullptr;
77*810390e3Srobert }
78*810390e3Srobert
reset(uptr NumberOfRegion,uptr CountersPerRegion,uptr MaxValue)79*810390e3Srobert void reset(uptr NumberOfRegion, uptr CountersPerRegion, uptr MaxValue) {
80*810390e3Srobert DCHECK_GT(NumberOfRegion, 0);
81*810390e3Srobert DCHECK_GT(CountersPerRegion, 0);
82d89ec533Spatrick DCHECK_GT(MaxValue, 0);
83*810390e3Srobert
84*810390e3Srobert Regions = NumberOfRegion;
85*810390e3Srobert NumCounters = CountersPerRegion;
86*810390e3Srobert
873cab2bb3Spatrick constexpr uptr MaxCounterBits = sizeof(*Buffer) * 8UL;
883cab2bb3Spatrick // Rounding counter storage size up to the power of two allows for using
893cab2bb3Spatrick // bit shifts calculating particular counter's Index and offset.
903cab2bb3Spatrick const uptr CounterSizeBits =
913cab2bb3Spatrick roundUpToPowerOfTwo(getMostSignificantSetBitIndex(MaxValue) + 1);
92d89ec533Spatrick DCHECK_LE(CounterSizeBits, MaxCounterBits);
933cab2bb3Spatrick CounterSizeBitsLog = getLog2(CounterSizeBits);
943cab2bb3Spatrick CounterMask = ~(static_cast<uptr>(0)) >> (MaxCounterBits - CounterSizeBits);
953cab2bb3Spatrick
963cab2bb3Spatrick const uptr PackingRatio = MaxCounterBits >> CounterSizeBitsLog;
97d89ec533Spatrick DCHECK_GT(PackingRatio, 0);
983cab2bb3Spatrick PackingRatioLog = getLog2(PackingRatio);
993cab2bb3Spatrick BitOffsetMask = PackingRatio - 1;
1003cab2bb3Spatrick
101d89ec533Spatrick SizePerRegion =
102d89ec533Spatrick roundUpTo(NumCounters, static_cast<uptr>(1U) << PackingRatioLog) >>
103d89ec533Spatrick PackingRatioLog;
104d89ec533Spatrick BufferSize = SizePerRegion * sizeof(*Buffer) * Regions;
105d89ec533Spatrick if (BufferSize <= (StaticBufferCount * sizeof(Buffer[0])) &&
106d89ec533Spatrick Mutex.tryLock()) {
1071f9cb04fSpatrick Buffer = &StaticBuffer[0];
1081f9cb04fSpatrick memset(Buffer, 0, BufferSize);
1091f9cb04fSpatrick } else {
110*810390e3Srobert // When using a heap-based buffer, precommit the pages backing the
111*810390e3Srobert // Vmar by passing |MAP_PRECOMMIT| flag. This allows an optimization
112*810390e3Srobert // where page fault exceptions are skipped as the allocated memory
113*810390e3Srobert // is accessed.
114*810390e3Srobert const uptr MmapFlags =
115*810390e3Srobert MAP_ALLOWNOMEM | (SCUDO_FUCHSIA ? MAP_PRECOMMIT : 0);
1163cab2bb3Spatrick Buffer = reinterpret_cast<uptr *>(
117d89ec533Spatrick map(nullptr, roundUpTo(BufferSize, getPageSizeCached()),
118*810390e3Srobert "scudo:counters", MmapFlags, &MapData));
1193cab2bb3Spatrick }
1201f9cb04fSpatrick }
1213cab2bb3Spatrick
isAllocated()1223cab2bb3Spatrick bool isAllocated() const { return !!Buffer; }
1233cab2bb3Spatrick
getCount()124d89ec533Spatrick uptr getCount() const { return NumCounters; }
1253cab2bb3Spatrick
get(uptr Region,uptr I)126d89ec533Spatrick uptr get(uptr Region, uptr I) const {
127d89ec533Spatrick DCHECK_LT(Region, Regions);
128d89ec533Spatrick DCHECK_LT(I, NumCounters);
1293cab2bb3Spatrick const uptr Index = I >> PackingRatioLog;
1303cab2bb3Spatrick const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
131d89ec533Spatrick return (Buffer[Region * SizePerRegion + Index] >> BitOffset) & CounterMask;
1323cab2bb3Spatrick }
1333cab2bb3Spatrick
inc(uptr Region,uptr I)134d89ec533Spatrick void inc(uptr Region, uptr I) const {
135d89ec533Spatrick DCHECK_LT(get(Region, I), CounterMask);
1363cab2bb3Spatrick const uptr Index = I >> PackingRatioLog;
1373cab2bb3Spatrick const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
1383cab2bb3Spatrick DCHECK_LT(BitOffset, SCUDO_WORDSIZE);
139*810390e3Srobert DCHECK_EQ(isAllCounted(Region, I), false);
140d89ec533Spatrick Buffer[Region * SizePerRegion + Index] += static_cast<uptr>(1U)
141d89ec533Spatrick << BitOffset;
1423cab2bb3Spatrick }
1433cab2bb3Spatrick
incRange(uptr Region,uptr From,uptr To)144d89ec533Spatrick void incRange(uptr Region, uptr From, uptr To) const {
1453cab2bb3Spatrick DCHECK_LE(From, To);
146d89ec533Spatrick const uptr Top = Min(To + 1, NumCounters);
1471f9cb04fSpatrick for (uptr I = From; I < Top; I++)
148d89ec533Spatrick inc(Region, I);
1493cab2bb3Spatrick }
1503cab2bb3Spatrick
151*810390e3Srobert // Set the counter to the max value. Note that the max number of blocks in a
152*810390e3Srobert // page may vary. To provide an easier way to tell if all the blocks are
153*810390e3Srobert // counted for different pages, set to the same max value to denote the
154*810390e3Srobert // all-counted status.
setAsAllCounted(uptr Region,uptr I)155*810390e3Srobert void setAsAllCounted(uptr Region, uptr I) const {
156*810390e3Srobert DCHECK_LE(get(Region, I), CounterMask);
157*810390e3Srobert const uptr Index = I >> PackingRatioLog;
158*810390e3Srobert const uptr BitOffset = (I & BitOffsetMask) << CounterSizeBitsLog;
159*810390e3Srobert DCHECK_LT(BitOffset, SCUDO_WORDSIZE);
160*810390e3Srobert Buffer[Region * SizePerRegion + Index] |= CounterMask << BitOffset;
161*810390e3Srobert }
isAllCounted(uptr Region,uptr I)162*810390e3Srobert bool isAllCounted(uptr Region, uptr I) const {
163*810390e3Srobert return get(Region, I) == CounterMask;
164*810390e3Srobert }
165*810390e3Srobert
getBufferSize()1663cab2bb3Spatrick uptr getBufferSize() const { return BufferSize; }
1673cab2bb3Spatrick
168d89ec533Spatrick static const uptr StaticBufferCount = 2048U;
169d89ec533Spatrick
1703cab2bb3Spatrick private:
171*810390e3Srobert uptr Regions;
172*810390e3Srobert uptr NumCounters;
1733cab2bb3Spatrick uptr CounterSizeBitsLog;
1743cab2bb3Spatrick uptr CounterMask;
1753cab2bb3Spatrick uptr PackingRatioLog;
1763cab2bb3Spatrick uptr BitOffsetMask;
1773cab2bb3Spatrick
178d89ec533Spatrick uptr SizePerRegion;
1793cab2bb3Spatrick uptr BufferSize;
1803cab2bb3Spatrick uptr *Buffer;
181*810390e3Srobert [[no_unique_address]] MapPlatformData MapData = {};
1821f9cb04fSpatrick
1831f9cb04fSpatrick static HybridMutex Mutex;
184d89ec533Spatrick static uptr StaticBuffer[StaticBufferCount];
1853cab2bb3Spatrick };
1863cab2bb3Spatrick
1873cab2bb3Spatrick template <class ReleaseRecorderT> class FreePagesRangeTracker {
1883cab2bb3Spatrick public:
FreePagesRangeTracker(ReleaseRecorderT & Recorder)189*810390e3Srobert explicit FreePagesRangeTracker(ReleaseRecorderT &Recorder)
1903cab2bb3Spatrick : Recorder(Recorder), PageSizeLog(getLog2(getPageSizeCached())) {}
1913cab2bb3Spatrick
processNextPage(bool Released)192*810390e3Srobert void processNextPage(bool Released) {
193*810390e3Srobert if (Released) {
1943cab2bb3Spatrick if (!InRange) {
1953cab2bb3Spatrick CurrentRangeStatePage = CurrentPage;
1963cab2bb3Spatrick InRange = true;
1973cab2bb3Spatrick }
1983cab2bb3Spatrick } else {
1993cab2bb3Spatrick closeOpenedRange();
2003cab2bb3Spatrick }
2013cab2bb3Spatrick CurrentPage++;
2023cab2bb3Spatrick }
2033cab2bb3Spatrick
skipPages(uptr N)204d89ec533Spatrick void skipPages(uptr N) {
205d89ec533Spatrick closeOpenedRange();
206d89ec533Spatrick CurrentPage += N;
207d89ec533Spatrick }
208d89ec533Spatrick
finish()2093cab2bb3Spatrick void finish() { closeOpenedRange(); }
2103cab2bb3Spatrick
2113cab2bb3Spatrick private:
closeOpenedRange()2123cab2bb3Spatrick void closeOpenedRange() {
2133cab2bb3Spatrick if (InRange) {
214*810390e3Srobert Recorder.releasePageRangeToOS((CurrentRangeStatePage << PageSizeLog),
2153cab2bb3Spatrick (CurrentPage << PageSizeLog));
2163cab2bb3Spatrick InRange = false;
2173cab2bb3Spatrick }
2183cab2bb3Spatrick }
2193cab2bb3Spatrick
220*810390e3Srobert ReleaseRecorderT &Recorder;
2213cab2bb3Spatrick const uptr PageSizeLog;
2223cab2bb3Spatrick bool InRange = false;
2233cab2bb3Spatrick uptr CurrentPage = 0;
2243cab2bb3Spatrick uptr CurrentRangeStatePage = 0;
2253cab2bb3Spatrick };
2263cab2bb3Spatrick
227*810390e3Srobert struct PageReleaseContext {
PageReleaseContextPageReleaseContext228*810390e3Srobert PageReleaseContext(uptr BlockSize, uptr RegionSize, uptr NumberOfRegions) :
229*810390e3Srobert BlockSize(BlockSize),
230*810390e3Srobert RegionSize(RegionSize),
231*810390e3Srobert NumberOfRegions(NumberOfRegions) {
232*810390e3Srobert PageSize = getPageSizeCached();
2333cab2bb3Spatrick if (BlockSize <= PageSize) {
2343cab2bb3Spatrick if (PageSize % BlockSize == 0) {
2353cab2bb3Spatrick // Same number of chunks per page, no cross overs.
2363cab2bb3Spatrick FullPagesBlockCountMax = PageSize / BlockSize;
2373cab2bb3Spatrick SameBlockCountPerPage = true;
2383cab2bb3Spatrick } else if (BlockSize % (PageSize % BlockSize) == 0) {
2393cab2bb3Spatrick // Some chunks are crossing page boundaries, which means that the page
2403cab2bb3Spatrick // contains one or two partial chunks, but all pages contain the same
2413cab2bb3Spatrick // number of chunks.
2423cab2bb3Spatrick FullPagesBlockCountMax = PageSize / BlockSize + 1;
2433cab2bb3Spatrick SameBlockCountPerPage = true;
2443cab2bb3Spatrick } else {
2453cab2bb3Spatrick // Some chunks are crossing page boundaries, which means that the page
2463cab2bb3Spatrick // contains one or two partial chunks.
2473cab2bb3Spatrick FullPagesBlockCountMax = PageSize / BlockSize + 2;
2483cab2bb3Spatrick SameBlockCountPerPage = false;
2493cab2bb3Spatrick }
2503cab2bb3Spatrick } else {
2513cab2bb3Spatrick if (BlockSize % PageSize == 0) {
2523cab2bb3Spatrick // One chunk covers multiple pages, no cross overs.
2533cab2bb3Spatrick FullPagesBlockCountMax = 1;
2543cab2bb3Spatrick SameBlockCountPerPage = true;
2553cab2bb3Spatrick } else {
2563cab2bb3Spatrick // One chunk covers multiple pages, Some chunks are crossing page
2573cab2bb3Spatrick // boundaries. Some pages contain one chunk, some contain two.
2583cab2bb3Spatrick FullPagesBlockCountMax = 2;
2593cab2bb3Spatrick SameBlockCountPerPage = false;
2603cab2bb3Spatrick }
2613cab2bb3Spatrick }
2623cab2bb3Spatrick
263*810390e3Srobert PagesCount = roundUpTo(RegionSize, PageSize) / PageSize;
264*810390e3Srobert PageSizeLog = getLog2(PageSize);
265*810390e3Srobert RoundedRegionSize = PagesCount << PageSizeLog;
266*810390e3Srobert RoundedSize = NumberOfRegions * RoundedRegionSize;
267*810390e3Srobert }
2683cab2bb3Spatrick
269*810390e3Srobert // PageMap is lazily allocated when markFreeBlocks() is invoked.
hasBlockMarkedPageReleaseContext270*810390e3Srobert bool hasBlockMarked() const {
271*810390e3Srobert return PageMap.isAllocated();
272*810390e3Srobert }
273*810390e3Srobert
ensurePageMapAllocatedPageReleaseContext274*810390e3Srobert void ensurePageMapAllocated() {
275*810390e3Srobert if (PageMap.isAllocated())
276*810390e3Srobert return;
277*810390e3Srobert PageMap.reset(NumberOfRegions, PagesCount, FullPagesBlockCountMax);
278*810390e3Srobert DCHECK(PageMap.isAllocated());
279*810390e3Srobert }
280*810390e3Srobert
281*810390e3Srobert template<class TransferBatchT, typename DecompactPtrT>
markFreeBlocksPageReleaseContext282*810390e3Srobert void markFreeBlocks(const IntrusiveList<TransferBatchT> &FreeList,
283*810390e3Srobert DecompactPtrT DecompactPtr, uptr Base) {
284*810390e3Srobert ensurePageMapAllocated();
2853cab2bb3Spatrick
2863cab2bb3Spatrick // Iterate over free chunks and count how many free chunks affect each
2873cab2bb3Spatrick // allocated page.
2883cab2bb3Spatrick if (BlockSize <= PageSize && PageSize % BlockSize == 0) {
2893cab2bb3Spatrick // Each chunk affects one page only.
2903cab2bb3Spatrick for (const auto &It : FreeList) {
291*810390e3Srobert for (u16 I = 0; I < It.getCount(); I++) {
292*810390e3Srobert const uptr P = DecompactPtr(It.get(I)) - Base;
293d89ec533Spatrick if (P >= RoundedSize)
294d89ec533Spatrick continue;
295d89ec533Spatrick const uptr RegionIndex = NumberOfRegions == 1U ? 0 : P / RegionSize;
296d89ec533Spatrick const uptr PInRegion = P - RegionIndex * RegionSize;
297*810390e3Srobert PageMap.inc(RegionIndex, PInRegion >> PageSizeLog);
2983cab2bb3Spatrick }
2993cab2bb3Spatrick }
3003cab2bb3Spatrick } else {
3013cab2bb3Spatrick // In all other cases chunks might affect more than one page.
302d89ec533Spatrick DCHECK_GE(RegionSize, BlockSize);
303*810390e3Srobert const uptr LastBlockInRegion =
304*810390e3Srobert ((RegionSize / BlockSize) - 1U) * BlockSize;
3053cab2bb3Spatrick for (const auto &It : FreeList) {
306*810390e3Srobert for (u16 I = 0; I < It.getCount(); I++) {
307*810390e3Srobert const uptr P = DecompactPtr(It.get(I)) - Base;
308d89ec533Spatrick if (P >= RoundedSize)
309d89ec533Spatrick continue;
310d89ec533Spatrick const uptr RegionIndex = NumberOfRegions == 1U ? 0 : P / RegionSize;
311d89ec533Spatrick uptr PInRegion = P - RegionIndex * RegionSize;
312*810390e3Srobert PageMap.incRange(RegionIndex, PInRegion >> PageSizeLog,
313d89ec533Spatrick (PInRegion + BlockSize - 1) >> PageSizeLog);
314d89ec533Spatrick // The last block in a region might straddle a page, so if it's
315d89ec533Spatrick // free, we mark the following "pretend" memory block(s) as free.
316d89ec533Spatrick if (PInRegion == LastBlockInRegion) {
317d89ec533Spatrick PInRegion += BlockSize;
318d89ec533Spatrick while (PInRegion < RoundedRegionSize) {
319*810390e3Srobert PageMap.incRange(RegionIndex, PInRegion >> PageSizeLog,
320d89ec533Spatrick (PInRegion + BlockSize - 1) >> PageSizeLog);
321d89ec533Spatrick PInRegion += BlockSize;
3223cab2bb3Spatrick }
3233cab2bb3Spatrick }
324d89ec533Spatrick }
325d89ec533Spatrick }
3263cab2bb3Spatrick }
327*810390e3Srobert }
328*810390e3Srobert
329*810390e3Srobert uptr BlockSize;
330*810390e3Srobert uptr RegionSize;
331*810390e3Srobert uptr NumberOfRegions;
332*810390e3Srobert uptr PageSize;
333*810390e3Srobert uptr PagesCount;
334*810390e3Srobert uptr PageSizeLog;
335*810390e3Srobert uptr RoundedRegionSize;
336*810390e3Srobert uptr RoundedSize;
337*810390e3Srobert uptr FullPagesBlockCountMax;
338*810390e3Srobert bool SameBlockCountPerPage;
339*810390e3Srobert RegionPageMap PageMap;
340*810390e3Srobert };
341*810390e3Srobert
342*810390e3Srobert // Try to release the page which doesn't have any in-used block, i.e., they are
343*810390e3Srobert // all free blocks. The `PageMap` will record the number of free blocks in each
344*810390e3Srobert // page.
345*810390e3Srobert template <class ReleaseRecorderT, typename SkipRegionT>
346*810390e3Srobert NOINLINE void
releaseFreeMemoryToOS(PageReleaseContext & Context,ReleaseRecorderT & Recorder,SkipRegionT SkipRegion)347*810390e3Srobert releaseFreeMemoryToOS(PageReleaseContext &Context,
348*810390e3Srobert ReleaseRecorderT &Recorder, SkipRegionT SkipRegion) {
349*810390e3Srobert const uptr PageSize = Context.PageSize;
350*810390e3Srobert const uptr BlockSize = Context.BlockSize;
351*810390e3Srobert const uptr PagesCount = Context.PagesCount;
352*810390e3Srobert const uptr NumberOfRegions = Context.NumberOfRegions;
353*810390e3Srobert const uptr FullPagesBlockCountMax = Context.FullPagesBlockCountMax;
354*810390e3Srobert const bool SameBlockCountPerPage = Context.SameBlockCountPerPage;
355*810390e3Srobert RegionPageMap &PageMap = Context.PageMap;
3563cab2bb3Spatrick
3573cab2bb3Spatrick // Iterate over pages detecting ranges of pages with chunk Counters equal
3583cab2bb3Spatrick // to the expected number of chunks for the particular page.
3593cab2bb3Spatrick FreePagesRangeTracker<ReleaseRecorderT> RangeTracker(Recorder);
3603cab2bb3Spatrick if (SameBlockCountPerPage) {
3613cab2bb3Spatrick // Fast path, every page has the same number of chunks affecting it.
362d89ec533Spatrick for (uptr I = 0; I < NumberOfRegions; I++) {
363d89ec533Spatrick if (SkipRegion(I)) {
364d89ec533Spatrick RangeTracker.skipPages(PagesCount);
365d89ec533Spatrick continue;
366d89ec533Spatrick }
367*810390e3Srobert for (uptr J = 0; J < PagesCount; J++) {
368*810390e3Srobert const bool CanRelease = PageMap.get(I, J) == FullPagesBlockCountMax;
369*810390e3Srobert if (CanRelease)
370*810390e3Srobert PageMap.setAsAllCounted(I, J);
371*810390e3Srobert RangeTracker.processNextPage(CanRelease);
372*810390e3Srobert }
373d89ec533Spatrick }
3743cab2bb3Spatrick } else {
3753cab2bb3Spatrick // Slow path, go through the pages keeping count how many chunks affect
3763cab2bb3Spatrick // each page.
3773cab2bb3Spatrick const uptr Pn = BlockSize < PageSize ? PageSize / BlockSize : 1;
3783cab2bb3Spatrick const uptr Pnc = Pn * BlockSize;
3793cab2bb3Spatrick // The idea is to increment the current page pointer by the first chunk
3803cab2bb3Spatrick // size, middle portion size (the portion of the page covered by chunks
3813cab2bb3Spatrick // except the first and the last one) and then the last chunk size, adding
3823cab2bb3Spatrick // up the number of chunks on the current page and checking on every step
3833cab2bb3Spatrick // whether the page boundary was crossed.
384d89ec533Spatrick for (uptr I = 0; I < NumberOfRegions; I++) {
385d89ec533Spatrick if (SkipRegion(I)) {
386d89ec533Spatrick RangeTracker.skipPages(PagesCount);
387d89ec533Spatrick continue;
388d89ec533Spatrick }
3893cab2bb3Spatrick uptr PrevPageBoundary = 0;
3903cab2bb3Spatrick uptr CurrentBoundary = 0;
391d89ec533Spatrick for (uptr J = 0; J < PagesCount; J++) {
3923cab2bb3Spatrick const uptr PageBoundary = PrevPageBoundary + PageSize;
3933cab2bb3Spatrick uptr BlocksPerPage = Pn;
3943cab2bb3Spatrick if (CurrentBoundary < PageBoundary) {
3953cab2bb3Spatrick if (CurrentBoundary > PrevPageBoundary)
3963cab2bb3Spatrick BlocksPerPage++;
3973cab2bb3Spatrick CurrentBoundary += Pnc;
3983cab2bb3Spatrick if (CurrentBoundary < PageBoundary) {
3993cab2bb3Spatrick BlocksPerPage++;
4003cab2bb3Spatrick CurrentBoundary += BlockSize;
4013cab2bb3Spatrick }
4023cab2bb3Spatrick }
4033cab2bb3Spatrick PrevPageBoundary = PageBoundary;
404*810390e3Srobert const bool CanRelease = PageMap.get(I, J) == BlocksPerPage;
405*810390e3Srobert if (CanRelease)
406*810390e3Srobert PageMap.setAsAllCounted(I, J);
407*810390e3Srobert RangeTracker.processNextPage(CanRelease);
408d89ec533Spatrick }
4093cab2bb3Spatrick }
4103cab2bb3Spatrick }
4113cab2bb3Spatrick RangeTracker.finish();
4123cab2bb3Spatrick }
4133cab2bb3Spatrick
414*810390e3Srobert // An overload releaseFreeMemoryToOS which doesn't require the page usage
415*810390e3Srobert // information after releasing.
416*810390e3Srobert template <class TransferBatchT, class ReleaseRecorderT, typename DecompactPtrT,
417*810390e3Srobert typename SkipRegionT>
418*810390e3Srobert NOINLINE void
releaseFreeMemoryToOS(const IntrusiveList<TransferBatchT> & FreeList,uptr RegionSize,uptr NumberOfRegions,uptr BlockSize,ReleaseRecorderT & Recorder,DecompactPtrT DecompactPtr,SkipRegionT SkipRegion)419*810390e3Srobert releaseFreeMemoryToOS(const IntrusiveList<TransferBatchT> &FreeList,
420*810390e3Srobert uptr RegionSize, uptr NumberOfRegions, uptr BlockSize,
421*810390e3Srobert ReleaseRecorderT &Recorder, DecompactPtrT DecompactPtr,
422*810390e3Srobert SkipRegionT SkipRegion) {
423*810390e3Srobert PageReleaseContext Context(BlockSize, RegionSize, NumberOfRegions);
424*810390e3Srobert Context.markFreeBlocks(FreeList, DecompactPtr, Recorder.getBase());
425*810390e3Srobert releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
426*810390e3Srobert }
427*810390e3Srobert
4283cab2bb3Spatrick } // namespace scudo
4293cab2bb3Spatrick
4303cab2bb3Spatrick #endif // SCUDO_RELEASE_H_
431