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