v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
page-memory.cc
Go to the documentation of this file.
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <algorithm>
8#include <cstddef>
9#include <optional>
10
11#include "include/v8config.h"
12#include "src/base/macros.h"
16
17#if V8_OS_POSIX
18#include <errno.h>
19#endif
20
21namespace cppgc {
22namespace internal {
23
24namespace {
25
26V8_WARN_UNUSED_RESULT bool TryUnprotect(PageAllocator& allocator,
27 const MemoryRegion& memory_region) {
28 // The allocator needs to support committing the overall range.
29 CHECK_EQ(0u, memory_region.size() % allocator.CommitPageSize());
30 return allocator.SetPermissions(memory_region.base(), memory_region.size(),
32}
33
34V8_WARN_UNUSED_RESULT bool TryDiscard(PageAllocator& allocator,
35 const MemoryRegion& memory_region) {
36 // See Unprotect().
37 CHECK_EQ(0u, memory_region.size() % allocator.CommitPageSize());
38 return allocator.DiscardSystemPages(memory_region.base(),
39 memory_region.size());
40}
41
42std::optional<MemoryRegion> ReserveMemoryRegion(PageAllocator& allocator,
43 size_t allocation_size) {
44 void* region_memory =
45 allocator.AllocatePages(nullptr, allocation_size, kPageSize,
47 if (!region_memory) {
48 return std::nullopt;
49 }
50 const MemoryRegion reserved_region(static_cast<Address>(region_memory),
51 allocation_size);
52 DCHECK_EQ(reserved_region.base() + allocation_size, reserved_region.end());
53 return reserved_region;
54}
55
56void FreeMemoryRegion(PageAllocator& allocator,
57 const MemoryRegion& reserved_region) {
58 // Make sure pages returned to OS are unpoisoned.
59 ASAN_UNPOISON_MEMORY_REGION(reserved_region.base(), reserved_region.size());
60 allocator.FreePages(reserved_region.base(), reserved_region.size());
61}
62
63std::unique_ptr<PageMemoryRegion> CreateNormalPageMemoryRegion(
64 PageAllocator& allocator) {
65 DCHECK_EQ(0u, kPageSize % allocator.AllocatePageSize());
66 const auto region = ReserveMemoryRegion(allocator, kPageSize);
67 if (!region) return {};
68 auto result = std::unique_ptr<PageMemoryRegion>(
69 new PageMemoryRegion(allocator, *region));
70 return result;
71}
72
73std::unique_ptr<PageMemoryRegion> CreateLargePageMemoryRegion(
74 PageAllocator& allocator, size_t length) {
75 const auto region = ReserveMemoryRegion(
76 allocator, RoundUp(length, allocator.AllocatePageSize()));
77 if (!region) return {};
78 auto result = std::unique_ptr<PageMemoryRegion>(
79 new PageMemoryRegion(allocator, *region));
80 return result;
81}
82
83} // namespace
84
86 MemoryRegion reserved_region)
87 : allocator_(allocator), reserved_region_(reserved_region) {}
88
90 FreeMemoryRegion(allocator_, region());
91}
92
96
98
100
102 DCHECK(region);
103 const auto result = set_.emplace(region->region().base(), region);
104 USE(result);
105 DCHECK(result.second);
106}
107
109 DCHECK(region);
110 const auto size = set_.erase(region->region().base());
111 USE(size);
112 DCHECK_EQ(1u, size);
113}
114
116 DCHECK_NOT_NULL(pmr);
117 DCHECK_EQ(pmr->region().size(), kPageSize);
118 // Oilpan requires the pages to be zero-initialized.
119 {
120 void* base = pmr->region().base();
121 const size_t size = pmr->region().size();
122 AsanUnpoisonScope unpoison_for_memset(base, size);
123 std::memset(base, 0, size);
124 }
125 pool_.emplace_back(PooledPageMemoryRegion(pmr));
126}
127
129 if (pool_.empty()) return nullptr;
130 PooledPageMemoryRegion entry = pool_.back();
131 DCHECK_NOT_NULL(entry.region);
132 pool_.pop_back();
133 void* base = entry.region->region().base();
134 const size_t size = entry.region->region().size();
136
138 if (entry.is_decommitted) {
139 // Also need to make the pages accessible.
142 bool ok = entry.region->allocator().SetPermissions(
144 if (!ok) {
145#if V8_OS_POSIX
146 // Changing permissions can return ENOMEM in several cases, including
147 // (since there is PROT_WRITE) when it would exceed the RLIMIT_DATA
148 // resource limit, at least on Linux. Check errno in this case, and
149 // declare that this is an OOM in this case.
150 if (errno == ENOMEM) {
151 GetGlobalOOMHandler()("Cannot change page permissions");
152 }
153#endif
154 CHECK(false);
155 }
156 }
157#if DEBUG
158 CheckMemoryIsZero(base, size);
159#endif
160 return entry.region;
161}
162
164 size_t total_size = 0;
165 for (auto& entry : pool_) {
166 if (entry.is_decommitted || entry.is_discarded) {
167 continue;
168 }
169 total_size += entry.region->region().size();
170 }
171 return total_size;
172}
173
175 for (auto& entry : pool_) {
176 DCHECK_NOT_NULL(entry.region);
177 void* base = entry.region->region().base();
178 size_t size = entry.region->region().size();
179 // Unpoison the memory before giving back to the OS.
182 if (entry.is_decommitted) {
183 continue;
184 }
185 CHECK(page_allocator.DecommitPages(base, size));
186 entry.is_decommitted = true;
187 } else {
188 if (entry.is_discarded) {
189 continue;
190 }
191 CHECK(TryDiscard(page_allocator, entry.region->region()));
192 entry.is_discarded = true;
193 }
194 }
195}
196
198 PageAllocator& large_page_allocator)
199 : normal_page_allocator_(normal_page_allocator),
200 large_page_allocator_(large_page_allocator) {}
201
202PageBackend::~PageBackend() = default;
203
206 if (PageMemoryRegion* cached = page_pool_.Take()) {
207 const auto region = cached->region();
209 normal_page_memory_regions_.find(cached));
211 return region.base();
212 }
213 auto pmr = CreateNormalPageMemoryRegion(normal_page_allocator_);
214 if (!pmr) {
215 return nullptr;
216 }
217 const auto memory_region = pmr->region();
218 if (V8_LIKELY(TryUnprotect(normal_page_allocator_, memory_region))) {
219 page_memory_region_tree_.Add(pmr.get());
220 normal_page_memory_regions_.emplace(pmr.get(), std::move(pmr));
221 return memory_region.base();
222 }
223 return nullptr;
224}
225
228 auto* pmr = page_memory_region_tree_.Lookup(writeable_base);
229 DCHECK_NOT_NULL(pmr);
231 page_pool_.Add(pmr);
232}
233
236 auto pmr = CreateLargePageMemoryRegion(large_page_allocator_, size);
237 if (!pmr) {
238 return nullptr;
239 }
240 const auto memory_region = pmr->region();
241 if (V8_LIKELY(TryUnprotect(large_page_allocator_, memory_region))) {
242 page_memory_region_tree_.Add(pmr.get());
243 large_page_memory_regions_.emplace(pmr.get(), std::move(pmr));
244 return memory_region.base();
245 }
246 return nullptr;
247}
248
251 PageMemoryRegion* pmr = page_memory_region_tree_.Lookup(writeable_base);
253 auto size = large_page_memory_regions_.erase(pmr);
254 USE(size);
255 DCHECK_EQ(1u, size);
256}
257
261
262} // namespace internal
263} // namespace cppgc
#define ASAN_UNPOISON_MEMORY_REGION(start, size)
Definition asan.h:71
RegisterAllocator * allocator_
std::vector< PooledPageMemoryRegion > pool_
void ReleasePooledPages(PageAllocator &allocator)
PageAllocator & normal_page_allocator_
void FreeNormalPageMemory(Address writeable_base)
std::unordered_map< PageMemoryRegion *, std::unique_ptr< PageMemoryRegion > > normal_page_memory_regions_
void FreeLargePageMemory(Address writeable_base)
PageMemoryRegionTree page_memory_region_tree_
Address TryAllocateLargePageMemory(size_t size)
std::unordered_map< PageMemoryRegion *, std::unique_ptr< PageMemoryRegion > > large_page_memory_regions_
PageBackend(PageAllocator &normal_page_allocator, PageAllocator &large_page_allocator)
PageAllocator & large_page_allocator_
NormalPageMemoryPool page_pool_
std::map< ConstAddress, PageMemoryRegion * > set_
Definition page-memory.h:91
void Remove(PageMemoryRegion *)
PageMemoryRegion * Lookup(ConstAddress) const
PageAllocator & allocator() const
Definition page-memory.h:62
PageMemoryRegion(PageAllocator &, MemoryRegion)
const MemoryRegion region() const
Definition page-memory.h:53
virtual bool DecommitPages(void *address, size_t size)=0
virtual bool RecommitPages(void *address, size_t length, Permission permissions)
virtual bool SetPermissions(void *address, size_t length, Permission permissions)=0
ZoneVector< RpoNumber > & result
V8_INLINE void CheckMemoryIsZero(const void *address, size_t size)
Definition memory.h:37
constexpr size_t kPageSize
Definition globals.h:42
uint8_t * Address
Definition globals.h:17
FatalOutOfMemoryHandler & GetGlobalOOMHandler()
Definition platform.cc:73
v8::PageAllocator PageAllocator
Definition platform.h:22
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
constexpr T RoundUp(T x, intptr_t m)
Definition macros.h:387
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671