]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/scudo/scudo_allocator_secondary.h
Merge ^/head r317281 through r317502.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / scudo / scudo_allocator_secondary.h
1 //===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// Scudo Secondary Allocator.
11 /// This services allocation that are too large to be serviced by the Primary
12 /// Allocator. It is directly backed by the memory mapping functions of the
13 /// operating system.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #ifndef SCUDO_ALLOCATOR_SECONDARY_H_
18 #define SCUDO_ALLOCATOR_SECONDARY_H_
19
20 #ifndef SCUDO_ALLOCATOR_H_
21 # error "This file must be included inside scudo_allocator.h."
22 #endif
23
24 class ScudoLargeMmapAllocator {
25  public:
26
27   void Init(bool AllocatorMayReturnNull) {
28     PageSize = GetPageSizeCached();
29     atomic_store(&MayReturnNull, AllocatorMayReturnNull, memory_order_relaxed);
30   }
31
32   void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) {
33     // The Scudo frontend prevents us from allocating more than
34     // MaxAllowedMallocSize, so integer overflow checks would be superfluous.
35     uptr MapSize = Size + SecondaryHeaderSize;
36     MapSize = RoundUpTo(MapSize, PageSize);
37     // Account for 2 guard pages, one before and one after the chunk.
38     MapSize += 2 * PageSize;
39     // The size passed to the Secondary comprises the alignment, if large
40     // enough. Subtract it here to get the requested size, including header.
41     if (Alignment > MinAlignment)
42       Size -= Alignment;
43
44     uptr MapBeg = reinterpret_cast<uptr>(MmapNoAccess(MapSize));
45     if (MapBeg == ~static_cast<uptr>(0))
46       return ReturnNullOrDieOnOOM();
47     // A page-aligned pointer is assumed after that, so check it now.
48     CHECK(IsAligned(MapBeg, PageSize));
49     uptr MapEnd = MapBeg + MapSize;
50     // The beginning of the user area for that allocation comes after the
51     // initial guard page, and both headers. This is the pointer that has to
52     // abide by alignment requirements.
53     uptr UserBeg = MapBeg + PageSize + HeadersSize;
54
55     // In the rare event of larger alignments, we will attempt to fit the mmap
56     // area better and unmap extraneous memory. This will also ensure that the
57     // offset and unused bytes field of the header stay small.
58     if (Alignment > MinAlignment) {
59       if (UserBeg & (Alignment - 1))
60         UserBeg += Alignment - (UserBeg & (Alignment - 1));
61       CHECK_GE(UserBeg, MapBeg);
62       uptr NewMapBeg = RoundDownTo(UserBeg - HeadersSize, PageSize) - PageSize;
63       CHECK_GE(NewMapBeg, MapBeg);
64       uptr NewMapEnd = RoundUpTo(UserBeg + (Size - AlignedChunkHeaderSize),
65                                  PageSize) + PageSize;
66       CHECK_LE(NewMapEnd, MapEnd);
67       // Unmap the extra memory if it's large enough, on both sides.
68       uptr Diff = NewMapBeg - MapBeg;
69       if (Diff > PageSize)
70         UnmapOrDie(reinterpret_cast<void *>(MapBeg), Diff);
71       Diff = MapEnd - NewMapEnd;
72       if (Diff > PageSize)
73         UnmapOrDie(reinterpret_cast<void *>(NewMapEnd), Diff);
74       MapBeg = NewMapBeg;
75       MapEnd = NewMapEnd;
76       MapSize = NewMapEnd - NewMapBeg;
77     }
78
79     uptr UserEnd = UserBeg + (Size - AlignedChunkHeaderSize);
80     CHECK_LE(UserEnd, MapEnd - PageSize);
81     // Actually mmap the memory, preserving the guard pages on either side.
82     CHECK_EQ(MapBeg + PageSize, reinterpret_cast<uptr>(
83         MmapFixedOrDie(MapBeg + PageSize, MapSize - 2 * PageSize)));
84     uptr Ptr = UserBeg - AlignedChunkHeaderSize;
85     SecondaryHeader *Header = getHeader(Ptr);
86     Header->MapBeg = MapBeg;
87     Header->MapSize = MapSize;
88     // The primary adds the whole class size to the stats when allocating a
89     // chunk, so we will do something similar here. But we will not account for
90     // the guard pages.
91     {
92       SpinMutexLock l(&StatsMutex);
93       Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize);
94       Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize);
95     }
96
97     return reinterpret_cast<void *>(UserBeg);
98   }
99
100   void *ReturnNullOrDieOnBadRequest() {
101     if (atomic_load(&MayReturnNull, memory_order_acquire))
102       return nullptr;
103     ReportAllocatorCannotReturnNull(false);
104   }
105
106   void *ReturnNullOrDieOnOOM() {
107     if (atomic_load(&MayReturnNull, memory_order_acquire))
108       return nullptr;
109     ReportAllocatorCannotReturnNull(true);
110   }
111
112   void SetMayReturnNull(bool AllocatorMayReturnNull) {
113     atomic_store(&MayReturnNull, AllocatorMayReturnNull, memory_order_release);
114   }
115
116   void Deallocate(AllocatorStats *Stats, void *Ptr) {
117     SecondaryHeader *Header = getHeader(Ptr);
118     {
119       SpinMutexLock l(&StatsMutex);
120       Stats->Sub(AllocatorStatAllocated, Header->MapSize - 2 * PageSize);
121       Stats->Sub(AllocatorStatMapped, Header->MapSize - 2 * PageSize);
122     }
123     UnmapOrDie(reinterpret_cast<void *>(Header->MapBeg), Header->MapSize);
124   }
125
126   uptr TotalMemoryUsed() {
127     UNIMPLEMENTED();
128   }
129
130   bool PointerIsMine(const void *Ptr) {
131     UNIMPLEMENTED();
132   }
133
134   uptr GetActuallyAllocatedSize(void *Ptr) {
135     SecondaryHeader *Header = getHeader(Ptr);
136     // Deduct PageSize as MapSize includes the trailing guard page.
137     uptr MapEnd = Header->MapBeg + Header->MapSize - PageSize;
138     return MapEnd - reinterpret_cast<uptr>(Ptr);
139   }
140
141   void *GetMetaData(const void *Ptr) {
142     UNIMPLEMENTED();
143   }
144
145   void *GetBlockBegin(const void *Ptr) {
146     UNIMPLEMENTED();
147   }
148
149   void *GetBlockBeginFastLocked(void *Ptr) {
150     UNIMPLEMENTED();
151   }
152
153   void PrintStats() {
154     UNIMPLEMENTED();
155   }
156
157   void ForceLock() {
158     UNIMPLEMENTED();
159   }
160
161   void ForceUnlock() {
162     UNIMPLEMENTED();
163   }
164
165   void ForEachChunk(ForEachChunkCallback Callback, void *Arg) {
166     UNIMPLEMENTED();
167   }
168
169  private:
170   // A Secondary allocated chunk header contains the base of the mapping and
171   // its size. Currently, the base is always a page before the header, but
172   // we might want to extend that number in the future based on the size of
173   // the allocation.
174   struct SecondaryHeader {
175     uptr MapBeg;
176     uptr MapSize;
177   };
178   // Check that sizeof(SecondaryHeader) is a multiple of MinAlignment.
179   COMPILER_CHECK((sizeof(SecondaryHeader) & (MinAlignment - 1)) == 0);
180
181   SecondaryHeader *getHeader(uptr Ptr) {
182     return reinterpret_cast<SecondaryHeader*>(Ptr - sizeof(SecondaryHeader));
183   }
184   SecondaryHeader *getHeader(const void *Ptr) {
185     return getHeader(reinterpret_cast<uptr>(Ptr));
186   }
187
188   const uptr SecondaryHeaderSize = sizeof(SecondaryHeader);
189   const uptr HeadersSize = SecondaryHeaderSize + AlignedChunkHeaderSize;
190   uptr PageSize;
191   SpinMutex StatsMutex;
192   atomic_uint8_t MayReturnNull;
193 };
194
195 #endif  // SCUDO_ALLOCATOR_SECONDARY_H_