1 // Copyright 2018 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 PARTITION_ALLOC_PAGE_ALLOCATOR_CONSTANTS_H_
6 #define PARTITION_ALLOC_PAGE_ALLOCATOR_CONSTANTS_H_
7 
8 #include <cstddef>
9 
10 #include "build/build_config.h"
11 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
12 #include "partition_alloc/partition_alloc_base/component_export.h"
13 
14 #if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
15 
16 #include <mach/vm_page_size.h>
17 
18 // Although page allocator constants are not constexpr, they are run-time
19 // constant. Because the underlying variables they access, such as vm_page_size,
20 // are not marked const, the compiler normally has no way to know that they
21 // don’t change and must obtain their values whenever it can't prove that they
22 // haven't been modified, even if they had already been obtained previously.
23 // Attaching __attribute__((const)) to these declarations allows these redundant
24 // accesses to be omitted under optimization such as common subexpression
25 // elimination.
26 #define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const))
27 
28 #elif (BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_64_BITS)) || \
29     (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
30 // This should work for all POSIX (if needed), but currently all other
31 // supported OS/architecture combinations use either hard-coded values
32 // (such as x86) or have means to determine these values without needing
33 // atomics (such as macOS on arm64).
34 
35 #define PARTITION_ALLOCATOR_CONSTANTS_POSIX_NONCONST_PAGE_SIZE
36 
37 // Page allocator constants are run-time constant
38 #define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const))
39 
40 #include <unistd.h>
41 #include <atomic>
42 
43 namespace partition_alloc::internal {
44 
45 // Holds the current page size and shift, where size = 1 << shift
46 // Use PageAllocationGranularity(), PageAllocationGranularityShift()
47 // to initialize and retrieve these values safely.
48 struct PageCharacteristics {
49   std::atomic<size_t> size;
50   std::atomic<size_t> shift;
51 };
52 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
53 extern PageCharacteristics page_characteristics;
54 
55 }  // namespace partition_alloc::internal
56 
57 #else
58 
59 // When defined, page size constants are fixed at compile time. When not
60 // defined, they may vary at run time.
61 #define PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR 1
62 
63 // Use this macro to declare a function as constexpr or not based on whether
64 // PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR is defined.
65 #define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR constexpr
66 
67 #endif
68 
69 // Ability to name anonymous VMAs is available on some, but not all Linux-based
70 // systems.
71 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
72 #include <sys/prctl.h>
73 
74 #if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
75 #define LINUX_NAME_REGION 1
76 #endif
77 
78 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
79 
80 namespace partition_alloc {
81 namespace internal {
82 
83 // Forward declaration, implementation below
84 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
85 PageAllocationGranularity();
86 
87 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
PageAllocationGranularityShift()88 PageAllocationGranularityShift() {
89 #if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_PPC64)
90   // Modern ppc64 systems support 4kB (shift = 12) and 64kB (shift = 16) page
91   // sizes.  Since 64kB is the de facto standard on the platform and binaries
92   // compiled for 64kB are likely to work on 4kB systems, 64kB is a good choice
93   // here.
94   return 16;  // 64kB
95 #elif defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_LOONGARCH64)
96   return 14;  // 16kB
97 #elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
98   return static_cast<size_t>(vm_page_shift);
99 #elif defined(PARTITION_ALLOCATOR_CONSTANTS_POSIX_NONCONST_PAGE_SIZE)
100   // arm64 supports 4kb (shift = 12), 16kb (shift = 14), and 64kb (shift = 16)
101   // page sizes. Retrieve from or initialize cache.
102   size_t shift = page_characteristics.shift.load(std::memory_order_relaxed);
103   if (PA_UNLIKELY(shift == 0)) {
104     shift = static_cast<size_t>(
105         __builtin_ctz((unsigned int)PageAllocationGranularity()));
106     page_characteristics.shift.store(shift, std::memory_order_relaxed);
107   }
108   return shift;
109 #else
110   return 12;  // 4kB
111 #endif
112 }
113 
114 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
PageAllocationGranularity()115 PageAllocationGranularity() {
116 #if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
117   // This is literally equivalent to |1 << PageAllocationGranularityShift()|
118   // below, but was separated out for IS_APPLE to avoid << on a non-constexpr.
119   return vm_page_size;
120 #elif defined(PARTITION_ALLOCATOR_CONSTANTS_POSIX_NONCONST_PAGE_SIZE)
121   // arm64 supports 4kb, 16kb, and 64kb page sizes. Retrieve from or
122   // initialize cache.
123   size_t size = page_characteristics.size.load(std::memory_order_relaxed);
124   if (PA_UNLIKELY(size == 0)) {
125     size = static_cast<size_t>(getpagesize());
126     page_characteristics.size.store(size, std::memory_order_relaxed);
127   }
128   return size;
129 #else
130   return 1 << PageAllocationGranularityShift();
131 #endif
132 }
133 
134 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
PageAllocationGranularityOffsetMask()135 PageAllocationGranularityOffsetMask() {
136   return PageAllocationGranularity() - 1;
137 }
138 
139 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
PageAllocationGranularityBaseMask()140 PageAllocationGranularityBaseMask() {
141   return ~PageAllocationGranularityOffsetMask();
142 }
143 
144 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
SystemPageShift()145 SystemPageShift() {
146   // On Windows allocation granularity is higher than the page size. This comes
147   // into play when reserving address space range (allocation granularity),
148   // compared to committing pages into memory (system page granularity).
149 #if BUILDFLAG(IS_WIN)
150   return 12;  // 4096=1<<12
151 #else
152   return PageAllocationGranularityShift();
153 #endif
154 }
155 
156 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
SystemPageSize()157 SystemPageSize() {
158 #if (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \
159     defined(PARTITION_ALLOCATOR_CONSTANTS_POSIX_NONCONST_PAGE_SIZE)
160   // This is literally equivalent to |1 << SystemPageShift()| below, but was
161   // separated out for 64-bit IS_APPLE and arm64 on Android/Linux to avoid <<
162   // on a non-constexpr.
163   return PageAllocationGranularity();
164 #else
165   return 1 << SystemPageShift();
166 #endif
167 }
168 
169 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
SystemPageOffsetMask()170 SystemPageOffsetMask() {
171   return SystemPageSize() - 1;
172 }
173 
174 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
SystemPageBaseMask()175 SystemPageBaseMask() {
176   return ~SystemPageOffsetMask();
177 }
178 
179 constexpr size_t kPageMetadataShift = 5;  // 32 bytes per partition page.
180 constexpr size_t kPageMetadataSize = 1 << kPageMetadataShift;
181 
182 }  // namespace internal
183 
184 PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
SystemPageSize()185 SystemPageSize() {
186   return internal::SystemPageSize();
187 }
188 
189 }  // namespace partition_alloc
190 
191 #endif  // PARTITION_ALLOC_PAGE_ALLOCATOR_CONSTANTS_H_
192