1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_
7
8 #include <bitset>
9 #include <limits>
10
11 #include "base/allocator/partition_allocator/address_pool_manager_types.h"
12 #include "base/allocator/partition_allocator/partition_address_space.h"
13 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
14 #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
15 #include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h"
16 #include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h"
17 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
18 #include "base/allocator/partition_allocator/partition_alloc_check.h"
19 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
20 #include "base/allocator/partition_allocator/partition_lock.h"
21 #include "build/build_config.h"
22
23 #if !BUILDFLAG(HAS_64_BIT_POINTERS)
24 #include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
25 #endif
26
27 namespace partition_alloc {
28
29 class AddressSpaceStatsDumper;
30 struct AddressSpaceStats;
31 struct PoolStats;
32
33 } // namespace partition_alloc
34
35 namespace partition_alloc::internal {
36
37 // (64bit version)
38 // AddressPoolManager takes a reserved virtual address space and manages address
39 // space allocation.
40 //
41 // AddressPoolManager (currently) supports up to 4 pools. Each pool manages a
42 // contiguous reserved address space. Alloc() takes a pool_handle and returns
43 // address regions from the specified pool. Free() also takes a pool_handle and
44 // returns the address region back to the manager.
45 //
46 // (32bit version)
47 // AddressPoolManager wraps AllocPages and FreePages and remembers allocated
48 // address regions using bitmaps. IsManagedByPartitionAlloc*Pool use the bitmaps
49 // to judge whether a given address is in a pool that supports BackupRefPtr or
50 // in a pool that doesn't. All PartitionAlloc allocations must be in either of
51 // the pools.
PA_COMPONENT_EXPORT(PARTITION_ALLOC)52 class PA_COMPONENT_EXPORT(PARTITION_ALLOC) AddressPoolManager {
53 public:
54 static AddressPoolManager& GetInstance();
55
56 AddressPoolManager(const AddressPoolManager&) = delete;
57 AddressPoolManager& operator=(const AddressPoolManager&) = delete;
58
59 #if BUILDFLAG(HAS_64_BIT_POINTERS)
60 void Add(pool_handle handle, uintptr_t address, size_t length);
61 void Remove(pool_handle handle);
62
63 // Populate a |used| bitset of superpages currently in use.
64 void GetPoolUsedSuperPages(pool_handle handle,
65 std::bitset<kMaxSuperPagesInPool>& used);
66
67 // Return the base address of a pool.
68 uintptr_t GetPoolBaseAddress(pool_handle handle);
69 #endif // BUILDFLAG(HAS_64_BIT_POINTERS)
70
71 // Reserves address space from the pool.
72 uintptr_t Reserve(pool_handle handle,
73 uintptr_t requested_address,
74 size_t length);
75
76 // Frees address space back to the pool and decommits underlying system pages.
77 void UnreserveAndDecommit(pool_handle handle,
78 uintptr_t address,
79 size_t length);
80 void ResetForTesting();
81
82 #if !BUILDFLAG(HAS_64_BIT_POINTERS)
83 void MarkUsed(pool_handle handle, uintptr_t address, size_t size);
84 void MarkUnused(pool_handle handle, uintptr_t address, size_t size);
85
86 static bool IsManagedByRegularPool(uintptr_t address) {
87 return AddressPoolManagerBitmap::IsManagedByRegularPool(address);
88 }
89
90 static bool IsManagedByBRPPool(uintptr_t address) {
91 return AddressPoolManagerBitmap::IsManagedByBRPPool(address);
92 }
93 #endif // !BUILDFLAG(HAS_64_BIT_POINTERS)
94
95 void DumpStats(AddressSpaceStatsDumper* dumper);
96
97 private:
98 friend class AddressPoolManagerForTesting;
99 #if BUILDFLAG(ENABLE_PKEYS)
100 // If we use a pkey pool, we need to tag its metadata with the pkey. Allow the
101 // function to get access to the pool pointer.
102 friend void TagGlobalsWithPkey(int pkey);
103 #endif
104
105 constexpr AddressPoolManager() = default;
106 ~AddressPoolManager() = default;
107
108 // Populates `stats` if applicable.
109 // Returns whether `stats` was populated. (They might not be, e.g.
110 // if PartitionAlloc is wholly unused in this process.)
111 bool GetStats(AddressSpaceStats* stats);
112
113 #if BUILDFLAG(HAS_64_BIT_POINTERS)
114 class Pool {
115 public:
116 constexpr Pool() = default;
117 ~Pool() = default;
118
119 Pool(const Pool&) = delete;
120 Pool& operator=(const Pool&) = delete;
121
122 void Initialize(uintptr_t ptr, size_t length);
123 bool IsInitialized();
124 void Reset();
125
126 uintptr_t FindChunk(size_t size);
127 void FreeChunk(uintptr_t address, size_t size);
128
129 bool TryReserveChunk(uintptr_t address, size_t size);
130
131 void GetUsedSuperPages(std::bitset<kMaxSuperPagesInPool>& used);
132 uintptr_t GetBaseAddress();
133
134 void GetStats(PoolStats* stats);
135
136 private:
137 Lock lock_;
138
139 // The bitset stores the allocation state of the address pool. 1 bit per
140 // super-page: 1 = allocated, 0 = free.
141 std::bitset<kMaxSuperPagesInPool> alloc_bitset_ PA_GUARDED_BY(lock_);
142
143 // An index of a bit in the bitset before which we know for sure there all
144 // 1s. This is a best-effort hint in the sense that there still may be lots
145 // of 1s after this index, but at least we know there is no point in
146 // starting the search before it.
147 size_t bit_hint_ PA_GUARDED_BY(lock_) = 0;
148
149 size_t total_bits_ = 0;
150 uintptr_t address_begin_ = 0;
151 #if BUILDFLAG(PA_DCHECK_IS_ON)
152 uintptr_t address_end_ = 0;
153 #endif
154 };
155
156 PA_ALWAYS_INLINE Pool* GetPool(pool_handle handle) {
157 PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools);
158 return &aligned_pools_.pools_[handle - 1];
159 }
160
161 // Gets the stats for the pool identified by `handle`, if
162 // initialized.
163 void GetPoolStats(pool_handle handle, PoolStats* stats);
164
165 // If pkey support is enabled, we need to pkey-tag the pkey pool (which needs
166 // to be last). For this, we need to add padding in front of the pools so that
167 // pkey one starts on a page boundary.
168 struct {
169 char pad_[PA_PKEY_ARRAY_PAD_SZ(Pool, kNumPools)] = {};
170 Pool pools_[kNumPools];
171 char pad_after_[PA_PKEY_FILL_PAGE_SZ(sizeof(Pool))] = {};
172 } aligned_pools_ PA_PKEY_ALIGN;
173
174 #endif // BUILDFLAG(HAS_64_BIT_POINTERS)
175
176 static PA_CONSTINIT AddressPoolManager singleton_;
177 };
178
179 } // namespace partition_alloc::internal
180
181 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_
182