Skip to content

Commit dfe8c16

Browse files
committed
[scudo] Reapply "Update secondary cache time-based release logic"
This reapplies commit e5271fe. In the event that MTE is turned on, in which case released cache entries may be inserted into the cache, all released cache entries are inserted after `LastUnreleasedEntry`. Additionally, when a cache entry is moved from `Quarantine` to `Entries`, its time field will only be updated if the cache entry's memory has not been released.
1 parent 12285cc commit dfe8c16

File tree

1 file changed

+57
-33
lines changed

1 file changed

+57
-33
lines changed

compiler-rt/lib/scudo/standalone/secondary.h

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ class MapAllocatorCache {
247247
// The cache is initially empty
248248
LRUHead = CachedBlock::InvalidEntry;
249249
LRUTail = CachedBlock::InvalidEntry;
250+
LastUnreleasedEntry = CachedBlock::InvalidEntry;
250251

251252
// Available entries will be retrieved starting from the beginning of the
252253
// Entries array
@@ -321,9 +322,11 @@ class MapAllocatorCache {
321322
}
322323
CachedBlock PrevEntry = Quarantine[QuarantinePos];
323324
Quarantine[QuarantinePos] = Entry;
324-
if (OldestTime == 0)
325-
OldestTime = Entry.Time;
326325
Entry = PrevEntry;
326+
// Update the entry time to reflect the time that the
327+
// quarantined memory is placed in the Entries array
328+
if (Entry.Time != 0)
329+
Entry.Time = Time;
327330
}
328331

329332
// All excess entries are evicted from the cache
@@ -334,16 +337,12 @@ class MapAllocatorCache {
334337
}
335338

336339
insert(Entry);
337-
338-
if (OldestTime == 0)
339-
OldestTime = Entry.Time;
340340
} while (0);
341341

342342
for (MemMapT &EvictMemMap : EvictionMemMaps)
343343
unmapCallBack(EvictMemMap);
344344

