xref: /aosp_15_r20/external/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h (revision 7c3d14c8b49c529e04be81a3ce6f5cc23712e4c6)
1*7c3d14c8STreehugger Robot //===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===//
2*7c3d14c8STreehugger Robot //
3*7c3d14c8STreehugger Robot //                     The LLVM Compiler Infrastructure
4*7c3d14c8STreehugger Robot //
5*7c3d14c8STreehugger Robot // This file is distributed under the University of Illinois Open Source
6*7c3d14c8STreehugger Robot // License. See LICENSE.TXT for details.
7*7c3d14c8STreehugger Robot //
8*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
9*7c3d14c8STreehugger Robot //
10*7c3d14c8STreehugger Robot // Memory quarantine for AddressSanitizer and potentially other tools.
11*7c3d14c8STreehugger Robot // Quarantine caches some specified amount of memory in per-thread caches,
12*7c3d14c8STreehugger Robot // then evicts to global FIFO queue. When the queue reaches specified threshold,
13*7c3d14c8STreehugger Robot // oldest memory is recycled.
14*7c3d14c8STreehugger Robot //
15*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
16*7c3d14c8STreehugger Robot 
17*7c3d14c8STreehugger Robot #ifndef SANITIZER_QUARANTINE_H
18*7c3d14c8STreehugger Robot #define SANITIZER_QUARANTINE_H
19*7c3d14c8STreehugger Robot 
20*7c3d14c8STreehugger Robot #include "sanitizer_internal_defs.h"
21*7c3d14c8STreehugger Robot #include "sanitizer_mutex.h"
22*7c3d14c8STreehugger Robot #include "sanitizer_list.h"
23*7c3d14c8STreehugger Robot 
24*7c3d14c8STreehugger Robot namespace __sanitizer {
25*7c3d14c8STreehugger Robot 
26*7c3d14c8STreehugger Robot template<typename Node> class QuarantineCache;
27*7c3d14c8STreehugger Robot 
28*7c3d14c8STreehugger Robot struct QuarantineBatch {
29*7c3d14c8STreehugger Robot   static const uptr kSize = 1021;
30*7c3d14c8STreehugger Robot   QuarantineBatch *next;
31*7c3d14c8STreehugger Robot   uptr size;
32*7c3d14c8STreehugger Robot   uptr count;
33*7c3d14c8STreehugger Robot   void *batch[kSize];
34*7c3d14c8STreehugger Robot };
35*7c3d14c8STreehugger Robot 
36*7c3d14c8STreehugger Robot COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13));  // 8Kb.
37*7c3d14c8STreehugger Robot 
38*7c3d14c8STreehugger Robot // The callback interface is:
39*7c3d14c8STreehugger Robot // void Callback::Recycle(Node *ptr);
40*7c3d14c8STreehugger Robot // void *cb.Allocate(uptr size);
41*7c3d14c8STreehugger Robot // void cb.Deallocate(void *ptr);
42*7c3d14c8STreehugger Robot template<typename Callback, typename Node>
43*7c3d14c8STreehugger Robot class Quarantine {
44*7c3d14c8STreehugger Robot  public:
45*7c3d14c8STreehugger Robot   typedef QuarantineCache<Callback> Cache;
46*7c3d14c8STreehugger Robot 
Quarantine(LinkerInitialized)47*7c3d14c8STreehugger Robot   explicit Quarantine(LinkerInitialized)
48*7c3d14c8STreehugger Robot       : cache_(LINKER_INITIALIZED) {
49*7c3d14c8STreehugger Robot   }
50*7c3d14c8STreehugger Robot 
Init(uptr size,uptr cache_size)51*7c3d14c8STreehugger Robot   void Init(uptr size, uptr cache_size) {
52*7c3d14c8STreehugger Robot     atomic_store(&max_size_, size, memory_order_release);
53*7c3d14c8STreehugger Robot     atomic_store(&min_size_, size / 10 * 9,
54*7c3d14c8STreehugger Robot                  memory_order_release); // 90% of max size.
55*7c3d14c8STreehugger Robot     max_cache_size_ = cache_size;
56*7c3d14c8STreehugger Robot   }
57*7c3d14c8STreehugger Robot 
GetSize()58*7c3d14c8STreehugger Robot   uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
59*7c3d14c8STreehugger Robot 
Put(Cache * c,Callback cb,Node * ptr,uptr size)60*7c3d14c8STreehugger Robot   void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
61*7c3d14c8STreehugger Robot     c->Enqueue(cb, ptr, size);
62*7c3d14c8STreehugger Robot     if (c->Size() > max_cache_size_)
63*7c3d14c8STreehugger Robot       Drain(c, cb);
64*7c3d14c8STreehugger Robot   }
65*7c3d14c8STreehugger Robot 
Drain(Cache * c,Callback cb)66*7c3d14c8STreehugger Robot   void NOINLINE Drain(Cache *c, Callback cb) {
67*7c3d14c8STreehugger Robot     {
68*7c3d14c8STreehugger Robot       SpinMutexLock l(&cache_mutex_);
69*7c3d14c8STreehugger Robot       cache_.Transfer(c);
70*7c3d14c8STreehugger Robot     }
71*7c3d14c8STreehugger Robot     if (cache_.Size() > GetSize() && recycle_mutex_.TryLock())
72*7c3d14c8STreehugger Robot       Recycle(cb);
73*7c3d14c8STreehugger Robot   }
74*7c3d14c8STreehugger Robot 
75*7c3d14c8STreehugger Robot  private:
76*7c3d14c8STreehugger Robot   // Read-only data.
77*7c3d14c8STreehugger Robot   char pad0_[kCacheLineSize];
78*7c3d14c8STreehugger Robot   atomic_uintptr_t max_size_;
79*7c3d14c8STreehugger Robot   atomic_uintptr_t min_size_;
80*7c3d14c8STreehugger Robot   uptr max_cache_size_;
81*7c3d14c8STreehugger Robot   char pad1_[kCacheLineSize];
82*7c3d14c8STreehugger Robot   SpinMutex cache_mutex_;
83*7c3d14c8STreehugger Robot   SpinMutex recycle_mutex_;
84*7c3d14c8STreehugger Robot   Cache cache_;
85*7c3d14c8STreehugger Robot   char pad2_[kCacheLineSize];
86*7c3d14c8STreehugger Robot 
Recycle(Callback cb)87*7c3d14c8STreehugger Robot   void NOINLINE Recycle(Callback cb) {
88*7c3d14c8STreehugger Robot     Cache tmp;
89*7c3d14c8STreehugger Robot     uptr min_size = atomic_load(&min_size_, memory_order_acquire);
90*7c3d14c8STreehugger Robot     {
91*7c3d14c8STreehugger Robot       SpinMutexLock l(&cache_mutex_);
92*7c3d14c8STreehugger Robot       while (cache_.Size() > min_size) {
93*7c3d14c8STreehugger Robot         QuarantineBatch *b = cache_.DequeueBatch();
94*7c3d14c8STreehugger Robot         tmp.EnqueueBatch(b);
95*7c3d14c8STreehugger Robot       }
96*7c3d14c8STreehugger Robot     }
97*7c3d14c8STreehugger Robot     recycle_mutex_.Unlock();
98*7c3d14c8STreehugger Robot     DoRecycle(&tmp, cb);
99*7c3d14c8STreehugger Robot   }
100*7c3d14c8STreehugger Robot 
DoRecycle(Cache * c,Callback cb)101*7c3d14c8STreehugger Robot   void NOINLINE DoRecycle(Cache *c, Callback cb) {
102*7c3d14c8STreehugger Robot     while (QuarantineBatch *b = c->DequeueBatch()) {
103*7c3d14c8STreehugger Robot       const uptr kPrefetch = 16;
104*7c3d14c8STreehugger Robot       CHECK(kPrefetch <= ARRAY_SIZE(b->batch));
105*7c3d14c8STreehugger Robot       for (uptr i = 0; i < kPrefetch; i++)
106*7c3d14c8STreehugger Robot         PREFETCH(b->batch[i]);
107*7c3d14c8STreehugger Robot       for (uptr i = 0, count = b->count; i < count; i++) {
108*7c3d14c8STreehugger Robot         if (i + kPrefetch < count)
109*7c3d14c8STreehugger Robot           PREFETCH(b->batch[i + kPrefetch]);
110*7c3d14c8STreehugger Robot         cb.Recycle((Node*)b->batch[i]);
111*7c3d14c8STreehugger Robot       }
112*7c3d14c8STreehugger Robot       cb.Deallocate(b);
113*7c3d14c8STreehugger Robot     }
114*7c3d14c8STreehugger Robot   }
115*7c3d14c8STreehugger Robot };
116*7c3d14c8STreehugger Robot 
117*7c3d14c8STreehugger Robot // Per-thread cache of memory blocks.
118*7c3d14c8STreehugger Robot template<typename Callback>
119*7c3d14c8STreehugger Robot class QuarantineCache {
120*7c3d14c8STreehugger Robot  public:
QuarantineCache(LinkerInitialized)121*7c3d14c8STreehugger Robot   explicit QuarantineCache(LinkerInitialized) {
122*7c3d14c8STreehugger Robot   }
123*7c3d14c8STreehugger Robot 
QuarantineCache()124*7c3d14c8STreehugger Robot   QuarantineCache()
125*7c3d14c8STreehugger Robot       : size_() {
126*7c3d14c8STreehugger Robot     list_.clear();
127*7c3d14c8STreehugger Robot   }
128*7c3d14c8STreehugger Robot 
Size()129*7c3d14c8STreehugger Robot   uptr Size() const {
130*7c3d14c8STreehugger Robot     return atomic_load(&size_, memory_order_relaxed);
131*7c3d14c8STreehugger Robot   }
132*7c3d14c8STreehugger Robot 
Enqueue(Callback cb,void * ptr,uptr size)133*7c3d14c8STreehugger Robot   void Enqueue(Callback cb, void *ptr, uptr size) {
134*7c3d14c8STreehugger Robot     if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) {
135*7c3d14c8STreehugger Robot       AllocBatch(cb);
136*7c3d14c8STreehugger Robot       size += sizeof(QuarantineBatch);  // Count the batch in Quarantine size.
137*7c3d14c8STreehugger Robot     }
138*7c3d14c8STreehugger Robot     QuarantineBatch *b = list_.back();
139*7c3d14c8STreehugger Robot     CHECK(b);
140*7c3d14c8STreehugger Robot     b->batch[b->count++] = ptr;
141*7c3d14c8STreehugger Robot     b->size += size;
142*7c3d14c8STreehugger Robot     SizeAdd(size);
143*7c3d14c8STreehugger Robot   }
144*7c3d14c8STreehugger Robot 
Transfer(QuarantineCache * c)145*7c3d14c8STreehugger Robot   void Transfer(QuarantineCache *c) {
146*7c3d14c8STreehugger Robot     list_.append_back(&c->list_);
147*7c3d14c8STreehugger Robot     SizeAdd(c->Size());
148*7c3d14c8STreehugger Robot     atomic_store(&c->size_, 0, memory_order_relaxed);
149*7c3d14c8STreehugger Robot   }
150*7c3d14c8STreehugger Robot 
EnqueueBatch(QuarantineBatch * b)151*7c3d14c8STreehugger Robot   void EnqueueBatch(QuarantineBatch *b) {
152*7c3d14c8STreehugger Robot     list_.push_back(b);
153*7c3d14c8STreehugger Robot     SizeAdd(b->size);
154*7c3d14c8STreehugger Robot   }
155*7c3d14c8STreehugger Robot 
DequeueBatch()156*7c3d14c8STreehugger Robot   QuarantineBatch *DequeueBatch() {
157*7c3d14c8STreehugger Robot     if (list_.empty())
158*7c3d14c8STreehugger Robot       return nullptr;
159*7c3d14c8STreehugger Robot     QuarantineBatch *b = list_.front();
160*7c3d14c8STreehugger Robot     list_.pop_front();
161*7c3d14c8STreehugger Robot     SizeSub(b->size);
162*7c3d14c8STreehugger Robot     return b;
163*7c3d14c8STreehugger Robot   }
164*7c3d14c8STreehugger Robot 
165*7c3d14c8STreehugger Robot  private:
166*7c3d14c8STreehugger Robot   IntrusiveList<QuarantineBatch> list_;
167*7c3d14c8STreehugger Robot   atomic_uintptr_t size_;
168*7c3d14c8STreehugger Robot 
SizeAdd(uptr add)169*7c3d14c8STreehugger Robot   void SizeAdd(uptr add) {
170*7c3d14c8STreehugger Robot     atomic_store(&size_, Size() + add, memory_order_relaxed);
171*7c3d14c8STreehugger Robot   }
SizeSub(uptr sub)172*7c3d14c8STreehugger Robot   void SizeSub(uptr sub) {
173*7c3d14c8STreehugger Robot     atomic_store(&size_, Size() - sub, memory_order_relaxed);
174*7c3d14c8STreehugger Robot   }
175*7c3d14c8STreehugger Robot 
AllocBatch(Callback cb)176*7c3d14c8STreehugger Robot   NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
177*7c3d14c8STreehugger Robot     QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
178*7c3d14c8STreehugger Robot     CHECK(b);
179*7c3d14c8STreehugger Robot     b->count = 0;
180*7c3d14c8STreehugger Robot     b->size = 0;
181*7c3d14c8STreehugger Robot     list_.push_back(b);
182*7c3d14c8STreehugger Robot     return b;
183*7c3d14c8STreehugger Robot   }
184*7c3d14c8STreehugger Robot };
185*7c3d14c8STreehugger Robot } // namespace __sanitizer
186*7c3d14c8STreehugger Robot 
187*7c3d14c8STreehugger Robot #endif // SANITIZER_QUARANTINE_H
188