blob: 27851d2c5a95dd64a1a098462af69a748b880850 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_ADDRESS_POOL_MANAGER_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_ADDRESS_POOL_MANAGER_H_
#include <bitset>
#include <limits>
#include "build/build_config.h"
#include "partition_alloc/address_pool_manager_types.h"
#include "partition_alloc/partition_address_space.h"
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/component_export.h"
#include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
#include "partition_alloc/partition_alloc_base/thread_annotations.h"
#include "partition_alloc/partition_alloc_buildflags.h"
#include "partition_alloc/partition_alloc_check.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/partition_lock.h"
#include "partition_alloc/thread_isolation/alignment.h"
#include "partition_alloc/thread_isolation/thread_isolation.h"
#if !BUILDFLAG(HAS_64_BIT_POINTERS)
#include "partition_alloc/address_pool_manager_bitmap.h"
#endif
namespace partition_alloc {
class AddressSpaceStatsDumper;
struct AddressSpaceStats;
struct PoolStats;
} // namespace partition_alloc
namespace partition_alloc::internal {
// (64bit version)
// AddressPoolManager takes a reserved virtual address space and manages address
// space allocation.
//
// AddressPoolManager (currently) supports up to 4 pools. Each pool manages a
// contiguous reserved address space. Alloc() takes a pool_handle and returns
// address regions from the specified pool. Free() also takes a pool_handle and
// returns the address region back to the manager.
//
// (32bit version)
// AddressPoolManager wraps AllocPages and FreePages and remembers allocated
// address regions using bitmaps. IsManagedByPartitionAlloc*Pool use the bitmaps
// to judge whether a given address is in a pool that supports BackupRefPtr or
// in a pool that doesn't. All PartitionAlloc allocations must be in either of
// the pools.
class PA_COMPONENT_EXPORT(PARTITION_ALLOC)
PA_THREAD_ISOLATED_ALIGN AddressPoolManager {
public:
static AddressPoolManager& GetInstance();
AddressPoolManager(const AddressPoolManager&) = delete;
AddressPoolManager& operator=(const AddressPoolManager&) = delete;
#if BUILDFLAG(HAS_64_BIT_POINTERS)
void Add(pool_handle handle, uintptr_t address, size_t length);
void Remove(pool_handle handle);
// Populate a |used| bitset of superpages currently in use.
void GetPoolUsedSuperPages(pool_handle handle,
std::bitset<kMaxSuperPagesInPool>& used);
// Return the base address of a pool.
uintptr_t GetPoolBaseAddress(pool_handle handle);
#endif // BUILDFLAG(HAS_64_BIT_POINTERS)
// Reserves address space from the pool.
uintptr_t Reserve(pool_handle handle,
uintptr_t requested_address,
size_t length);
// Frees address space back to the pool and decommits underlying system pages.
void UnreserveAndDecommit(pool_handle handle,
uintptr_t address,
size_t length);
void ResetForTesting();
#if !BUILDFLAG(HAS_64_BIT_POINTERS)
void MarkUsed(pool_handle handle, uintptr_t address, size_t size);
void MarkUnused(pool_handle handle, uintptr_t address, size_t size);
static bool IsManagedByRegularPool(uintptr_t address) {
return AddressPoolManagerBitmap::IsManagedByRegularPool(address);
}
static bool IsManagedByBRPPool(uintptr_t address) {
return AddressPoolManagerBitmap::IsManagedByBRPPool(address);
}
#endif // !BUILDFLAG(HAS_64_BIT_POINTERS)
void DumpStats(AddressSpaceStatsDumper* dumper);
private:
friend class AddressPoolManagerForTesting;
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
// If we use a thread isolated pool, we need to write-protect its metadata.
// Allow the function to get access to the pool pointer.
friend void WriteProtectThreadIsolatedGlobals(ThreadIsolationOption);
#endif
constexpr AddressPoolManager() = default;
~AddressPoolManager() = default;
// Populates `stats` if applicable.
// Returns whether `stats` was populated. (They might not be, e.g.
// if PartitionAlloc is wholly unused in this process.)
bool GetStats(AddressSpaceStats* stats);
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
static void AssertThreadIsolatedLayout();
#endif // BUILDFLAG(ENABLE_THREAD_ISOLATION)
#if BUILDFLAG(HAS_64_BIT_POINTERS)
class Pool {
public:
constexpr Pool() = default;
~Pool() = default;
Pool(const Pool&) = delete;
Pool& operator=(const Pool&) = delete;
void Initialize(uintptr_t ptr, size_t length);
bool IsInitialized();
void Reset();
uintptr_t FindChunk(size_t size);
void FreeChunk(uintptr_t address, size_t size);
bool TryReserveChunk(uintptr_t address, size_t size);
void GetUsedSuperPages(std::bitset<kMaxSuperPagesInPool>& used);
uintptr_t GetBaseAddress();
void GetStats(PoolStats* stats);
private:
// The lock needs to be the first field in this class.
// We write-protect the pool in the ThreadIsolated case, except that the
// lock can be used without acquiring write-permission first (via
// DumpStats()). So instead of protecting the whole variable, we only
// protect the memory after the lock.
// See the alignment of ` below.
Lock lock_;
// The bitset stores the allocation state of the address pool. 1 bit per
// super-page: 1 = allocated, 0 = free.
std::bitset<kMaxSuperPagesInPool> alloc_bitset_ PA_GUARDED_BY(lock_);
// An index of a bit in the bitset before which we know for sure there all
// 1s. This is a best-effort hint in the sense that there still may be lots
// of 1s after this index, but at least we know there is no point in
// starting the search before it.
size_t bit_hint_ PA_GUARDED_BY(lock_) = 0;
size_t total_bits_ = 0;
uintptr_t address_begin_ = 0;
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t address_end_ = 0;
#endif
#if BUILDFLAG(ENABLE_THREAD_ISOLATION)
friend class AddressPoolManager;
friend void WriteProtectThreadIsolatedGlobals(ThreadIsolationOption);
#endif // BUILDFLAG(ENABLE_THREAD_ISOLATION)
};
PA_ALWAYS_INLINE Pool* GetPool(pool_handle handle) {
PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools);
return &pools_[handle - 1];
}
// Gets the stats for the pool identified by `handle`, if
// initialized.
void GetPoolStats(pool_handle handle, PoolStats* stats);
// If thread isolation support is enabled, we need to write-protect the
// isolated pool (which needs to be last). For this, we need to add padding in
// front of the pools so that the isolated one starts on a page boundary.
// We also skip the Lock at the beginning of the pool since it needs to be
// used in contexts where we didn't enable write access to the pool memory.
char pad_[PA_THREAD_ISOLATED_ARRAY_PAD_SZ_WITH_OFFSET(
Pool,
kNumPools,
offsetof(Pool, alloc_bitset_))] = {};
Pool pools_[kNumPools];
#endif // BUILDFLAG(HAS_64_BIT_POINTERS)
static PA_CONSTINIT AddressPoolManager singleton_;
};
} // namespace partition_alloc::internal
#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_ADDRESS_POOL_MANAGER_H_