• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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/base/emulated-virtual-address-subspace.h"
6 
7 #include "src/base/bits.h"
8 #include "src/base/platform/platform.h"
9 #include "src/base/platform/wrappers.h"
10 
11 namespace v8 {
12 namespace base {
13 
EmulatedVirtualAddressSubspace(VirtualAddressSpace * parent_space,Address base,size_t mapped_size,size_t total_size)14 EmulatedVirtualAddressSubspace::EmulatedVirtualAddressSubspace(
15     VirtualAddressSpace* parent_space, Address base, size_t mapped_size,
16     size_t total_size)
17     : VirtualAddressSpace(parent_space->page_size(),
18                           parent_space->allocation_granularity(), base,
19                           total_size, parent_space->max_page_permissions()),
20       mapped_size_(mapped_size),
21       parent_space_(parent_space),
22       region_allocator_(base, mapped_size, parent_space_->page_size()) {
23   // For simplicity, we currently require both the mapped and total size to be
24   // a power of two. This simplifies some things later on, for example, random
25   // addresses can be generated with a simply bitmask, and will then be inside
26   // the unmapped space with a probability >= 50% (mapped size == unmapped
27   // size) or never (mapped size == total size).
28   DCHECK(base::bits::IsPowerOfTwo(mapped_size));
29   DCHECK(base::bits::IsPowerOfTwo(total_size));
30 }
31 
~EmulatedVirtualAddressSubspace()32 EmulatedVirtualAddressSubspace::~EmulatedVirtualAddressSubspace() {
33   parent_space_->FreePages(base(), mapped_size_);
34 }
35 
SetRandomSeed(int64_t seed)36 void EmulatedVirtualAddressSubspace::SetRandomSeed(int64_t seed) {
37   MutexGuard guard(&mutex_);
38   rng_.SetSeed(seed);
39 }
40 
RandomPageAddress()41 Address EmulatedVirtualAddressSubspace::RandomPageAddress() {
42   MutexGuard guard(&mutex_);
43   Address addr = base() + (static_cast<uint64_t>(rng_.NextInt64()) % size());
44   return RoundDown(addr, allocation_granularity());
45 }
46 
AllocatePages(Address hint,size_t size,size_t alignment,PagePermissions permissions)47 Address EmulatedVirtualAddressSubspace::AllocatePages(
48     Address hint, size_t size, size_t alignment, PagePermissions permissions) {
49   if (hint == kNoHint || MappedRegionContains(hint, size)) {
50     MutexGuard guard(&mutex_);
51 
52     // Attempt to find a region in the mapped region.
53     Address address = region_allocator_.AllocateRegion(hint, size, alignment);
54     if (address != RegionAllocator::kAllocationFailure) {
55       // Success. Only need to adjust the page permissions.
56       if (parent_space_->SetPagePermissions(address, size, permissions)) {
57         return address;
58       }
59       // Probably ran out of memory, but still try to allocate in the unmapped
60       // space.
61       CHECK_EQ(size, region_allocator_.FreeRegion(address));
62     }
63   }
64 
65   // No luck or hint is outside of the mapped region. Try to allocate pages in
66   // the unmapped space using page allocation hints instead.
67   if (!IsUsableSizeForUnmappedRegion(size)) return kNullAddress;
68 
69   static constexpr int kMaxAttempts = 10;
70   for (int i = 0; i < kMaxAttempts; i++) {
71     // If an unmapped region exists, it must cover at least 50% of the whole
72     // space (unmapped + mapped region). Since we limit the size of allocation
73     // to 50% of the unmapped region (see IsUsableSizeForUnmappedRegion), a
74     // random page address has at least a 25% chance of being a usable base. As
75     // such, this loop should usually terminate quickly.
76     DCHECK_GE(unmapped_size(), mapped_size());
77     while (!UnmappedRegionContains(hint, size)) {
78       hint = RandomPageAddress();
79     }
80     hint = RoundDown(hint, alignment);
81 
82     const Address result =
83         parent_space_->AllocatePages(hint, size, alignment, permissions);
84     if (UnmappedRegionContains(result, size)) {
85       return result;
86     } else if (result) {
87       parent_space_->FreePages(result, size);
88     }
89 
90     // Retry at a different address.
91     hint = RandomPageAddress();
92   }
93 
94   return kNullAddress;
95 }
96 
FreePages(Address address,size_t size)97 void EmulatedVirtualAddressSubspace::FreePages(Address address, size_t size) {
98   if (MappedRegionContains(address, size)) {
99     MutexGuard guard(&mutex_);
100     CHECK_EQ(size, region_allocator_.FreeRegion(address));
101     CHECK(parent_space_->DecommitPages(address, size));
102   } else {
103     DCHECK(UnmappedRegionContains(address, size));
104     parent_space_->FreePages(address, size);
105   }
106 }
107 
AllocateSharedPages(Address hint,size_t size,PagePermissions permissions,PlatformSharedMemoryHandle handle,uint64_t offset)108 Address EmulatedVirtualAddressSubspace::AllocateSharedPages(
109     Address hint, size_t size, PagePermissions permissions,
110     PlatformSharedMemoryHandle handle, uint64_t offset) {
111   // Can only allocate shared pages in the unmapped region.
112   if (!IsUsableSizeForUnmappedRegion(size)) return kNullAddress;
113 
114   static constexpr int kMaxAttempts = 10;
115   for (int i = 0; i < kMaxAttempts; i++) {
116     // See AllocatePages() for why this loop usually terminates quickly.
117     DCHECK_GE(unmapped_size(), mapped_size());
118     while (!UnmappedRegionContains(hint, size)) {
119       hint = RandomPageAddress();
120     }
121 
122     Address region = parent_space_->AllocateSharedPages(hint, size, permissions,
123                                                         handle, offset);
124     if (UnmappedRegionContains(region, size)) {
125       return region;
126     } else if (region) {
127       parent_space_->FreeSharedPages(region, size);
128     }
129 
130     hint = RandomPageAddress();
131   }
132 
133   return kNullAddress;
134 }
135 
FreeSharedPages(Address address,size_t size)136 void EmulatedVirtualAddressSubspace::FreeSharedPages(Address address,
137                                                      size_t size) {
138   DCHECK(UnmappedRegionContains(address, size));
139   parent_space_->FreeSharedPages(address, size);
140 }
141 
SetPagePermissions(Address address,size_t size,PagePermissions permissions)142 bool EmulatedVirtualAddressSubspace::SetPagePermissions(
143     Address address, size_t size, PagePermissions permissions) {
144   DCHECK(Contains(address, size));
145   return parent_space_->SetPagePermissions(address, size, permissions);
146 }
147 
AllocateGuardRegion(Address address,size_t size)148 bool EmulatedVirtualAddressSubspace::AllocateGuardRegion(Address address,
149                                                          size_t size) {
150   if (MappedRegionContains(address, size)) {
151     MutexGuard guard(&mutex_);
152     return region_allocator_.AllocateRegionAt(address, size);
153   }
154   if (!UnmappedRegionContains(address, size)) return false;
155   return parent_space_->AllocateGuardRegion(address, size);
156 }
157 
FreeGuardRegion(Address address,size_t size)158 void EmulatedVirtualAddressSubspace::FreeGuardRegion(Address address,
159                                                      size_t size) {
160   if (MappedRegionContains(address, size)) {
161     MutexGuard guard(&mutex_);
162     CHECK_EQ(size, region_allocator_.FreeRegion(address));
163   } else {
164     DCHECK(UnmappedRegionContains(address, size));
165     parent_space_->FreeGuardRegion(address, size);
166   }
167 }
168 
CanAllocateSubspaces()169 bool EmulatedVirtualAddressSubspace::CanAllocateSubspaces() {
170   // This is not supported, mostly because it's not (yet) needed in practice.
171   return false;
172 }
173 
174 std::unique_ptr<v8::VirtualAddressSpace>
AllocateSubspace(Address hint,size_t size,size_t alignment,PagePermissions max_page_permissions)175 EmulatedVirtualAddressSubspace::AllocateSubspace(
176     Address hint, size_t size, size_t alignment,
177     PagePermissions max_page_permissions) {
178   UNREACHABLE();
179 }
180 
DiscardSystemPages(Address address,size_t size)181 bool EmulatedVirtualAddressSubspace::DiscardSystemPages(Address address,
182                                                         size_t size) {
183   DCHECK(Contains(address, size));
184   return parent_space_->DiscardSystemPages(address, size);
185 }
186 
DecommitPages(Address address,size_t size)187 bool EmulatedVirtualAddressSubspace::DecommitPages(Address address,
188                                                    size_t size) {
189   DCHECK(Contains(address, size));
190   return parent_space_->DecommitPages(address, size);
191 }
192 
193 }  // namespace base
194 }  // namespace v8
195