/src/cpython/Objects/mimalloc/segment-map.c
Line | Count | Source |
1 | | /* ---------------------------------------------------------------------------- |
2 | | Copyright (c) 2019-2023, Microsoft Research, Daan Leijen |
3 | | This is free software; you can redistribute it and/or modify it under the |
4 | | terms of the MIT license. A copy of the license can be found in the file |
5 | | "LICENSE" at the root of this distribution. |
6 | | -----------------------------------------------------------------------------*/ |
7 | | |
8 | | /* ----------------------------------------------------------- |
9 | | The following functions are to reliably find the segment or |
10 | | block that encompasses any pointer p (or NULL if it is not |
11 | | in any of our segments). |
12 | | We maintain a bitmap of all memory with 1 bit per MI_SEGMENT_SIZE (64MiB) |
13 | | set to 1 if it contains the segment meta data. |
14 | | ----------------------------------------------------------- */ |
15 | | #include "mimalloc.h" |
16 | | #include "mimalloc/internal.h" |
17 | | #include "mimalloc/atomic.h" |
18 | | |
19 | | // Reduce total address space to reduce .bss (due to the `mi_segment_map`) |
20 | | #if (MI_INTPTR_SIZE > 4) && MI_TRACK_ASAN |
21 | | #define MI_SEGMENT_MAP_MAX_ADDRESS (128*1024ULL*MI_GiB) // 128 TiB (see issue #881) |
22 | | #elif (MI_INTPTR_SIZE > 4) |
23 | 0 | #define MI_SEGMENT_MAP_MAX_ADDRESS (48*1024ULL*MI_GiB) // 48 TiB |
24 | | #else |
25 | | #define MI_SEGMENT_MAP_MAX_ADDRESS (UINT32_MAX) |
26 | | #endif |
27 | | |
28 | 0 | #define MI_SEGMENT_MAP_PART_SIZE (MI_INTPTR_SIZE*MI_KiB - 128) // 128 > sizeof(mi_memid_t) ! |
29 | 0 | #define MI_SEGMENT_MAP_PART_BITS (8*MI_SEGMENT_MAP_PART_SIZE) |
30 | | #define MI_SEGMENT_MAP_PART_ENTRIES (MI_SEGMENT_MAP_PART_SIZE / MI_INTPTR_SIZE) |
31 | 0 | #define MI_SEGMENT_MAP_PART_BIT_SPAN (MI_SEGMENT_ALIGN) // memory area covered by 1 bit |
32 | | |
33 | | #if (MI_SEGMENT_MAP_PART_BITS < (MI_SEGMENT_MAP_MAX_ADDRESS / MI_SEGMENT_MAP_PART_BIT_SPAN)) // prevent overflow on 32-bit (issue #1017) |
34 | 0 | #define MI_SEGMENT_MAP_PART_SPAN (MI_SEGMENT_MAP_PART_BITS * MI_SEGMENT_MAP_PART_BIT_SPAN) |
35 | | #else |
36 | | #define MI_SEGMENT_MAP_PART_SPAN MI_SEGMENT_MAP_MAX_ADDRESS |
37 | | #endif |
38 | | |
39 | 0 | #define MI_SEGMENT_MAP_MAX_PARTS ((MI_SEGMENT_MAP_MAX_ADDRESS / MI_SEGMENT_MAP_PART_SPAN) + 1) |
40 | | |
41 | | // A part of the segment map. |
42 | | typedef struct mi_segmap_part_s { |
43 | | mi_memid_t memid; |
44 | | _Atomic(uintptr_t) map[MI_SEGMENT_MAP_PART_ENTRIES]; |
45 | | } mi_segmap_part_t; |
46 | | |
47 | | // Allocate parts on-demand to reduce .bss footprint |
48 | | static _Atomic(mi_segmap_part_t*) mi_segment_map[MI_SEGMENT_MAP_MAX_PARTS]; // = { NULL, .. } |
49 | | |
50 | 0 | static mi_segmap_part_t* mi_segment_map_index_of(const mi_segment_t* segment, bool create_on_demand, size_t* idx, size_t* bitidx) { |
51 | | // note: segment can be invalid or NULL. |
52 | 0 | mi_assert_internal(_mi_ptr_segment(segment + 1) == segment); // is it aligned on MI_SEGMENT_SIZE? |
53 | 0 | *idx = 0; |
54 | 0 | *bitidx = 0; |
55 | 0 | if ((uintptr_t)segment >= MI_SEGMENT_MAP_MAX_ADDRESS) return NULL; |
56 | 0 | const uintptr_t segindex = ((uintptr_t)segment) / MI_SEGMENT_MAP_PART_SPAN; |
57 | 0 | if (segindex >= MI_SEGMENT_MAP_MAX_PARTS) return NULL; |
58 | 0 | mi_segmap_part_t* part = mi_atomic_load_ptr_relaxed(mi_segmap_part_t, &mi_segment_map[segindex]); |
59 | | |
60 | | // allocate on demand to reduce .bss footprint |
61 | 0 | if mi_unlikely(part == NULL) { |
62 | 0 | if (!create_on_demand) return NULL; |
63 | 0 | mi_memid_t memid; |
64 | 0 | part = (mi_segmap_part_t*)_mi_os_zalloc(sizeof(mi_segmap_part_t), &memid); |
65 | 0 | if (part == NULL) return NULL; |
66 | 0 | part->memid = memid; |
67 | 0 | mi_segmap_part_t* expected = NULL; |
68 | 0 | if (!mi_atomic_cas_ptr_strong_release(mi_segmap_part_t, &mi_segment_map[segindex], &expected, part)) { |
69 | 0 | _mi_os_free(part, sizeof(mi_segmap_part_t), memid); |
70 | 0 | part = expected; |
71 | 0 | if (part == NULL) return NULL; |
72 | 0 | } |
73 | 0 | } |
74 | 0 | mi_assert(part != NULL); |
75 | 0 | const uintptr_t offset = ((uintptr_t)segment) % MI_SEGMENT_MAP_PART_SPAN; |
76 | 0 | const uintptr_t bitofs = offset / MI_SEGMENT_MAP_PART_BIT_SPAN; |
77 | 0 | *idx = bitofs / MI_INTPTR_BITS; |
78 | 0 | *bitidx = bitofs % MI_INTPTR_BITS; |
79 | 0 | return part; |
80 | 0 | } |
81 | | |
82 | 0 | void _mi_segment_map_allocated_at(const mi_segment_t* segment) { |
83 | 0 | if (segment->memid.memkind == MI_MEM_ARENA) return; // we lookup segments first in the arena's and don't need the segment map |
84 | 0 | size_t index; |
85 | 0 | size_t bitidx; |
86 | 0 | mi_segmap_part_t* part = mi_segment_map_index_of(segment, true /* alloc map if needed */, &index, &bitidx); |
87 | 0 | if (part == NULL) return; // outside our address range.. |
88 | 0 | uintptr_t mask = mi_atomic_load_relaxed(&part->map[index]); |
89 | 0 | uintptr_t newmask; |
90 | 0 | do { |
91 | 0 | newmask = (mask | ((uintptr_t)1 << bitidx)); |
92 | 0 | } while (!mi_atomic_cas_weak_release(&part->map[index], &mask, newmask)); |
93 | 0 | } |
94 | | |
95 | 0 | void _mi_segment_map_freed_at(const mi_segment_t* segment) { |
96 | 0 | if (segment->memid.memkind == MI_MEM_ARENA) return; |
97 | 0 | size_t index; |
98 | 0 | size_t bitidx; |
99 | 0 | mi_segmap_part_t* part = mi_segment_map_index_of(segment, false /* don't alloc if not present */, &index, &bitidx); |
100 | 0 | if (part == NULL) return; // outside our address range.. |
101 | 0 | uintptr_t mask = mi_atomic_load_relaxed(&part->map[index]); |
102 | 0 | uintptr_t newmask; |
103 | 0 | do { |
104 | 0 | newmask = (mask & ~((uintptr_t)1 << bitidx)); |
105 | 0 | } while (!mi_atomic_cas_weak_release(&part->map[index], &mask, newmask)); |
106 | 0 | } |
107 | | |
108 | | // Determine the segment belonging to a pointer or NULL if it is not in a valid segment. |
109 | 0 | static mi_segment_t* _mi_segment_of(const void* p) { |
110 | 0 | if (p == NULL) return NULL; |
111 | 0 | mi_segment_t* segment = _mi_ptr_segment(p); // segment can be NULL |
112 | 0 | size_t index; |
113 | 0 | size_t bitidx; |
114 | 0 | mi_segmap_part_t* part = mi_segment_map_index_of(segment, false /* dont alloc if not present */, &index, &bitidx); |
115 | 0 | if (part == NULL) return NULL; |
116 | 0 | const uintptr_t mask = mi_atomic_load_relaxed(&part->map[index]); |
117 | 0 | if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) { |
118 | | // yes, allocated by us |
119 | 0 | const bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie); |
120 | 0 | if mi_likely(cookie_ok) { |
121 | 0 | return segment; // yes, allocated by us and valid |
122 | 0 | } |
123 | 0 | else { |
124 | 0 | _mi_error_message(EFAULT, "segment map found an invalid segment, possibly corrupted meta-data (address=%p, segment=%p)\n", p, segment); |
125 | 0 | } |
126 | 0 | } |
127 | 0 | return NULL; |
128 | 0 | } |
129 | | |
130 | | // Is this a valid pointer in our heap? |
131 | 0 | static bool mi_is_valid_pointer(const void* p) { |
132 | | // first check if it is in an arena, then check if it is OS allocated |
133 | 0 | return (_mi_arena_contains(p) || _mi_segment_of(p) != NULL); |
134 | 0 | } |
135 | | |
136 | 0 | mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { |
137 | 0 | return mi_is_valid_pointer(p); |
138 | 0 | } |
139 | | |
140 | 0 | void _mi_segment_map_unsafe_destroy(void) { |
141 | 0 | for (size_t i = 0; i < MI_SEGMENT_MAP_MAX_PARTS; i++) { |
142 | 0 | mi_segmap_part_t* part = mi_atomic_exchange_ptr_relaxed(mi_segmap_part_t, &mi_segment_map[i], NULL); |
143 | 0 | if (part != NULL) { |
144 | 0 | _mi_os_free(part, sizeof(mi_segmap_part_t), part->memid); |
145 | 0 | } |
146 | 0 | } |
147 | 0 | } |