MySQL 9.1.0
Source Code Documentation
page_alloc.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 2021, 2024, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is designed to work with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation. The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have either included with
15the program or referenced in the documentation.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
20for more details.
21
22You should have received a copy of the GNU General Public License along with
23this program; if not, write to the Free Software Foundation, Inc.,
2451 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26*****************************************************************************/
27
28/** @file include/detail/ut/page_alloc.h
29 Implementation bits and pieces for page-aligned allocations. */
30
31#ifndef detail_ut_page_alloc_h
32#define detail_ut_page_alloc_h
33
34#ifdef _WIN32
35#include <windows.h>
36// _must_ go after windows.h, this comment makes clang-format to preserve
37// the include order
38#include <memoryapi.h>
39#else
40#include <sys/mman.h>
41#endif
42
43#include <cassert>
44#include <cstddef>
45#include <cstdint>
46#include <cstdlib>
47#include <memory>
48#include <utility>
49
50#include "my_compiler.h"
51#include "my_config.h"
52#include "mysqld_error.h"
58
59namespace ut {
60namespace detail {
61
62/** Allocates system page-aligned memory.
63
64 @param[in] n_bytes Size of storage (in bytes) requested to be allocated.
65 @return Pointer to the allocated storage. nullptr if allocation failed.
66*/
67inline void *page_aligned_alloc(size_t n_bytes) {
68#ifdef _WIN32
69 // With lpAddress set to nullptr, VirtualAlloc will internally round n_bytes
70 // to the multiple of system page size if it is not already
71 void *ptr =
72 VirtualAlloc(nullptr, n_bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
73 if (unlikely(!ptr)) {
74 ib::log_warn(ER_IB_MSG_856) << "page_aligned_alloc VirtualAlloc(" << n_bytes
75 << " bytes) failed;"
76 " Windows error "
77 << GetLastError();
78 }
79 return ptr;
80#else
81 // With addr set to nullptr, mmap will internally round n_bytes to the
82 // multiple of system page size if it is not already
83 void *ptr = mmap(nullptr, n_bytes, PROT_READ | PROT_WRITE,
84 MAP_PRIVATE | MAP_ANON, -1, 0);
85 if (unlikely(ptr == (void *)-1)) {
86 ib::log_warn(ER_IB_MSG_856) << "page_aligned_alloc mmap(" << n_bytes
87 << " bytes) failed;"
88 " errno "
89 << errno;
90 }
91 return (ptr != (void *)-1) ? ptr : nullptr;
92#endif
93}
94
95/** Releases system page-aligned storage.
96
97 @param[in] ptr Pointer to system page-aligned storage.
98 @param[in] n_bytes Size of the storage.
99 @return True if releasing the page-aligned memory was successful.
100 */
101inline bool page_aligned_free(void *ptr, size_t n_bytes [[maybe_unused]]) {
102 if (unlikely(!ptr)) return false;
103#ifdef _WIN32
104 auto ret = VirtualFree(ptr, 0, MEM_RELEASE);
105 if (unlikely(ret == 0)) {
106 ib::log_error(ER_IB_MSG_858)
107 << "large_page_aligned_free VirtualFree(" << ptr
108 << ") failed;"
109 " Windows error "
110 << GetLastError();
111 }
112 return ret != 0;
113#else
114 // length aka n_bytes does not need to be aligned to page-size
115 auto ret = munmap(ptr, n_bytes);
116 if (unlikely(ret != 0)) {
117 ib::log_error(ER_IB_MSG_858)
118 << "page_aligned_free munmap(" << ptr << ", " << n_bytes
119 << ") failed;"
120 " errno "
121 << errno;
122 }
123 return ret == 0;
124#endif
125}
126
127/** Allocation routines which are purposed for allocating system page-aligned
128 memory.
129
130 page_aligned_alloc() and page_aligned_free() are taking care of
131 OS specific details and Page_alloc is a convenience wrapper which only
132 makes the use of system page-aligned memory more ergonomic so that it
133 serializes the actual size being allocated into the raw memory. This size
134 is then automagically deduced when system page-aligned memory is being
135 freed. Otherwise, client code would have been responsible to store and keep
136 that value somewhere until the memory segment is freed. Additionally,
137 information on type of page used to back up requested allocation is also
138 serialized into the memory allowing to build higher-kinded abstractions more
139 easily. See ut::malloc_large_page with option to fallback to regular pages
140 through ut::malloc_page.
141
142 Cost associated with this abstraction is the size of a single CPU page. In
143 terms of virtual memory, especially in 64-bit address space, this cost is
144 negligible. In practice this means that for each N pages allocation
145 request there will be N+1 pages allocated beneath.
146
147 Memory layout representation looks like the following:
148
149 ------------------------------------------
150 | PAGE-ALLOC-METADATA | ... DATA ... |
151 ------------------------------------------
152 ^ ^
153 | |
154 | |
155 | ptr (system-page) to be
156 | returned to call-site
157 |
158 --------------------------------
159 | DATALEN | PAGE-TYPE | VARLEN |
160 --------------------------------
161 ^
162 |
163 |
164 ptr returned by
165 page_aligned_alloc
166
167
168 For details on DATALEN, PAGE-TYPE and VARLEN fields see Page_alloc_metadata.
169
170 DATA is an actual page-aligned segment that will be returned to the
171 call-site and which the client code will be able to use for the application
172 data.
173 */
174struct Page_alloc : public allocator_traits<false> {
176
177 /** Allocates memory through large-page support.
178
179 @param[in] size Size of storage (in bytes) requested to be allocated.
180 @return Pointer to the allocated storage. nullptr if allocation failed.
181 */
182 static inline void *alloc(std::size_t size) {
183 auto total_len = round_to_next_multiple(
185 auto mem = page_aligned_alloc(total_len);
186 if (unlikely(!mem)) return nullptr;
189 return static_cast<uint8_t *>(mem) + page_allocation_metadata::len;
190 }
191
192 /** Releases storage allocated through Page_alloc::alloc().
193
194 @param[in] data Pointer to storage allocated through Page_alloc::alloc()
195 @return True if releasing the page-aligned memory was successful.
196 */
197 static inline bool free(void *data) noexcept {
198 if (unlikely(!data)) return false;
200 return page_aligned_free(deduce(data),
202 }
203
204 /** Returns the number of bytes that have been allocated.
205
206 @param[in] data Pointer to storage allocated through Page_alloc::alloc()
207 @return Number of bytes.
208 */
213 }
214
215 /** Returns the the type of the page.
216
217 @param[in] data Pointer to storage allocated through Page_alloc::alloc()
218 @return Page type.
219 */
220 static inline Page_type page_type(void *data) {
223 }
224
225 /** Retrieves the pointer and size of the allocation provided by the OS. It is
226 a low level information, and is needed only to call low level
227 memory-related OS functions.
228
229 @param[in] data Pointer to storage allocated through Page_alloc::alloc()
230 @return Low level allocation info.
231 */
232 static inline allocation_low_level_info low_level_info(void *data) {
234 return {deduce(data), page_allocation_metadata::datalen(data)};
235 }
236
237 private:
238 /** Helper function which deduces the original pointer returned by
239 Page_alloc from a pointer which is passed to us by the call-site.
240 */
241 static inline void *deduce(void *data) noexcept {
243 const auto res = reinterpret_cast<void *>(static_cast<uint8_t *>(data) -
245 ut_ad(reinterpret_cast<std::uintptr_t>(res) % CPU_PAGE_SIZE == 0);
246 return res;
247 }
248};
249
250/** Allocation routines which are purposed for allocating system page-aligned
251 memory. This is a PFS (performance-schema) variant of Page_alloc.
252 Implemented in terms of Page_alloc_metadata_pfs.
253
254 page_aligned_alloc() and page_aligned_free() are taking care of
255 OS specific details and Page_alloc_pfs is a convenience wrapper which
256 only makes the use of system page-aligned memory more ergonomic so that
257 it serializes all the relevant PFS details into the raw memory. Otherwise,
258 client code would have been responsible to store and keep those details
259 somewhere until the memory segment is freed. Additionally, information on
260 type of page used to back up requested allocation is also serialized into
261 the memory allowing to build higher-kinded abstractions more easily. See
262 ut::malloc_large_page with option to fallback to regular pages through
263 ut::malloc_page.
264
265 Cost associated with this abstraction is the size of a single CPU page. In
266 terms of virtual memory, especially in 64-bit address space, this cost is
267 negligible. In practice this means that for each N pages allocation
268 request there will be N+1 pages allocated beneath.
269
270 Memory layout representation looks like the following:
271
272 ----------------------------------------------
273 | PAGE-ALLOC-METADATA-PFS | ... DATA ... |
274 ----------------------------------------------
275 ^ ^
276 | |
277 | |
278 | ptr (system-page) to be
279 | returned to call-site
280 |
281 ---------------------------------------------------
282 | PFS-META | PAGE-TYPE | VARLEN | PFS-META-OFFSET |
283 ---------------------------------------------------
284 ^ ^
285 | |
286 | ---------------------------
287 | | OWNER | DATALEN | KEY |
288 | ---------------------------
289 |
290 ptr returned by
291 page_aligned_alloc
292
293 For details on PFS-META, PAGE-TYPE, VARLEN and PFS-META-OFFSET fields
294 see Page_alloc_metadata_pfs.
295
296 DATA is an actual page-aligned segment that will be returned to the
297 call-site and which the client code will be able to use for the application
298 data.
299 */
300struct Page_alloc_pfs : public allocator_traits<true> {
302
303 /** Allocates system page-aligned memory.
304
305 @param[in] size Size of storage (in bytes) requested to be allocated.
306 @param[in] key PSI memory key to be used for PFS memory instrumentation.
307 @return Pointer to the allocated storage. nullptr if allocation failed.
308 */
309 static inline void *alloc(
310 std::size_t size,
312 auto total_len = round_to_next_multiple(
314 auto mem = page_aligned_alloc(total_len);
315 if (unlikely(!mem)) return nullptr;
316
317#ifdef HAVE_PSI_MEMORY_INTERFACE
318 // The point of this allocator variant is to trace the memory allocations
319 // through PFS (PSI) so do it.
321 key = PSI_MEMORY_CALL(memory_alloc)(key, total_len, &owner);
322 // To be able to do the opposite action of tracing when we are releasing the
323 // memory, we need right about the same data we passed to the tracing
324 // memory_alloc function. Let's encode this it into our allocator so we
325 // don't have to carry and keep this data around.
332#endif
333
334 return static_cast<uint8_t *>(mem) + page_allocation_metadata::len;
335 }
336
337 /** Releases storage allocated through Page_alloc_pfs::alloc().
338
339 @param[in] data Pointer to storage allocated through
340 Page_alloc_pfs::alloc()
341 @return True if releasing the page-aligned memory was successful.
342 */
343 static inline bool free(PFS_metadata::data_segment_ptr data) noexcept {
344 if (unlikely(!data)) return false;
346
347 PFS_metadata::pfs_datalen_t total_len = {};
348#ifdef HAVE_PSI_MEMORY_INTERFACE
349 // Deduce the PFS data we encoded in Page_alloc_pfs::alloc()
351 auto owner =
354 // With the deduced PFS data, now trace the memory release action.
356 (key, total_len, owner);
357#endif
358
359 return page_aligned_free(deduce(data), total_len);
360 }
361
362 /** Returns the number of bytes that have been allocated.
363
364 @param[in] data Pointer to storage allocated through
365 Page_alloc_pfs::alloc()
366 @return Number of bytes.
367 */
368 static inline size_t datalen(PFS_metadata::data_segment_ptr data) {
372 }
373
374 /** Returns the Page_type.
375
376 @param[in] data Pointer to storage allocated through
377 Page_alloc_pfs::alloc()
378 @return Page type.
379 */
383 }
384
385 /** Retrieves the pointer and size of the allocation provided by the OS. It is
386 a low level information, and is needed only to call low level
387 memory-related OS functions.
388
389 @param[in] data Pointer to storage allocated through
390 Page_alloc_pfs::alloc()
391 @return Low level allocation info.
392 */
393 static inline allocation_low_level_info low_level_info(void *data) {
395 return {deduce(data),
397 }
398
399 private:
400 /** Helper function which deduces the original pointer returned by
401 Page_alloc_pfs from a pointer which is passed to us by the call-site.
402 */
403 static inline void *deduce(PFS_metadata::data_segment_ptr data) noexcept {
405 const auto res =
407 ut_ad(reinterpret_cast<std::uintptr_t>(res) % CPU_PAGE_SIZE == 0);
408 return res;
409 }
410};
411
412/** Simple utility meta-function which selects appropriate allocator variant
413 (implementation) depending on the input parameter(s).
414 */
415template <bool Pfs_memory_instrumentation_on>
417 using type = Page_alloc; // When PFS is OFF, pick ordinary, non-PFS, variant
418};
419
420template <>
422 using type = Page_alloc_pfs; // Otherwise, pick PFS variant
423};
424
425/** Just a small helper type which saves us some keystrokes. */
426template <bool Pfs_memory_instrumentation_on>
429
430/** Small wrapper which utilizes SFINAE to dispatch the call to appropriate
431 aligned allocator implementation.
432 */
433template <typename Impl>
435 template <typename T = Impl>
438 return Impl::alloc(size, key);
439 }
440 template <typename T = Impl>
442 alloc(size_t size, PSI_memory_key /*key*/) {
443 return Impl::alloc(size);
444 }
445 static inline bool free(void *ptr) { return Impl::free(ptr); }
446 static inline size_t datalen(void *ptr) { return Impl::datalen(ptr); }
447 static inline Page_type page_type(void *ptr) { return Impl::page_type(ptr); }
448 static inline allocation_low_level_info low_level_info(void *ptr) {
449 return Impl::low_level_info(ptr);
450 }
451};
452
453} // namespace detail
454} // namespace ut
455
456#endif
Simple allocator traits.
#define PSI_MEMORY_CALL(M)
Definition: psi_memory.h:36
unsigned int PSI_memory_key
Instrumented memory key.
Definition: psi_memory_bits.h:49
Small helper functions.
#define free(A)
Definition: lexyy.cc:915
Header for compiler-dependent features.
constexpr bool unlikely(bool expr)
Definition: my_compiler.h:58
#define CPU_PAGE_SIZE
Definition: my_config.h:311
Definition: ut0tuple.h:57
static auto log_warn()
Definition: ut0log.h:452
static auto log_error()
Definition: ut0log.h:453
size_t size(const char *const c)
Definition: base64.h:46
static PSI_memory_key memory_alloc(PSI_memory_key, size_t, struct PSI_thread **owner)
Definition: psi_memory_v2_empty.cc:35
static void memory_free(PSI_memory_key, size_t, struct PSI_thread *)
Definition: psi_memory_v2_empty.cc:53
Page_type
Types of pages currently supported by ut:: library functions.
Definition: page_metadata.h:43
constexpr size_t round_to_next_multiple(size_t n, size_t m)
Calculates the next multiple of m that is bigger or equal to n.
Definition: helper.h:68
typename select_page_alloc_impl< Pfs_memory_instrumentation_on >::type select_page_alloc_impl_t
Just a small helper type which saves us some keystrokes.
Definition: page_alloc.h:428
void * page_aligned_alloc(size_t n_bytes)
Allocates system page-aligned memory.
Definition: page_alloc.h:67
bool page_aligned_free(void *ptr, size_t n_bytes)
Releases system page-aligned storage.
Definition: page_alloc.h:101
This file contains a set of libraries providing overloads for regular dynamic allocation routines whi...
Definition: aligned_alloc.h:48
Implementation bits and pieces for metadata for normal and large (huge) page allocations.
required string key
Definition: replication_asynchronous_connection_failover.proto:60
required string type
Definition: replication_group_member_actions.proto:34
static MEM_ROOT mem
Definition: sql_servers.cc:100
Implementation bits and pieces for PFS metadata handling.
Can be used to extract pointer and size of the allocation provided by the OS.
Definition: ut0new.h:140
void * data_segment_ptr
Definition: pfs.h:94
std::size_t pfs_datalen_t
Definition: pfs.h:91
static void pfs_datalen(data_segment_ptr data, size_t datalen) noexcept
Helper function which stores the PFS datalen info into the DATALEN field.
Definition: pfs.h:109
static void pfs_key(data_segment_ptr data, pfs_memory_key_t key) noexcept
Helper function which stores the PFS key info into the KEY field.
Definition: pfs.h:115
PSI_thread * pfs_owning_thread_t
Convenience types that we will be using to serialize necessary details into the Aligned_alloc metadat...
Definition: pfs.h:90
static void pfs_owning_thread(data_segment_ptr data, pfs_owning_thread_t thread) noexcept
Helper function which stores the PFS thread info into the OWNER field.
Definition: pfs.h:103
static void pfs_metaoffset(data_segment_ptr data, std::size_t alignment) noexcept
Helper function which stores the offset to PFS metadata segment into the PFS-META-OFFSET field.
Definition: pfs.h:122
PSI_memory_key pfs_memory_key_t
Definition: pfs.h:92
static void * deduce_pfs_meta(data_segment_ptr data) noexcept
Helper function which deduces the pointer to the beginning of PFS metadata segment given the pointer ...
Definition: pfs.h:156
Small wrapper which utilizes SFINAE to dispatch the call to appropriate aligned allocator implementat...
Definition: page_alloc.h:434
static std::enable_if<!T::is_pfs_instrumented_v, void * >::type alloc(size_t size, PSI_memory_key)
Definition: page_alloc.h:442
static std::enable_if< T::is_pfs_instrumented_v, void * >::type alloc(size_t size, PSI_memory_key key)
Definition: page_alloc.h:437
static Page_type page_type(void *ptr)
Definition: page_alloc.h:447
static size_t datalen(void *ptr)
Definition: page_alloc.h:446
static bool free(void *ptr)
Definition: page_alloc.h:445
static allocation_low_level_info low_level_info(void *ptr)
Definition: page_alloc.h:448
Helper struct implementing the type which represents the metadata for all types of PFS-aware page-ali...
Definition: page_metadata.h:198
static constexpr auto len
This is how much this metadata segment will be big.
Definition: page_metadata.h:203
static Page_type page_type(void *data)
Sanity check so that we can be sure that the size of our metadata segment is such so that the pointer...
Definition: page_metadata.h:221
Helper struct implementing the type which represents the metadata for all types of page-aligned alloc...
Definition: page_metadata.h:75
static constexpr auto len
This is how much tise metadata segment will be big.
Definition: page_metadata.h:77
static Page_type page_type(void *data)
Accessor to the page_type_t field.
Definition: page_metadata.h:110
static datalen_t datalen(void *data)
Sanity check so that we can be sure that the size of our metadata segment is such so that the next se...
Definition: page_metadata.h:101
size_t datalen_t
These are the types representing our memory fields.
Definition: page_metadata.h:80
Allocation routines which are purposed for allocating system page-aligned memory.
Definition: page_alloc.h:300
static bool free(PFS_metadata::data_segment_ptr data) noexcept
Releases storage allocated through Page_alloc_pfs::alloc().
Definition: page_alloc.h:343
static allocation_low_level_info low_level_info(void *data)
Retrieves the pointer and size of the allocation provided by the OS.
Definition: page_alloc.h:393
static size_t datalen(PFS_metadata::data_segment_ptr data)
Returns the number of bytes that have been allocated.
Definition: page_alloc.h:368
static void * alloc(std::size_t size, page_allocation_metadata::pfs_metadata::pfs_memory_key_t key)
Allocates system page-aligned memory.
Definition: page_alloc.h:309
static Page_type page_type(PFS_metadata::data_segment_ptr data)
Returns the Page_type.
Definition: page_alloc.h:380
static void * deduce(PFS_metadata::data_segment_ptr data) noexcept
Helper function which deduces the original pointer returned by Page_alloc_pfs from a pointer which is...
Definition: page_alloc.h:403
Allocation routines which are purposed for allocating system page-aligned memory.
Definition: page_alloc.h:174
static void * alloc(std::size_t size)
Allocates memory through large-page support.
Definition: page_alloc.h:182
static allocation_low_level_info low_level_info(void *data)
Retrieves the pointer and size of the allocation provided by the OS.
Definition: page_alloc.h:232
static void * deduce(void *data) noexcept
Helper function which deduces the original pointer returned by Page_alloc from a pointer which is pas...
Definition: page_alloc.h:241
static bool free(void *data) noexcept
Releases storage allocated through Page_alloc::alloc().
Definition: page_alloc.h:197
static Page_type page_type(void *data)
Returns the the type of the page.
Definition: page_alloc.h:220
static page_allocation_metadata::datalen_t datalen(void *data)
Returns the number of bytes that have been allocated.
Definition: page_alloc.h:209
Simple allocator traits.
Definition: allocator_traits.h:82
Simple utility meta-function which selects appropriate allocator variant (implementation) depending o...
Definition: page_alloc.h:416
#define ut_ad(EXPR)
Debug assertion.
Definition: ut0dbg.h:105
Base of InnoDB utilities.