emap.h revision 1.1 1 1.1 christos #ifndef JEMALLOC_INTERNAL_EMAP_H
2 1.1 christos #define JEMALLOC_INTERNAL_EMAP_H
3 1.1 christos
4 1.1 christos #include "jemalloc/internal/base.h"
5 1.1 christos #include "jemalloc/internal/rtree.h"
6 1.1 christos
7 1.1 christos /*
8 1.1 christos * Note: Ends without at semicolon, so that
9 1.1 christos * EMAP_DECLARE_RTREE_CTX;
10 1.1 christos * in uses will avoid empty-statement warnings.
11 1.1 christos */
12 1.1 christos #define EMAP_DECLARE_RTREE_CTX \
13 1.1 christos rtree_ctx_t rtree_ctx_fallback; \
14 1.1 christos rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback)
15 1.1 christos
16 1.1 christos typedef struct emap_s emap_t;
17 1.1 christos struct emap_s {
18 1.1 christos rtree_t rtree;
19 1.1 christos };
20 1.1 christos
21 1.1 christos /* Used to pass rtree lookup context down the path. */
22 1.1 christos typedef struct emap_alloc_ctx_t emap_alloc_ctx_t;
23 1.1 christos struct emap_alloc_ctx_t {
24 1.1 christos szind_t szind;
25 1.1 christos bool slab;
26 1.1 christos };
27 1.1 christos
28 1.1 christos typedef struct emap_full_alloc_ctx_s emap_full_alloc_ctx_t;
29 1.1 christos struct emap_full_alloc_ctx_s {
30 1.1 christos szind_t szind;
31 1.1 christos bool slab;
32 1.1 christos edata_t *edata;
33 1.1 christos };
34 1.1 christos
35 1.1 christos bool emap_init(emap_t *emap, base_t *base, bool zeroed);
36 1.1 christos
37 1.1 christos void emap_remap(tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind,
38 1.1 christos bool slab);
39 1.1 christos
40 1.1 christos void emap_update_edata_state(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
41 1.1 christos extent_state_t state);
42 1.1 christos
43 1.1 christos /*
44 1.1 christos * The two acquire functions below allow accessing neighbor edatas, if it's safe
45 1.1 christos * and valid to do so (i.e. from the same arena, of the same state, etc.). This
46 1.1 christos * is necessary because the ecache locks are state based, and only protect
47 1.1 christos * edatas with the same state. Therefore the neighbor edata's state needs to be
48 1.1 christos * verified first, before chasing the edata pointer. The returned edata will be
49 1.1 christos * in an acquired state, meaning other threads will be prevented from accessing
50 1.1 christos * it, even if technically the edata can still be discovered from the rtree.
51 1.1 christos *
52 1.1 christos * This means, at any moment when holding pointers to edata, either one of the
53 1.1 christos * state based locks is held (and the edatas are all of the protected state), or
54 1.1 christos * the edatas are in an acquired state (e.g. in active or merging state). The
55 1.1 christos * acquire operation itself (changing the edata to an acquired state) is done
56 1.1 christos * under the state locks.
57 1.1 christos */
58 1.1 christos edata_t *emap_try_acquire_edata_neighbor(tsdn_t *tsdn, emap_t *emap,
59 1.1 christos edata_t *edata, extent_pai_t pai, extent_state_t expected_state,
60 1.1 christos bool forward);
61 1.1 christos edata_t *emap_try_acquire_edata_neighbor_expand(tsdn_t *tsdn, emap_t *emap,
62 1.1 christos edata_t *edata, extent_pai_t pai, extent_state_t expected_state);
63 1.1 christos void emap_release_edata(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
64 1.1 christos extent_state_t new_state);
65 1.1 christos
66 1.1 christos /*
67 1.1 christos * Associate the given edata with its beginning and end address, setting the
68 1.1 christos * szind and slab info appropriately.
69 1.1 christos * Returns true on error (i.e. resource exhaustion).
70 1.1 christos */
71 1.1 christos bool emap_register_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
72 1.1 christos szind_t szind, bool slab);
73 1.1 christos
74 1.1 christos /*
75 1.1 christos * Does the same thing, but with the interior of the range, for slab
76 1.1 christos * allocations.
77 1.1 christos *
78 1.1 christos * You might wonder why we don't just have a single emap_register function that
79 1.1 christos * does both depending on the value of 'slab'. The answer is twofold:
80 1.1 christos * - As a practical matter, in places like the extract->split->commit pathway,
81 1.1 christos * we defer the interior operation until we're sure that the commit won't fail
82 1.1 christos * (but we have to register the split boundaries there).
83 1.1 christos * - In general, we're trying to move to a world where the page-specific
84 1.1 christos * allocator doesn't know as much about how the pages it allocates will be
85 1.1 christos * used, and passing a 'slab' parameter everywhere makes that more
86 1.1 christos * complicated.
87 1.1 christos *
88 1.1 christos * Unlike the boundary version, this function can't fail; this is because slabs
89 1.1 christos * can't get big enough to touch a new page that neither of the boundaries
90 1.1 christos * touched, so no allocation is necessary to fill the interior once the boundary
91 1.1 christos * has been touched.
92 1.1 christos */
93 1.1 christos void emap_register_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
94 1.1 christos szind_t szind);
95 1.1 christos
96 1.1 christos void emap_deregister_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
97 1.1 christos void emap_deregister_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
98 1.1 christos
99 1.1 christos typedef struct emap_prepare_s emap_prepare_t;
100 1.1 christos struct emap_prepare_s {
101 1.1 christos rtree_leaf_elm_t *lead_elm_a;
102 1.1 christos rtree_leaf_elm_t *lead_elm_b;
103 1.1 christos rtree_leaf_elm_t *trail_elm_a;
104 1.1 christos rtree_leaf_elm_t *trail_elm_b;
105 1.1 christos };
106 1.1 christos
107 1.1 christos /**
108 1.1 christos * These functions the emap metadata management for merging, splitting, and
109 1.1 christos * reusing extents. In particular, they set the boundary mappings from
110 1.1 christos * addresses to edatas. If the result is going to be used as a slab, you
111 1.1 christos * still need to call emap_register_interior on it, though.
112 1.1 christos *
113 1.1 christos * Remap simply changes the szind and slab status of an extent's boundary
114 1.1 christos * mappings. If the extent is not a slab, it doesn't bother with updating the
115 1.1 christos * end mapping (since lookups only occur in the interior of an extent for
116 1.1 christos * slabs). Since the szind and slab status only make sense for active extents,
117 1.1 christos * this should only be called while activating or deactivating an extent.
118 1.1 christos *
119 1.1 christos * Split and merge have a "prepare" and a "commit" portion. The prepare portion
120 1.1 christos * does the operations that can be done without exclusive access to the extent
121 1.1 christos * in question, while the commit variant requires exclusive access to maintain
122 1.1 christos * the emap invariants. The only function that can fail is emap_split_prepare,
123 1.1 christos * and it returns true on failure (at which point the caller shouldn't commit).
124 1.1 christos *
125 1.1 christos * In all cases, "lead" refers to the lower-addressed extent, and trail to the
126 1.1 christos * higher-addressed one. It's the caller's responsibility to set the edata
127 1.1 christos * state appropriately.
128 1.1 christos */
129 1.1 christos bool emap_split_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
130 1.1 christos edata_t *edata, size_t size_a, edata_t *trail, size_t size_b);
131 1.1 christos void emap_split_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
132 1.1 christos edata_t *lead, size_t size_a, edata_t *trail, size_t size_b);
133 1.1 christos void emap_merge_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
134 1.1 christos edata_t *lead, edata_t *trail);
135 1.1 christos void emap_merge_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
136 1.1 christos edata_t *lead, edata_t *trail);
137 1.1 christos
138 1.1 christos /* Assert that the emap's view of the given edata matches the edata's view. */
139 1.1 christos void emap_do_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
140 1.1 christos static inline void
141 1.1 christos emap_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
142 1.1 christos if (config_debug) {
143 1.1 christos emap_do_assert_mapped(tsdn, emap, edata);
144 1.1 christos }
145 1.1 christos }
146 1.1 christos
147 1.1 christos /* Assert that the given edata isn't in the map. */
148 1.1 christos void emap_do_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
149 1.1 christos static inline void
150 1.1 christos emap_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
151 1.1 christos if (config_debug) {
152 1.1 christos emap_do_assert_not_mapped(tsdn, emap, edata);
153 1.1 christos }
154 1.1 christos }
155 1.1 christos
156 1.1 christos JEMALLOC_ALWAYS_INLINE bool
157 1.1 christos emap_edata_in_transition(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
158 1.1 christos assert(config_debug);
159 1.1 christos emap_assert_mapped(tsdn, emap, edata);
160 1.1 christos
161 1.1 christos EMAP_DECLARE_RTREE_CTX;
162 1.1 christos rtree_contents_t contents = rtree_read(tsdn, &emap->rtree, rtree_ctx,
163 1.1 christos (uintptr_t)edata_base_get(edata));
164 1.1 christos
165 1.1 christos return edata_state_in_transition(contents.metadata.state);
166 1.1 christos }
167 1.1 christos
168 1.1 christos JEMALLOC_ALWAYS_INLINE bool
169 1.1 christos emap_edata_is_acquired(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
170 1.1 christos if (!config_debug) {
171 1.1 christos /* For assertions only. */
172 1.1 christos return false;
173 1.1 christos }
174 1.1 christos
175 1.1 christos /*
176 1.1 christos * The edata is considered acquired if no other threads will attempt to
177 1.1 christos * read / write any fields from it. This includes a few cases:
178 1.1 christos *
179 1.1 christos * 1) edata not hooked into emap yet -- This implies the edata just got
180 1.1 christos * allocated or initialized.
181 1.1 christos *
182 1.1 christos * 2) in an active or transition state -- In both cases, the edata can
183 1.1 christos * be discovered from the emap, however the state tracked in the rtree
184 1.1 christos * will prevent other threads from accessing the actual edata.
185 1.1 christos */
186 1.1 christos EMAP_DECLARE_RTREE_CTX;
187 1.1 christos rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &emap->rtree,
188 1.1 christos rtree_ctx, (uintptr_t)edata_base_get(edata), /* dependent */ true,
189 1.1 christos /* init_missing */ false);
190 1.1 christos if (elm == NULL) {
191 1.1 christos return true;
192 1.1 christos }
193 1.1 christos rtree_contents_t contents = rtree_leaf_elm_read(tsdn, &emap->rtree, elm,
194 1.1 christos /* dependent */ true);
195 1.1 christos if (contents.edata == NULL ||
196 1.1 christos contents.metadata.state == extent_state_active ||
197 1.1 christos edata_state_in_transition(contents.metadata.state)) {
198 1.1 christos return true;
199 1.1 christos }
200 1.1 christos
201 1.1 christos return false;
202 1.1 christos }
203 1.1 christos
204 1.1 christos JEMALLOC_ALWAYS_INLINE void
205 1.1 christos extent_assert_can_coalesce(const edata_t *inner, const edata_t *outer) {
206 1.1 christos assert(edata_arena_ind_get(inner) == edata_arena_ind_get(outer));
207 1.1 christos assert(edata_pai_get(inner) == edata_pai_get(outer));
208 1.1 christos assert(edata_committed_get(inner) == edata_committed_get(outer));
209 1.1 christos assert(edata_state_get(inner) == extent_state_active);
210 1.1 christos assert(edata_state_get(outer) == extent_state_merging);
211 1.1 christos assert(!edata_guarded_get(inner) && !edata_guarded_get(outer));
212 1.1 christos assert(edata_base_get(inner) == edata_past_get(outer) ||
213 1.1 christos edata_base_get(outer) == edata_past_get(inner));
214 1.1 christos }
215 1.1 christos
216 1.1 christos JEMALLOC_ALWAYS_INLINE void
217 1.1 christos extent_assert_can_expand(const edata_t *original, const edata_t *expand) {
218 1.1 christos assert(edata_arena_ind_get(original) == edata_arena_ind_get(expand));
219 1.1 christos assert(edata_pai_get(original) == edata_pai_get(expand));
220 1.1 christos assert(edata_state_get(original) == extent_state_active);
221 1.1 christos assert(edata_state_get(expand) == extent_state_merging);
222 1.1 christos assert(edata_past_get(original) == edata_base_get(expand));
223 1.1 christos }
224 1.1 christos
225 1.1 christos JEMALLOC_ALWAYS_INLINE edata_t *
226 1.1 christos emap_edata_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr) {
227 1.1 christos EMAP_DECLARE_RTREE_CTX;
228 1.1 christos
229 1.1 christos return rtree_read(tsdn, &emap->rtree, rtree_ctx, (uintptr_t)ptr).edata;
230 1.1 christos }
231 1.1 christos
232 1.1 christos /* Fills in alloc_ctx with the info in the map. */
233 1.1 christos JEMALLOC_ALWAYS_INLINE void
234 1.1 christos emap_alloc_ctx_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
235 1.1 christos emap_alloc_ctx_t *alloc_ctx) {
236 1.1 christos EMAP_DECLARE_RTREE_CTX;
237 1.1 christos
238 1.1 christos rtree_metadata_t metadata = rtree_metadata_read(tsdn, &emap->rtree,
239 1.1 christos rtree_ctx, (uintptr_t)ptr);
240 1.1 christos alloc_ctx->szind = metadata.szind;
241 1.1 christos alloc_ctx->slab = metadata.slab;
242 1.1 christos }
243 1.1 christos
244 1.1 christos /* The pointer must be mapped. */
245 1.1 christos JEMALLOC_ALWAYS_INLINE void
246 1.1 christos emap_full_alloc_ctx_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
247 1.1 christos emap_full_alloc_ctx_t *full_alloc_ctx) {
248 1.1 christos EMAP_DECLARE_RTREE_CTX;
249 1.1 christos
250 1.1 christos rtree_contents_t contents = rtree_read(tsdn, &emap->rtree, rtree_ctx,
251 1.1 christos (uintptr_t)ptr);
252 1.1 christos full_alloc_ctx->edata = contents.edata;
253 1.1 christos full_alloc_ctx->szind = contents.metadata.szind;
254 1.1 christos full_alloc_ctx->slab = contents.metadata.slab;
255 1.1 christos }
256 1.1 christos
257 1.1 christos /*
258 1.1 christos * The pointer is allowed to not be mapped.
259 1.1 christos *
260 1.1 christos * Returns true when the pointer is not present.
261 1.1 christos */
262 1.1 christos JEMALLOC_ALWAYS_INLINE bool
263 1.1 christos emap_full_alloc_ctx_try_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
264 1.1 christos emap_full_alloc_ctx_t *full_alloc_ctx) {
265 1.1 christos EMAP_DECLARE_RTREE_CTX;
266 1.1 christos
267 1.1 christos rtree_contents_t contents;
268 1.1 christos bool err = rtree_read_independent(tsdn, &emap->rtree, rtree_ctx,
269 1.1 christos (uintptr_t)ptr, &contents);
270 1.1 christos if (err) {
271 1.1 christos return true;
272 1.1 christos }
273 1.1 christos full_alloc_ctx->edata = contents.edata;
274 1.1 christos full_alloc_ctx->szind = contents.metadata.szind;
275 1.1 christos full_alloc_ctx->slab = contents.metadata.slab;
276 1.1 christos return false;
277 1.1 christos }
278 1.1 christos
279 1.1 christos /*
280 1.1 christos * Only used on the fastpath of free. Returns true when cannot be fulfilled by
281 1.1 christos * fast path, e.g. when the metadata key is not cached.
282 1.1 christos */
283 1.1 christos JEMALLOC_ALWAYS_INLINE bool
284 1.1 christos emap_alloc_ctx_try_lookup_fast(tsd_t *tsd, emap_t *emap, const void *ptr,
285 1.1 christos emap_alloc_ctx_t *alloc_ctx) {
286 1.1 christos /* Use the unsafe getter since this may gets called during exit. */
287 1.1 christos rtree_ctx_t *rtree_ctx = tsd_rtree_ctxp_get_unsafe(tsd);
288 1.1 christos
289 1.1 christos rtree_metadata_t metadata;
290 1.1 christos bool err = rtree_metadata_try_read_fast(tsd_tsdn(tsd), &emap->rtree,
291 1.1 christos rtree_ctx, (uintptr_t)ptr, &metadata);
292 1.1 christos if (err) {
293 1.1 christos return true;
294 1.1 christos }
295 1.1 christos alloc_ctx->szind = metadata.szind;
296 1.1 christos alloc_ctx->slab = metadata.slab;
297 1.1 christos return false;
298 1.1 christos }
299 1.1 christos
300 1.1 christos /*
301 1.1 christos * We want to do batch lookups out of the cache bins, which use
302 1.1 christos * cache_bin_ptr_array_get to access the i'th element of the bin (since they
303 1.1 christos * invert usual ordering in deciding what to flush). This lets the emap avoid
304 1.1 christos * caring about its caller's ordering.
305 1.1 christos */
306 1.1 christos typedef const void *(*emap_ptr_getter)(void *ctx, size_t ind);
307 1.1 christos /*
308 1.1 christos * This allows size-checking assertions, which we can only do while we're in the
309 1.1 christos * process of edata lookups.
310 1.1 christos */
311 1.1 christos typedef void (*emap_metadata_visitor)(void *ctx, emap_full_alloc_ctx_t *alloc_ctx);
312 1.1 christos
313 1.1 christos typedef union emap_batch_lookup_result_u emap_batch_lookup_result_t;
314 1.1 christos union emap_batch_lookup_result_u {
315 1.1 christos edata_t *edata;
316 1.1 christos rtree_leaf_elm_t *rtree_leaf;
317 1.1 christos };
318 1.1 christos
319 1.1 christos JEMALLOC_ALWAYS_INLINE void
320 1.1 christos emap_edata_lookup_batch(tsd_t *tsd, emap_t *emap, size_t nptrs,
321 1.1 christos emap_ptr_getter ptr_getter, void *ptr_getter_ctx,
322 1.1 christos emap_metadata_visitor metadata_visitor, void *metadata_visitor_ctx,
323 1.1 christos emap_batch_lookup_result_t *result) {
324 1.1 christos /* Avoids null-checking tsdn in the loop below. */
325 1.1 christos util_assume(tsd != NULL);
326 1.1 christos rtree_ctx_t *rtree_ctx = tsd_rtree_ctxp_get(tsd);
327 1.1 christos
328 1.1 christos for (size_t i = 0; i < nptrs; i++) {
329 1.1 christos const void *ptr = ptr_getter(ptr_getter_ctx, i);
330 1.1 christos /*
331 1.1 christos * Reuse the edatas array as a temp buffer, lying a little about
332 1.1 christos * the types.
333 1.1 christos */
334 1.1 christos result[i].rtree_leaf = rtree_leaf_elm_lookup(tsd_tsdn(tsd),
335 1.1 christos &emap->rtree, rtree_ctx, (uintptr_t)ptr,
336 1.1 christos /* dependent */ true, /* init_missing */ false);
337 1.1 christos }
338 1.1 christos
339 1.1 christos for (size_t i = 0; i < nptrs; i++) {
340 1.1 christos rtree_leaf_elm_t *elm = result[i].rtree_leaf;
341 1.1 christos rtree_contents_t contents = rtree_leaf_elm_read(tsd_tsdn(tsd),
342 1.1 christos &emap->rtree, elm, /* dependent */ true);
343 1.1 christos result[i].edata = contents.edata;
344 1.1 christos emap_full_alloc_ctx_t alloc_ctx;
345 1.1 christos /*
346 1.1 christos * Not all these fields are read in practice by the metadata
347 1.1 christos * visitor. But the compiler can easily optimize away the ones
348 1.1 christos * that aren't, so no sense in being incomplete.
349 1.1 christos */
350 1.1 christos alloc_ctx.szind = contents.metadata.szind;
351 1.1 christos alloc_ctx.slab = contents.metadata.slab;
352 1.1 christos alloc_ctx.edata = contents.edata;
353 1.1 christos metadata_visitor(metadata_visitor_ctx, &alloc_ctx);
354 1.1 christos }
355 1.1 christos }
356 1.1 christos
357 1.1 christos #endif /* JEMALLOC_INTERNAL_EMAP_H */
358