forked from LeenkxTeam/Kmake
200 lines
7.2 KiB
C++
200 lines
7.2 KiB
C++
|
|
// Copyright 2021 the V8 project authors. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "src/base/emulated-virtual-address-subspace.h"
|
||
|
|
|
||
|
|
#include "src/base/bits.h"
|
||
|
|
#include "src/base/platform/platform.h"
|
||
|
|
|
||
|
|
namespace v8 {
|
||
|
|
namespace base {
|
||
|
|
|
||
|
|
EmulatedVirtualAddressSubspace::EmulatedVirtualAddressSubspace(
|
||
|
|
VirtualAddressSpace* parent_space, Address base, size_t mapped_size,
|
||
|
|
size_t total_size)
|
||
|
|
: VirtualAddressSpace(parent_space->page_size(),
|
||
|
|
parent_space->allocation_granularity(), base,
|
||
|
|
total_size, parent_space->max_page_permissions()),
|
||
|
|
mapped_size_(mapped_size),
|
||
|
|
parent_space_(parent_space),
|
||
|
|
region_allocator_(base, mapped_size, parent_space_->page_size()) {
|
||
|
|
// For simplicity, we currently require both the mapped and total size to be
|
||
|
|
// a power of two. This simplifies some things later on, for example, random
|
||
|
|
// addresses can be generated with a simply bitmask, and will then be inside
|
||
|
|
// the unmapped space with a probability >= 50% (mapped size == unmapped
|
||
|
|
// size) or never (mapped size == total size).
|
||
|
|
DCHECK(base::bits::IsPowerOfTwo(mapped_size));
|
||
|
|
DCHECK(base::bits::IsPowerOfTwo(total_size));
|
||
|
|
}
|
||
|
|
|
||
|
|
EmulatedVirtualAddressSubspace::~EmulatedVirtualAddressSubspace() {
|
||
|
|
parent_space_->FreePages(base(), mapped_size_);
|
||
|
|
}
|
||
|
|
|
||
|
|
void EmulatedVirtualAddressSubspace::SetRandomSeed(int64_t seed) {
|
||
|
|
MutexGuard guard(&mutex_);
|
||
|
|
rng_.SetSeed(seed);
|
||
|
|
}
|
||
|
|
|
||
|
|
Address EmulatedVirtualAddressSubspace::RandomPageAddress() {
|
||
|
|
MutexGuard guard(&mutex_);
|
||
|
|
Address addr = base() + (static_cast<uint64_t>(rng_.NextInt64()) % size());
|
||
|
|
return RoundDown(addr, allocation_granularity());
|
||
|
|
}
|
||
|
|
|
||
|
|
Address EmulatedVirtualAddressSubspace::AllocatePages(
|
||
|
|
Address hint, size_t size, size_t alignment, PagePermissions permissions) {
|
||
|
|
if (hint == kNoHint || MappedRegionContains(hint, size)) {
|
||
|
|
MutexGuard guard(&mutex_);
|
||
|
|
|
||
|
|
// Attempt to find a region in the mapped region.
|
||
|
|
Address address = region_allocator_.AllocateRegion(hint, size, alignment);
|
||
|
|
if (address != RegionAllocator::kAllocationFailure) {
|
||
|
|
// Success. Only need to adjust the page permissions.
|
||
|
|
if (parent_space_->SetPagePermissions(address, size, permissions)) {
|
||
|
|
return address;
|
||
|
|
}
|
||
|
|
// Probably ran out of memory, but still try to allocate in the unmapped
|
||
|
|
// space.
|
||
|
|
CHECK_EQ(size, region_allocator_.FreeRegion(address));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// No luck or hint is outside of the mapped region. Try to allocate pages in
|
||
|
|
// the unmapped space using page allocation hints instead.
|
||
|
|
if (!IsUsableSizeForUnmappedRegion(size)) return kNullAddress;
|
||
|
|
|
||
|
|
static constexpr int kMaxAttempts = 10;
|
||
|
|
for (int i = 0; i < kMaxAttempts; i++) {
|
||
|
|
// If an unmapped region exists, it must cover at least 50% of the whole
|
||
|
|
// space (unmapped + mapped region). Since we limit the size of allocation
|
||
|
|
// to 50% of the unmapped region (see IsUsableSizeForUnmappedRegion), a
|
||
|
|
// random page address has at least a 25% chance of being a usable base. As
|
||
|
|
// such, this loop should usually terminate quickly.
|
||
|
|
DCHECK_GE(unmapped_size(), mapped_size());
|
||
|
|
while (!UnmappedRegionContains(hint, size)) {
|
||
|
|
hint = RandomPageAddress();
|
||
|
|
}
|
||
|
|
hint = RoundDown(hint, alignment);
|
||
|
|
|
||
|
|
const Address result =
|
||
|
|
parent_space_->AllocatePages(hint, size, alignment, permissions);
|
||
|
|
if (UnmappedRegionContains(result, size)) {
|
||
|
|
return result;
|
||
|
|
} else if (result) {
|
||
|
|
parent_space_->FreePages(result, size);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Retry at a different address.
|
||
|
|
hint = RandomPageAddress();
|
||
|
|
}
|
||
|
|
|
||
|
|
return kNullAddress;
|
||
|
|
}
|
||
|
|
|
||
|
|
void EmulatedVirtualAddressSubspace::FreePages(Address address, size_t size) {
|
||
|
|
if (MappedRegionContains(address, size)) {
|
||
|
|
MutexGuard guard(&mutex_);
|
||
|
|
CHECK_EQ(size, region_allocator_.FreeRegion(address));
|
||
|
|
CHECK(parent_space_->DecommitPages(address, size));
|
||
|
|
} else {
|
||
|
|
DCHECK(UnmappedRegionContains(address, size));
|
||
|
|
parent_space_->FreePages(address, size);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Address EmulatedVirtualAddressSubspace::AllocateSharedPages(
|
||
|
|
Address hint, size_t size, PagePermissions permissions,
|
||
|
|
PlatformSharedMemoryHandle handle, uint64_t offset) {
|
||
|
|
// Can only allocate shared pages in the unmapped region.
|
||
|
|
if (!IsUsableSizeForUnmappedRegion(size)) return kNullAddress;
|
||
|
|
|
||
|
|
static constexpr int kMaxAttempts = 10;
|
||
|
|
for (int i = 0; i < kMaxAttempts; i++) {
|
||
|
|
// See AllocatePages() for why this loop usually terminates quickly.
|
||
|
|
DCHECK_GE(unmapped_size(), mapped_size());
|
||
|
|
while (!UnmappedRegionContains(hint, size)) {
|
||
|
|
hint = RandomPageAddress();
|
||
|
|
}
|
||
|
|
|
||
|
|
Address region = parent_space_->AllocateSharedPages(hint, size, permissions,
|
||
|
|
handle, offset);
|
||
|
|
if (UnmappedRegionContains(region, size)) {
|
||
|
|
return region;
|
||
|
|
} else if (region) {
|
||
|
|
parent_space_->FreeSharedPages(region, size);
|
||
|
|
}
|
||
|
|
|
||
|
|
hint = RandomPageAddress();
|
||
|
|
}
|
||
|
|
|
||
|
|
return kNullAddress;
|
||
|
|
}
|
||
|
|
|
||
|
|
void EmulatedVirtualAddressSubspace::FreeSharedPages(Address address,
|
||
|
|
size_t size) {
|
||
|
|
DCHECK(UnmappedRegionContains(address, size));
|
||
|
|
parent_space_->FreeSharedPages(address, size);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EmulatedVirtualAddressSubspace::SetPagePermissions(
|
||
|
|
Address address, size_t size, PagePermissions permissions) {
|
||
|
|
DCHECK(Contains(address, size));
|
||
|
|
return parent_space_->SetPagePermissions(address, size, permissions);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EmulatedVirtualAddressSubspace::AllocateGuardRegion(Address address,
|
||
|
|
size_t size) {
|
||
|
|
if (MappedRegionContains(address, size)) {
|
||
|
|
MutexGuard guard(&mutex_);
|
||
|
|
return region_allocator_.AllocateRegionAt(address, size);
|
||
|
|
}
|
||
|
|
if (!UnmappedRegionContains(address, size)) return false;
|
||
|
|
return parent_space_->AllocateGuardRegion(address, size);
|
||
|
|
}
|
||
|
|
|
||
|
|
void EmulatedVirtualAddressSubspace::FreeGuardRegion(Address address,
|
||
|
|
size_t size) {
|
||
|
|
if (MappedRegionContains(address, size)) {
|
||
|
|
MutexGuard guard(&mutex_);
|
||
|
|
CHECK_EQ(size, region_allocator_.FreeRegion(address));
|
||
|
|
} else {
|
||
|
|
DCHECK(UnmappedRegionContains(address, size));
|
||
|
|
parent_space_->FreeGuardRegion(address, size);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EmulatedVirtualAddressSubspace::CanAllocateSubspaces() {
|
||
|
|
// This is not supported, mostly because it's not (yet) needed in practice.
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::unique_ptr<v8::VirtualAddressSpace>
|
||
|
|
EmulatedVirtualAddressSubspace::AllocateSubspace(
|
||
|
|
Address hint, size_t size, size_t alignment,
|
||
|
|
PagePermissions max_page_permissions) {
|
||
|
|
UNREACHABLE();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EmulatedVirtualAddressSubspace::RecommitPages(
|
||
|
|
Address address, size_t size, PagePermissions permissions) {
|
||
|
|
DCHECK(Contains(address, size));
|
||
|
|
return parent_space_->RecommitPages(address, size, permissions);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EmulatedVirtualAddressSubspace::DiscardSystemPages(Address address,
|
||
|
|
size_t size) {
|
||
|
|
DCHECK(Contains(address, size));
|
||
|
|
return parent_space_->DiscardSystemPages(address, size);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EmulatedVirtualAddressSubspace::DecommitPages(Address address,
|
||
|
|
size_t size) {
|
||
|
|
DCHECK(Contains(address, size));
|
||
|
|
return parent_space_->DecommitPages(address, size);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace base
|
||
|
|
} // namespace v8
|