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 5#include "src/heap/cppgc/page-memory.h" 6 7#include "src/base/macros.h" 8#include "src/base/sanitizer/asan.h" 9#include "src/heap/cppgc/platform.h" 10 11namespace cppgc { 12namespace internal { 13 14namespace { 15 16void Unprotect(PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler, 17 const PageMemory& page_memory) { 18 if (SupportsCommittingGuardPages(allocator)) { 19 if (!allocator.SetPermissions(page_memory.writeable_region().base(), 20 page_memory.writeable_region().size(), 21 PageAllocator::Permission::kReadWrite)) { 22 oom_handler("Oilpan: Unprotecting memory."); 23 } 24 } else { 25 // No protection in case the allocator cannot commit at the required 26 // granularity. Only protect if the allocator supports committing at that 27 // granularity. 28 // 29 // The allocator needs to support committing the overall range. 30 CHECK_EQ(0u, 31 page_memory.overall_region().size() % allocator.CommitPageSize()); 32 if (!allocator.SetPermissions(page_memory.overall_region().base(), 33 page_memory.overall_region().size(), 34 PageAllocator::Permission::kReadWrite)) { 35 oom_handler("Oilpan: Unprotecting memory."); 36 } 37 } 38} 39 40void Protect(PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler, 41 const PageMemory& page_memory) { 42 if (SupportsCommittingGuardPages(allocator)) { 43 // Swap the same region, providing the OS with a chance for fast lookup and 44 // change. 45 if (!allocator.SetPermissions(page_memory.writeable_region().base(), 46 page_memory.writeable_region().size(), 47 PageAllocator::Permission::kNoAccess)) { 48 oom_handler("Oilpan: Protecting memory."); 49 } 50 } else { 51 // See Unprotect(). 52 CHECK_EQ(0u, 53 page_memory.overall_region().size() % allocator.CommitPageSize()); 54 if (!allocator.SetPermissions(page_memory.overall_region().base(), 55 page_memory.overall_region().size(), 56 PageAllocator::Permission::kNoAccess)) { 57 oom_handler("Oilpan: Protecting memory."); 58 } 59 } 60} 61 62MemoryRegion ReserveMemoryRegion(PageAllocator& allocator, 63 FatalOutOfMemoryHandler& oom_handler, 64 size_t allocation_size) { 65 void* region_memory = 66 allocator.AllocatePages(nullptr, allocation_size, kPageSize, 67 PageAllocator::Permission::kNoAccess); 68 if (!region_memory) { 69 oom_handler("Oilpan: Reserving memory."); 70 } 71 const MemoryRegion reserved_region(static_cast<Address>(region_memory), 72 allocation_size); 73 DCHECK_EQ(reserved_region.base() + allocation_size, reserved_region.end()); 74 return reserved_region; 75} 76 77void FreeMemoryRegion(PageAllocator& allocator, 78 const MemoryRegion& reserved_region) { 79 // Make sure pages returned to OS are unpoisoned. 80 ASAN_UNPOISON_MEMORY_REGION(reserved_region.base(), reserved_region.size()); 81 allocator.FreePages(reserved_region.base(), reserved_region.size()); 82} 83 84} // namespace 85 86PageMemoryRegion::PageMemoryRegion(PageAllocator& allocator, 87 FatalOutOfMemoryHandler& oom_handler, 88 MemoryRegion reserved_region, bool is_large) 89 : allocator_(allocator), 90 oom_handler_(oom_handler), 91 reserved_region_(reserved_region), 92 is_large_(is_large) {} 93 94PageMemoryRegion::~PageMemoryRegion() { 95 FreeMemoryRegion(allocator_, reserved_region()); 96} 97 98// static 99constexpr size_t NormalPageMemoryRegion::kNumPageRegions; 100 101NormalPageMemoryRegion::NormalPageMemoryRegion( 102 PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler) 103 : PageMemoryRegion( 104 allocator, oom_handler, 105 ReserveMemoryRegion(allocator, oom_handler, 106 RoundUp(kPageSize * kNumPageRegions, 107 allocator.AllocatePageSize())), 108 false) { 109#ifdef DEBUG 110 for (size_t i = 0; i < kNumPageRegions; ++i) { 111 DCHECK_EQ(false, page_memories_in_use_[i]); 112 } 113#endif // DEBUG 114} 115 116NormalPageMemoryRegion::~NormalPageMemoryRegion() = default; 117 118void NormalPageMemoryRegion::Allocate(Address writeable_base) { 119 const size_t index = GetIndex(writeable_base); 120 ChangeUsed(index, true); 121 Unprotect(allocator_, oom_handler_, GetPageMemory(index)); 122} 123 124void NormalPageMemoryRegion::Free(Address writeable_base) { 125 const size_t index = GetIndex(writeable_base); 126 ChangeUsed(index, false); 127 Protect(allocator_, oom_handler_, GetPageMemory(index)); 128} 129 130void NormalPageMemoryRegion::UnprotectForTesting() { 131 for (size_t i = 0; i < kNumPageRegions; ++i) { 132 Unprotect(allocator_, oom_handler_, GetPageMemory(i)); 133 } 134} 135 136LargePageMemoryRegion::LargePageMemoryRegion( 137 PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler, 138 size_t length) 139 : PageMemoryRegion( 140 allocator, oom_handler, 141 ReserveMemoryRegion(allocator, oom_handler, 142 RoundUp(length + 2 * kGuardPageSize, 143 allocator.AllocatePageSize())), 144 true) {} 145 146LargePageMemoryRegion::~LargePageMemoryRegion() = default; 147 148void LargePageMemoryRegion::UnprotectForTesting() { 149 Unprotect(allocator_, oom_handler_, GetPageMemory()); 150} 151 152PageMemoryRegionTree::PageMemoryRegionTree() = default; 153 154PageMemoryRegionTree::~PageMemoryRegionTree() = default; 155 156void PageMemoryRegionTree::Add(PageMemoryRegion* region) { 157 DCHECK(region); 158 auto result = set_.emplace(region->reserved_region().base(), region); 159 USE(result); 160 DCHECK(result.second); 161} 162 163void PageMemoryRegionTree::Remove(PageMemoryRegion* region) { 164 DCHECK(region); 165 auto size = set_.erase(region->reserved_region().base()); 166 USE(size); 167 DCHECK_EQ(1u, size); 168} 169 170NormalPageMemoryPool::NormalPageMemoryPool() = default; 171 172NormalPageMemoryPool::~NormalPageMemoryPool() = default; 173 174void NormalPageMemoryPool::Add(size_t bucket, NormalPageMemoryRegion* pmr, 175 Address writeable_base) { 176 DCHECK_LT(bucket, kNumPoolBuckets); 177 pool_[bucket].push_back(std::make_pair(pmr, writeable_base)); 178} 179 180std::pair<NormalPageMemoryRegion*, Address> NormalPageMemoryPool::Take( 181 size_t bucket) { 182 DCHECK_LT(bucket, kNumPoolBuckets); 183 if (pool_[bucket].empty()) return {nullptr, nullptr}; 184 std::pair<NormalPageMemoryRegion*, Address> pair = pool_[bucket].back(); 185 pool_[bucket].pop_back(); 186 return pair; 187} 188 189PageBackend::PageBackend(PageAllocator& allocator, 190 FatalOutOfMemoryHandler& oom_handler) 191 : allocator_(allocator), oom_handler_(oom_handler) {} 192 193PageBackend::~PageBackend() = default; 194 195Address PageBackend::AllocateNormalPageMemory(size_t bucket) { 196 v8::base::MutexGuard guard(&mutex_); 197 std::pair<NormalPageMemoryRegion*, Address> result = page_pool_.Take(bucket); 198 if (!result.first) { 199 auto pmr = 200 std::make_unique<NormalPageMemoryRegion>(allocator_, oom_handler_); 201 for (size_t i = 0; i < NormalPageMemoryRegion::kNumPageRegions; ++i) { 202 page_pool_.Add(bucket, pmr.get(), 203 pmr->GetPageMemory(i).writeable_region().base()); 204 } 205 page_memory_region_tree_.Add(pmr.get()); 206 normal_page_memory_regions_.push_back(std::move(pmr)); 207 result = page_pool_.Take(bucket); 208 DCHECK(result.first); 209 } 210 result.first->Allocate(result.second); 211 return result.second; 212} 213 214void PageBackend::FreeNormalPageMemory(size_t bucket, Address writeable_base) { 215 v8::base::MutexGuard guard(&mutex_); 216 auto* pmr = static_cast<NormalPageMemoryRegion*>( 217 page_memory_region_tree_.Lookup(writeable_base)); 218 pmr->Free(writeable_base); 219 page_pool_.Add(bucket, pmr, writeable_base); 220} 221 222Address PageBackend::AllocateLargePageMemory(size_t size) { 223 v8::base::MutexGuard guard(&mutex_); 224 auto pmr = 225 std::make_unique<LargePageMemoryRegion>(allocator_, oom_handler_, size); 226 const PageMemory pm = pmr->GetPageMemory(); 227 Unprotect(allocator_, oom_handler_, pm); 228 page_memory_region_tree_.Add(pmr.get()); 229 large_page_memory_regions_.insert(std::make_pair(pmr.get(), std::move(pmr))); 230 return pm.writeable_region().base(); 231} 232 233void PageBackend::FreeLargePageMemory(Address writeable_base) { 234 v8::base::MutexGuard guard(&mutex_); 235 PageMemoryRegion* pmr = page_memory_region_tree_.Lookup(writeable_base); 236 page_memory_region_tree_.Remove(pmr); 237 auto size = large_page_memory_regions_.erase(pmr); 238 USE(size); 239 DCHECK_EQ(1u, size); 240} 241 242} // namespace internal 243} // namespace cppgc 244