11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#include "src/heap/cppgc/page-memory.h" 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ci#include "src/base/macros.h" 81cb0ef41Sopenharmony_ci#include "src/base/sanitizer/asan.h" 91cb0ef41Sopenharmony_ci#include "src/heap/cppgc/platform.h" 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_cinamespace cppgc { 121cb0ef41Sopenharmony_cinamespace internal { 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_cinamespace { 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_civoid Unprotect(PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler, 171cb0ef41Sopenharmony_ci const PageMemory& page_memory) { 181cb0ef41Sopenharmony_ci if (SupportsCommittingGuardPages(allocator)) { 191cb0ef41Sopenharmony_ci if (!allocator.SetPermissions(page_memory.writeable_region().base(), 201cb0ef41Sopenharmony_ci page_memory.writeable_region().size(), 211cb0ef41Sopenharmony_ci PageAllocator::Permission::kReadWrite)) { 221cb0ef41Sopenharmony_ci oom_handler("Oilpan: Unprotecting memory."); 231cb0ef41Sopenharmony_ci } 241cb0ef41Sopenharmony_ci } else { 251cb0ef41Sopenharmony_ci // No protection in case the allocator cannot commit at the required 261cb0ef41Sopenharmony_ci // granularity. Only protect if the allocator supports committing at that 271cb0ef41Sopenharmony_ci // granularity. 281cb0ef41Sopenharmony_ci // 291cb0ef41Sopenharmony_ci // The allocator needs to support committing the overall range. 301cb0ef41Sopenharmony_ci CHECK_EQ(0u, 311cb0ef41Sopenharmony_ci page_memory.overall_region().size() % allocator.CommitPageSize()); 321cb0ef41Sopenharmony_ci if (!allocator.SetPermissions(page_memory.overall_region().base(), 331cb0ef41Sopenharmony_ci page_memory.overall_region().size(), 341cb0ef41Sopenharmony_ci PageAllocator::Permission::kReadWrite)) { 351cb0ef41Sopenharmony_ci oom_handler("Oilpan: Unprotecting memory."); 361cb0ef41Sopenharmony_ci } 371cb0ef41Sopenharmony_ci } 381cb0ef41Sopenharmony_ci} 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_civoid Protect(PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler, 411cb0ef41Sopenharmony_ci const PageMemory& page_memory) { 421cb0ef41Sopenharmony_ci if (SupportsCommittingGuardPages(allocator)) { 431cb0ef41Sopenharmony_ci // Swap the same region, providing the OS with a chance for fast lookup and 441cb0ef41Sopenharmony_ci // change. 451cb0ef41Sopenharmony_ci if (!allocator.SetPermissions(page_memory.writeable_region().base(), 461cb0ef41Sopenharmony_ci page_memory.writeable_region().size(), 471cb0ef41Sopenharmony_ci PageAllocator::Permission::kNoAccess)) { 481cb0ef41Sopenharmony_ci oom_handler("Oilpan: Protecting memory."); 491cb0ef41Sopenharmony_ci } 501cb0ef41Sopenharmony_ci } else { 511cb0ef41Sopenharmony_ci // See Unprotect(). 521cb0ef41Sopenharmony_ci CHECK_EQ(0u, 531cb0ef41Sopenharmony_ci page_memory.overall_region().size() % allocator.CommitPageSize()); 541cb0ef41Sopenharmony_ci if (!allocator.SetPermissions(page_memory.overall_region().base(), 551cb0ef41Sopenharmony_ci page_memory.overall_region().size(), 561cb0ef41Sopenharmony_ci PageAllocator::Permission::kNoAccess)) { 571cb0ef41Sopenharmony_ci oom_handler("Oilpan: Protecting memory."); 581cb0ef41Sopenharmony_ci } 591cb0ef41Sopenharmony_ci } 601cb0ef41Sopenharmony_ci} 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ciMemoryRegion ReserveMemoryRegion(PageAllocator& allocator, 631cb0ef41Sopenharmony_ci FatalOutOfMemoryHandler& oom_handler, 641cb0ef41Sopenharmony_ci size_t allocation_size) { 651cb0ef41Sopenharmony_ci void* region_memory = 661cb0ef41Sopenharmony_ci allocator.AllocatePages(nullptr, allocation_size, kPageSize, 671cb0ef41Sopenharmony_ci PageAllocator::Permission::kNoAccess); 681cb0ef41Sopenharmony_ci if (!region_memory) { 691cb0ef41Sopenharmony_ci oom_handler("Oilpan: Reserving memory."); 701cb0ef41Sopenharmony_ci } 711cb0ef41Sopenharmony_ci const MemoryRegion reserved_region(static_cast<Address>(region_memory), 721cb0ef41Sopenharmony_ci allocation_size); 731cb0ef41Sopenharmony_ci DCHECK_EQ(reserved_region.base() + allocation_size, reserved_region.end()); 741cb0ef41Sopenharmony_ci return reserved_region; 751cb0ef41Sopenharmony_ci} 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_civoid FreeMemoryRegion(PageAllocator& allocator, 781cb0ef41Sopenharmony_ci const MemoryRegion& reserved_region) { 791cb0ef41Sopenharmony_ci // Make sure pages returned to OS are unpoisoned. 801cb0ef41Sopenharmony_ci ASAN_UNPOISON_MEMORY_REGION(reserved_region.base(), reserved_region.size()); 811cb0ef41Sopenharmony_ci allocator.FreePages(reserved_region.base(), reserved_region.size()); 821cb0ef41Sopenharmony_ci} 831cb0ef41Sopenharmony_ci 841cb0ef41Sopenharmony_ci} // namespace 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ciPageMemoryRegion::PageMemoryRegion(PageAllocator& allocator, 871cb0ef41Sopenharmony_ci FatalOutOfMemoryHandler& oom_handler, 881cb0ef41Sopenharmony_ci MemoryRegion reserved_region, bool is_large) 891cb0ef41Sopenharmony_ci : allocator_(allocator), 901cb0ef41Sopenharmony_ci oom_handler_(oom_handler), 911cb0ef41Sopenharmony_ci reserved_region_(reserved_region), 921cb0ef41Sopenharmony_ci is_large_(is_large) {} 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ciPageMemoryRegion::~PageMemoryRegion() { 951cb0ef41Sopenharmony_ci FreeMemoryRegion(allocator_, reserved_region()); 961cb0ef41Sopenharmony_ci} 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci// static 991cb0ef41Sopenharmony_ciconstexpr size_t NormalPageMemoryRegion::kNumPageRegions; 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ciNormalPageMemoryRegion::NormalPageMemoryRegion( 1021cb0ef41Sopenharmony_ci PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler) 1031cb0ef41Sopenharmony_ci : PageMemoryRegion( 1041cb0ef41Sopenharmony_ci allocator, oom_handler, 1051cb0ef41Sopenharmony_ci ReserveMemoryRegion(allocator, oom_handler, 1061cb0ef41Sopenharmony_ci RoundUp(kPageSize * kNumPageRegions, 1071cb0ef41Sopenharmony_ci allocator.AllocatePageSize())), 1081cb0ef41Sopenharmony_ci false) { 1091cb0ef41Sopenharmony_ci#ifdef DEBUG 1101cb0ef41Sopenharmony_ci for (size_t i = 0; i < kNumPageRegions; ++i) { 1111cb0ef41Sopenharmony_ci DCHECK_EQ(false, page_memories_in_use_[i]); 1121cb0ef41Sopenharmony_ci } 1131cb0ef41Sopenharmony_ci#endif // DEBUG 1141cb0ef41Sopenharmony_ci} 1151cb0ef41Sopenharmony_ci 1161cb0ef41Sopenharmony_ciNormalPageMemoryRegion::~NormalPageMemoryRegion() = default; 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_civoid NormalPageMemoryRegion::Allocate(Address writeable_base) { 1191cb0ef41Sopenharmony_ci const size_t index = GetIndex(writeable_base); 1201cb0ef41Sopenharmony_ci ChangeUsed(index, true); 1211cb0ef41Sopenharmony_ci Unprotect(allocator_, oom_handler_, GetPageMemory(index)); 1221cb0ef41Sopenharmony_ci} 1231cb0ef41Sopenharmony_ci 1241cb0ef41Sopenharmony_civoid NormalPageMemoryRegion::Free(Address writeable_base) { 1251cb0ef41Sopenharmony_ci const size_t index = GetIndex(writeable_base); 1261cb0ef41Sopenharmony_ci ChangeUsed(index, false); 1271cb0ef41Sopenharmony_ci Protect(allocator_, oom_handler_, GetPageMemory(index)); 1281cb0ef41Sopenharmony_ci} 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_civoid NormalPageMemoryRegion::UnprotectForTesting() { 1311cb0ef41Sopenharmony_ci for (size_t i = 0; i < kNumPageRegions; ++i) { 1321cb0ef41Sopenharmony_ci Unprotect(allocator_, oom_handler_, GetPageMemory(i)); 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci} 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_ciLargePageMemoryRegion::LargePageMemoryRegion( 1371cb0ef41Sopenharmony_ci PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler, 1381cb0ef41Sopenharmony_ci size_t length) 1391cb0ef41Sopenharmony_ci : PageMemoryRegion( 1401cb0ef41Sopenharmony_ci allocator, oom_handler, 1411cb0ef41Sopenharmony_ci ReserveMemoryRegion(allocator, oom_handler, 1421cb0ef41Sopenharmony_ci RoundUp(length + 2 * kGuardPageSize, 1431cb0ef41Sopenharmony_ci allocator.AllocatePageSize())), 1441cb0ef41Sopenharmony_ci true) {} 1451cb0ef41Sopenharmony_ci 1461cb0ef41Sopenharmony_ciLargePageMemoryRegion::~LargePageMemoryRegion() = default; 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_civoid LargePageMemoryRegion::UnprotectForTesting() { 1491cb0ef41Sopenharmony_ci Unprotect(allocator_, oom_handler_, GetPageMemory()); 1501cb0ef41Sopenharmony_ci} 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ciPageMemoryRegionTree::PageMemoryRegionTree() = default; 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ciPageMemoryRegionTree::~PageMemoryRegionTree() = default; 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_civoid PageMemoryRegionTree::Add(PageMemoryRegion* region) { 1571cb0ef41Sopenharmony_ci DCHECK(region); 1581cb0ef41Sopenharmony_ci auto result = set_.emplace(region->reserved_region().base(), region); 1591cb0ef41Sopenharmony_ci USE(result); 1601cb0ef41Sopenharmony_ci DCHECK(result.second); 1611cb0ef41Sopenharmony_ci} 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_civoid PageMemoryRegionTree::Remove(PageMemoryRegion* region) { 1641cb0ef41Sopenharmony_ci DCHECK(region); 1651cb0ef41Sopenharmony_ci auto size = set_.erase(region->reserved_region().base()); 1661cb0ef41Sopenharmony_ci USE(size); 1671cb0ef41Sopenharmony_ci DCHECK_EQ(1u, size); 1681cb0ef41Sopenharmony_ci} 1691cb0ef41Sopenharmony_ci 1701cb0ef41Sopenharmony_ciNormalPageMemoryPool::NormalPageMemoryPool() = default; 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ciNormalPageMemoryPool::~NormalPageMemoryPool() = default; 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_civoid NormalPageMemoryPool::Add(size_t bucket, NormalPageMemoryRegion* pmr, 1751cb0ef41Sopenharmony_ci Address writeable_base) { 1761cb0ef41Sopenharmony_ci DCHECK_LT(bucket, kNumPoolBuckets); 1771cb0ef41Sopenharmony_ci pool_[bucket].push_back(std::make_pair(pmr, writeable_base)); 1781cb0ef41Sopenharmony_ci} 1791cb0ef41Sopenharmony_ci 1801cb0ef41Sopenharmony_cistd::pair<NormalPageMemoryRegion*, Address> NormalPageMemoryPool::Take( 1811cb0ef41Sopenharmony_ci size_t bucket) { 1821cb0ef41Sopenharmony_ci DCHECK_LT(bucket, kNumPoolBuckets); 1831cb0ef41Sopenharmony_ci if (pool_[bucket].empty()) return {nullptr, nullptr}; 1841cb0ef41Sopenharmony_ci std::pair<NormalPageMemoryRegion*, Address> pair = pool_[bucket].back(); 1851cb0ef41Sopenharmony_ci pool_[bucket].pop_back(); 1861cb0ef41Sopenharmony_ci return pair; 1871cb0ef41Sopenharmony_ci} 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ciPageBackend::PageBackend(PageAllocator& allocator, 1901cb0ef41Sopenharmony_ci FatalOutOfMemoryHandler& oom_handler) 1911cb0ef41Sopenharmony_ci : allocator_(allocator), oom_handler_(oom_handler) {} 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ciPageBackend::~PageBackend() = default; 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ciAddress PageBackend::AllocateNormalPageMemory(size_t bucket) { 1961cb0ef41Sopenharmony_ci v8::base::MutexGuard guard(&mutex_); 1971cb0ef41Sopenharmony_ci std::pair<NormalPageMemoryRegion*, Address> result = page_pool_.Take(bucket); 1981cb0ef41Sopenharmony_ci if (!result.first) { 1991cb0ef41Sopenharmony_ci auto pmr = 2001cb0ef41Sopenharmony_ci std::make_unique<NormalPageMemoryRegion>(allocator_, oom_handler_); 2011cb0ef41Sopenharmony_ci for (size_t i = 0; i < NormalPageMemoryRegion::kNumPageRegions; ++i) { 2021cb0ef41Sopenharmony_ci page_pool_.Add(bucket, pmr.get(), 2031cb0ef41Sopenharmony_ci pmr->GetPageMemory(i).writeable_region().base()); 2041cb0ef41Sopenharmony_ci } 2051cb0ef41Sopenharmony_ci page_memory_region_tree_.Add(pmr.get()); 2061cb0ef41Sopenharmony_ci normal_page_memory_regions_.push_back(std::move(pmr)); 2071cb0ef41Sopenharmony_ci result = page_pool_.Take(bucket); 2081cb0ef41Sopenharmony_ci DCHECK(result.first); 2091cb0ef41Sopenharmony_ci } 2101cb0ef41Sopenharmony_ci result.first->Allocate(result.second); 2111cb0ef41Sopenharmony_ci return result.second; 2121cb0ef41Sopenharmony_ci} 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_civoid PageBackend::FreeNormalPageMemory(size_t bucket, Address writeable_base) { 2151cb0ef41Sopenharmony_ci v8::base::MutexGuard guard(&mutex_); 2161cb0ef41Sopenharmony_ci auto* pmr = static_cast<NormalPageMemoryRegion*>( 2171cb0ef41Sopenharmony_ci page_memory_region_tree_.Lookup(writeable_base)); 2181cb0ef41Sopenharmony_ci pmr->Free(writeable_base); 2191cb0ef41Sopenharmony_ci page_pool_.Add(bucket, pmr, writeable_base); 2201cb0ef41Sopenharmony_ci} 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ciAddress PageBackend::AllocateLargePageMemory(size_t size) { 2231cb0ef41Sopenharmony_ci v8::base::MutexGuard guard(&mutex_); 2241cb0ef41Sopenharmony_ci auto pmr = 2251cb0ef41Sopenharmony_ci std::make_unique<LargePageMemoryRegion>(allocator_, oom_handler_, size); 2261cb0ef41Sopenharmony_ci const PageMemory pm = pmr->GetPageMemory(); 2271cb0ef41Sopenharmony_ci Unprotect(allocator_, oom_handler_, pm); 2281cb0ef41Sopenharmony_ci page_memory_region_tree_.Add(pmr.get()); 2291cb0ef41Sopenharmony_ci large_page_memory_regions_.insert(std::make_pair(pmr.get(), std::move(pmr))); 2301cb0ef41Sopenharmony_ci return pm.writeable_region().base(); 2311cb0ef41Sopenharmony_ci} 2321cb0ef41Sopenharmony_ci 2331cb0ef41Sopenharmony_civoid PageBackend::FreeLargePageMemory(Address writeable_base) { 2341cb0ef41Sopenharmony_ci v8::base::MutexGuard guard(&mutex_); 2351cb0ef41Sopenharmony_ci PageMemoryRegion* pmr = page_memory_region_tree_.Lookup(writeable_base); 2361cb0ef41Sopenharmony_ci page_memory_region_tree_.Remove(pmr); 2371cb0ef41Sopenharmony_ci auto size = large_page_memory_regions_.erase(pmr); 2381cb0ef41Sopenharmony_ci USE(size); 2391cb0ef41Sopenharmony_ci DCHECK_EQ(1u, size); 2401cb0ef41Sopenharmony_ci} 2411cb0ef41Sopenharmony_ci 2421cb0ef41Sopenharmony_ci} // namespace internal 2431cb0ef41Sopenharmony_ci} // namespace cppgc 244