345345
if (Interval >= 0) {
346-
// TODO: Add ReleaseToOS logic to LRU algorithm
347346
releaseOlderThan(Time - static_cast<u64>(Interval) * 1000000);
348347
}
349348
}
@@ -523,22 +522,37 @@ class MapAllocatorCache {
523522
// Cache should be populated with valid entries when not empty
524523
DCHECK_NE(AvailableHead, CachedBlock::InvalidEntry);
525524

526-
u32 FreeIndex = AvailableHead;
525+
u16 FreeIndex = AvailableHead;
527526
AvailableHead = Entries[AvailableHead].Next;
527+
Entries[FreeIndex] = Entry;
528528

529-
if (EntriesCount == 0) {
530-
LRUTail = static_cast<u16>(FreeIndex);
529+
// Check list order
530+
if (EntriesCount > 1)
531+
DCHECK_GE(Entries[LRUHead].Time, Entries[Entries[LRUHead].Next].Time);
532+
533+
// Released entry goes after LastUnreleasedEntry rather than at LRUHead
534+
if (Entry.Time == 0 && LastUnreleasedEntry != CachedBlock::InvalidEntry) {
535+
Entries[FreeIndex].Next = Entries[LastUnreleasedEntry].Next;
536+
Entries[FreeIndex].Prev = LastUnreleasedEntry;
537+
Entries[LastUnreleasedEntry].Next = FreeIndex;
538+
if (LRUTail == LastUnreleasedEntry) {
539+
LRUTail = FreeIndex;
540+
} else {
541+
Entries[Entries[FreeIndex].Next].Prev = FreeIndex;
542+
}
531543
} else {
532-
// Check list order
533-
if (EntriesCount > 1)
534-
DCHECK_GE(Entries[LRUHead].Time, Entries[Entries[LRUHead].Next].Time);
535-
Entries[LRUHead].Prev = static_cast<u16>(FreeIndex);
544+
Entries[FreeIndex].Next = LRUHead;
545+
Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
546+
if (EntriesCount == 0) {
547+
LRUTail = FreeIndex;
548+
} else {
549+
Entries[LRUHead].Prev = FreeIndex;
550+
}
551+
LRUHead = FreeIndex;
552+
if (LastUnreleasedEntry == CachedBlock::InvalidEntry)
553+
LastUnreleasedEntry = FreeIndex;
536554
}
537555

538-
Entries[FreeIndex] = Entry;
539-
Entries[FreeIndex].Next = LRUHead;
540-
Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
541-
LRUHead = static_cast<u16>(FreeIndex);
542556
EntriesCount++;
543557

544558
// Availability stack should not have available entries when all entries
@@ -552,6 +566,9 @@ class MapAllocatorCache {
552566

553567
Entries[I].invalidate();
554568

569+
if (I == LastUnreleasedEntry)
570+
LastUnreleasedEntry = Entries[LastUnreleasedEntry].Prev;
571+
555572
if (I == LRUHead)
556573
LRUHead = Entries[I].Next;
557574
else
@@ -593,35 +610,39 @@ class MapAllocatorCache {
593610
}
594611
}
595612

596-
void releaseIfOlderThan(CachedBlock &Entry, u64 Time) REQUIRES(Mutex) {
597-
if (!Entry.isValid() || !Entry.Time)
598-
return;
599-
if (Entry.Time > Time) {
600-
if (OldestTime == 0 || Entry.Time < OldestTime)
601-
OldestTime = Entry.Time;
602-
return;
603-
}
613+
inline void release(CachedBlock &Entry) {
614+
DCHECK(Entry.Time != 0);
604615
Entry.MemMap.releaseAndZeroPagesToOS(Entry.CommitBase, Entry.CommitSize);
605616
Entry.Time = 0;
606617
}
607618

608619
void releaseOlderThan(u64 Time) EXCLUDES(Mutex) {
609620
ScopedLock L(Mutex);
610-
if (!EntriesCount || OldestTime == 0 || OldestTime > Time)
621+
if (!EntriesCount)
611622
return;
612-
OldestTime = 0;
613-
for (uptr I = 0; I < Config::getQuarantineSize(); I++)
614-
releaseIfOlderThan(Quarantine[I], Time);
615-
for (uptr I = 0; I < Config::getEntriesArraySize(); I++)
616-
releaseIfOlderThan(Entries[I], Time);
617-
}
618623

624+
// TODO: Add conditional to skip iteration over quarantine
625+
// if quarantine is disabled
626+
for (uptr I = 0; I < Config::getQuarantineSize(); I++) {
627+
CachedBlock &ReleaseEntry = Quarantine[I];
628+
if (!ReleaseEntry.isValid() || !ReleaseEntry.Time ||
629+
ReleaseEntry.Time > Time)
630+
continue;
631+
release(ReleaseEntry);
632+
}
633+
634+
// Release oldest entries first by releasing from decommit base
635+
while (LastUnreleasedEntry != CachedBlock::InvalidEntry &&
636+
Entries[LastUnreleasedEntry].Time <= Time) {
637+
release(Entries[LastUnreleasedEntry]);
638+
LastUnreleasedEntry = Entries[LastUnreleasedEntry].Prev;
639+
}
640+
}
619641
HybridMutex Mutex;
620642
u32 EntriesCount GUARDED_BY(Mutex) = 0;
621643
u32 QuarantinePos GUARDED_BY(Mutex) = 0;
622644
atomic_u32 MaxEntriesCount = {};
623645
atomic_uptr MaxEntrySize = {};
624-
u64 OldestTime GUARDED_BY(Mutex) = 0;
625646
atomic_s32 ReleaseToOsIntervalMs = {};
626647
u32 CallsToRetrieve GUARDED_BY(Mutex) = 0;
627648
u32 SuccessfulRetrieves GUARDED_BY(Mutex) = 0;
@@ -636,6 +657,9 @@ class MapAllocatorCache {
636657
u16 LRUTail GUARDED_BY(Mutex) = 0;
637658
// The AvailableHead is the top of the stack of available entries
638659
u16 AvailableHead GUARDED_BY(Mutex) = 0;
660+
// The LastUnreleasedEntry is the least recently used entry that has not
661+
// been released
662+
u16 LastUnreleasedEntry GUARDED_BY(Mutex) = 0;
639663
};
640664

641665
template <typename Config> class MapAllocator {

0 commit comments

Comments
 (0)