Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/jemalloc/src/extent.c
39483 views
1
#include "jemalloc/internal/jemalloc_preamble.h"
2
#include "jemalloc/internal/jemalloc_internal_includes.h"
3
4
#include "jemalloc/internal/assert.h"
5
#include "jemalloc/internal/emap.h"
6
#include "jemalloc/internal/extent_dss.h"
7
#include "jemalloc/internal/extent_mmap.h"
8
#include "jemalloc/internal/ph.h"
9
#include "jemalloc/internal/mutex.h"
10
11
/******************************************************************************/
12
/* Data. */
13
14
size_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT;
15
16
static bool extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
17
size_t offset, size_t length, bool growing_retained);
18
static bool extent_purge_lazy_impl(tsdn_t *tsdn, ehooks_t *ehooks,
19
edata_t *edata, size_t offset, size_t length, bool growing_retained);
20
static bool extent_purge_forced_impl(tsdn_t *tsdn, ehooks_t *ehooks,
21
edata_t *edata, size_t offset, size_t length, bool growing_retained);
22
static edata_t *extent_split_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
23
edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks);
24
static bool extent_merge_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
25
edata_t *a, edata_t *b, bool holding_core_locks);
26
27
/* Used exclusively for gdump triggering. */
28
static atomic_zu_t curpages;
29
static atomic_zu_t highpages;
30
31
/******************************************************************************/
32
/*
33
* Function prototypes for static functions that are referenced prior to
34
* definition.
35
*/
36
37
static void extent_deregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata);
38
static edata_t *extent_recycle(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
39
ecache_t *ecache, edata_t *expand_edata, size_t usize, size_t alignment,
40
bool zero, bool *commit, bool growing_retained, bool guarded);
41
static edata_t *extent_try_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
42
ecache_t *ecache, edata_t *edata, bool *coalesced);
43
static edata_t *extent_alloc_retained(tsdn_t *tsdn, pac_t *pac,
44
ehooks_t *ehooks, edata_t *expand_edata, size_t size, size_t alignment,
45
bool zero, bool *commit, bool guarded);
46
47
/******************************************************************************/
48
49
size_t
50
extent_sn_next(pac_t *pac) {
51
return atomic_fetch_add_zu(&pac->extent_sn_next, 1, ATOMIC_RELAXED);
52
}
53
54
static inline bool
55
extent_may_force_decay(pac_t *pac) {
56
return !(pac_decay_ms_get(pac, extent_state_dirty) == -1
57
|| pac_decay_ms_get(pac, extent_state_muzzy) == -1);
58
}
59
60
static bool
61
extent_try_delayed_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
62
ecache_t *ecache, edata_t *edata) {
63
emap_update_edata_state(tsdn, pac->emap, edata, extent_state_active);
64
65
bool coalesced;
66
edata = extent_try_coalesce(tsdn, pac, ehooks, ecache,
67
edata, &coalesced);
68
emap_update_edata_state(tsdn, pac->emap, edata, ecache->state);
69
70
if (!coalesced) {
71
return true;
72
}
73
eset_insert(&ecache->eset, edata);
74
return false;
75
}
76
77
edata_t *
78
ecache_alloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
79
edata_t *expand_edata, size_t size, size_t alignment, bool zero,
80
bool guarded) {
81
assert(size != 0);
82
assert(alignment != 0);
83
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
84
WITNESS_RANK_CORE, 0);
85
86
bool commit = true;
87
edata_t *edata = extent_recycle(tsdn, pac, ehooks, ecache, expand_edata,
88
size, alignment, zero, &commit, false, guarded);
89
assert(edata == NULL || edata_pai_get(edata) == EXTENT_PAI_PAC);
90
assert(edata == NULL || edata_guarded_get(edata) == guarded);
91
return edata;
92
}
93
94
edata_t *
95
ecache_alloc_grow(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
96
edata_t *expand_edata, size_t size, size_t alignment, bool zero,
97
bool guarded) {
98
assert(size != 0);
99
assert(alignment != 0);
100
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
101
WITNESS_RANK_CORE, 0);
102
103
bool commit = true;
104
edata_t *edata = extent_alloc_retained(tsdn, pac, ehooks, expand_edata,
105
size, alignment, zero, &commit, guarded);
106
if (edata == NULL) {
107
if (opt_retain && expand_edata != NULL) {
108
/*
109
* When retain is enabled and trying to expand, we do
110
* not attempt extent_alloc_wrapper which does mmap that
111
* is very unlikely to succeed (unless it happens to be
112
* at the end).
113
*/
114
return NULL;
115
}
116
if (guarded) {
117
/*
118
* Means no cached guarded extents available (and no
119
* grow_retained was attempted). The pac_alloc flow
120
* will alloc regular extents to make new guarded ones.
121
*/
122
return NULL;
123
}
124
void *new_addr = (expand_edata == NULL) ? NULL :
125
edata_past_get(expand_edata);
126
edata = extent_alloc_wrapper(tsdn, pac, ehooks, new_addr,
127
size, alignment, zero, &commit,
128
/* growing_retained */ false);
129
}
130
131
assert(edata == NULL || edata_pai_get(edata) == EXTENT_PAI_PAC);
132
return edata;
133
}
134
135
void
136
ecache_dalloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
137
edata_t *edata) {
138
assert(edata_base_get(edata) != NULL);
139
assert(edata_size_get(edata) != 0);
140
assert(edata_pai_get(edata) == EXTENT_PAI_PAC);
141
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
142
WITNESS_RANK_CORE, 0);
143
144
edata_addr_set(edata, edata_base_get(edata));
145
edata_zeroed_set(edata, false);
146
147
extent_record(tsdn, pac, ehooks, ecache, edata);
148
}
149
150
edata_t *
151
ecache_evict(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
152
ecache_t *ecache, size_t npages_min) {
153
malloc_mutex_lock(tsdn, &ecache->mtx);
154
155
/*
156
* Get the LRU coalesced extent, if any. If coalescing was delayed,
157
* the loop will iterate until the LRU extent is fully coalesced.
158
*/
159
edata_t *edata;
160
while (true) {
161
/* Get the LRU extent, if any. */
162
eset_t *eset = &ecache->eset;
163
edata = edata_list_inactive_first(&eset->lru);
164
if (edata == NULL) {
165
/*
166
* Next check if there are guarded extents. They are
167
* more expensive to purge (since they are not
168
* mergeable), thus in favor of caching them longer.
169
*/
170
eset = &ecache->guarded_eset;
171
edata = edata_list_inactive_first(&eset->lru);
172
if (edata == NULL) {
173
goto label_return;
174
}
175
}
176
/* Check the eviction limit. */
177
size_t extents_npages = ecache_npages_get(ecache);
178
if (extents_npages <= npages_min) {
179
edata = NULL;
180
goto label_return;
181
}
182
eset_remove(eset, edata);
183
if (!ecache->delay_coalesce || edata_guarded_get(edata)) {
184
break;
185
}
186
/* Try to coalesce. */
187
if (extent_try_delayed_coalesce(tsdn, pac, ehooks, ecache,
188
edata)) {
189
break;
190
}
191
/*
192
* The LRU extent was just coalesced and the result placed in
193
* the LRU at its neighbor's position. Start over.
194
*/
195
}
196
197
/*
198
* Either mark the extent active or deregister it to protect against
199
* concurrent operations.
200
*/
201
switch (ecache->state) {
202
case extent_state_active:
203
not_reached();
204
case extent_state_dirty:
205
case extent_state_muzzy:
206
emap_update_edata_state(tsdn, pac->emap, edata,
207
extent_state_active);
208
break;
209
case extent_state_retained:
210
extent_deregister(tsdn, pac, edata);
211
break;
212
default:
213
not_reached();
214
}
215
216
label_return:
217
malloc_mutex_unlock(tsdn, &ecache->mtx);
218
return edata;
219
}
220
221
/*
222
* This can only happen when we fail to allocate a new extent struct (which
223
* indicates OOM), e.g. when trying to split an existing extent.
224
*/
225
static void
226
extents_abandon_vm(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
227
edata_t *edata, bool growing_retained) {
228
size_t sz = edata_size_get(edata);
229
if (config_stats) {
230
atomic_fetch_add_zu(&pac->stats->abandoned_vm, sz,
231
ATOMIC_RELAXED);
232
}
233
/*
234
* Leak extent after making sure its pages have already been purged, so
235
* that this is only a virtual memory leak.
236
*/
237
if (ecache->state == extent_state_dirty) {
238
if (extent_purge_lazy_impl(tsdn, ehooks, edata, 0, sz,
239
growing_retained)) {
240
extent_purge_forced_impl(tsdn, ehooks, edata, 0,
241
edata_size_get(edata), growing_retained);
242
}
243
}
244
edata_cache_put(tsdn, pac->edata_cache, edata);
245
}
246
247
static void
248
extent_deactivate_locked_impl(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
249
edata_t *edata) {
250
malloc_mutex_assert_owner(tsdn, &ecache->mtx);
251
assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache));
252
253
emap_update_edata_state(tsdn, pac->emap, edata, ecache->state);
254
eset_t *eset = edata_guarded_get(edata) ? &ecache->guarded_eset :
255
&ecache->eset;
256
eset_insert(eset, edata);
257
}
258
259
static void
260
extent_deactivate_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
261
edata_t *edata) {
262
assert(edata_state_get(edata) == extent_state_active);
263
extent_deactivate_locked_impl(tsdn, pac, ecache, edata);
264
}
265
266
static void
267
extent_deactivate_check_state_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
268
edata_t *edata, extent_state_t expected_state) {
269
assert(edata_state_get(edata) == expected_state);
270
extent_deactivate_locked_impl(tsdn, pac, ecache, edata);
271
}
272
273
static void
274
extent_activate_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, eset_t *eset,
275
edata_t *edata) {
276
assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache));
277
assert(edata_state_get(edata) == ecache->state ||
278
edata_state_get(edata) == extent_state_merging);
279
280
eset_remove(eset, edata);
281
emap_update_edata_state(tsdn, pac->emap, edata, extent_state_active);
282
}
283
284
void
285
extent_gdump_add(tsdn_t *tsdn, const edata_t *edata) {
286
cassert(config_prof);
287
/* prof_gdump() requirement. */
288
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
289
WITNESS_RANK_CORE, 0);
290
291
if (opt_prof && edata_state_get(edata) == extent_state_active) {
292
size_t nadd = edata_size_get(edata) >> LG_PAGE;
293
size_t cur = atomic_fetch_add_zu(&curpages, nadd,
294
ATOMIC_RELAXED) + nadd;
295
size_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED);
296
while (cur > high && !atomic_compare_exchange_weak_zu(
297
&highpages, &high, cur, ATOMIC_RELAXED, ATOMIC_RELAXED)) {
298
/*
299
* Don't refresh cur, because it may have decreased
300
* since this thread lost the highpages update race.
301
* Note that high is updated in case of CAS failure.
302
*/
303
}
304
if (cur > high && prof_gdump_get_unlocked()) {
305
prof_gdump(tsdn);
306
}
307
}
308
}
309
310
static void
311
extent_gdump_sub(tsdn_t *tsdn, const edata_t *edata) {
312
cassert(config_prof);
313
314
if (opt_prof && edata_state_get(edata) == extent_state_active) {
315
size_t nsub = edata_size_get(edata) >> LG_PAGE;
316
assert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub);
317
atomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED);
318
}
319
}
320
321
static bool
322
extent_register_impl(tsdn_t *tsdn, pac_t *pac, edata_t *edata, bool gdump_add) {
323
assert(edata_state_get(edata) == extent_state_active);
324
/*
325
* No locking needed, as the edata must be in active state, which
326
* prevents other threads from accessing the edata.
327
*/
328
if (emap_register_boundary(tsdn, pac->emap, edata, SC_NSIZES,
329
/* slab */ false)) {
330
return true;
331
}
332
333
if (config_prof && gdump_add) {
334
extent_gdump_add(tsdn, edata);
335
}
336
337
return false;
338
}
339
340
static bool
341
extent_register(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
342
return extent_register_impl(tsdn, pac, edata, true);
343
}
344
345
static bool
346
extent_register_no_gdump_add(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
347
return extent_register_impl(tsdn, pac, edata, false);
348
}
349
350
static void
351
extent_reregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
352
bool err = extent_register(tsdn, pac, edata);
353
assert(!err);
354
}
355
356
/*
357
* Removes all pointers to the given extent from the global rtree.
358
*/
359
static void
360
extent_deregister_impl(tsdn_t *tsdn, pac_t *pac, edata_t *edata,
361
bool gdump) {
362
emap_deregister_boundary(tsdn, pac->emap, edata);
363
364
if (config_prof && gdump) {
365
extent_gdump_sub(tsdn, edata);
366
}
367
}
368
369
static void
370
extent_deregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
371
extent_deregister_impl(tsdn, pac, edata, true);
372
}
373
374
static void
375
extent_deregister_no_gdump_sub(tsdn_t *tsdn, pac_t *pac,
376
edata_t *edata) {
377
extent_deregister_impl(tsdn, pac, edata, false);
378
}
379
380
/*
381
* Tries to find and remove an extent from ecache that can be used for the
382
* given allocation request.
383
*/
384
static edata_t *
385
extent_recycle_extract(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
386
ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
387
bool guarded) {
388
malloc_mutex_assert_owner(tsdn, &ecache->mtx);
389
assert(alignment > 0);
390
if (config_debug && expand_edata != NULL) {
391
/*
392
* Non-NULL expand_edata indicates in-place expanding realloc.
393
* new_addr must either refer to a non-existing extent, or to
394
* the base of an extant extent, since only active slabs support
395
* interior lookups (which of course cannot be recycled).
396
*/
397
void *new_addr = edata_past_get(expand_edata);
398
assert(PAGE_ADDR2BASE(new_addr) == new_addr);
399
assert(alignment <= PAGE);
400
}
401
402
edata_t *edata;
403
eset_t *eset = guarded ? &ecache->guarded_eset : &ecache->eset;
404
if (expand_edata != NULL) {
405
edata = emap_try_acquire_edata_neighbor_expand(tsdn, pac->emap,
406
expand_edata, EXTENT_PAI_PAC, ecache->state);
407
if (edata != NULL) {
408
extent_assert_can_expand(expand_edata, edata);
409
if (edata_size_get(edata) < size) {
410
emap_release_edata(tsdn, pac->emap, edata,
411
ecache->state);
412
edata = NULL;
413
}
414
}
415
} else {
416
/*
417
* A large extent might be broken up from its original size to
418
* some small size to satisfy a small request. When that small
419
* request is freed, though, it won't merge back with the larger
420
* extent if delayed coalescing is on. The large extent can
421
* then no longer satify a request for its original size. To
422
* limit this effect, when delayed coalescing is enabled, we
423
* put a cap on how big an extent we can split for a request.
424
*/
425
unsigned lg_max_fit = ecache->delay_coalesce
426
? (unsigned)opt_lg_extent_max_active_fit : SC_PTR_BITS;
427
428
/*
429
* If split and merge are not allowed (Windows w/o retain), try
430
* exact fit only.
431
*
432
* For simplicity purposes, splitting guarded extents is not
433
* supported. Hence, we do only exact fit for guarded
434
* allocations.
435
*/
436
bool exact_only = (!maps_coalesce && !opt_retain) || guarded;
437
edata = eset_fit(eset, size, alignment, exact_only,
438
lg_max_fit);
439
}
440
if (edata == NULL) {
441
return NULL;
442
}
443
assert(!guarded || edata_guarded_get(edata));
444
extent_activate_locked(tsdn, pac, ecache, eset, edata);
445
446
return edata;
447
}
448
449
/*
450
* Given an allocation request and an extent guaranteed to be able to satisfy
451
* it, this splits off lead and trail extents, leaving edata pointing to an
452
* extent satisfying the allocation.
453
* This function doesn't put lead or trail into any ecache; it's the caller's
454
* job to ensure that they can be reused.
455
*/
456
typedef enum {
457
/*
458
* Split successfully. lead, edata, and trail, are modified to extents
459
* describing the ranges before, in, and after the given allocation.
460
*/
461
extent_split_interior_ok,
462
/*
463
* The extent can't satisfy the given allocation request. None of the
464
* input edata_t *s are touched.
465
*/
466
extent_split_interior_cant_alloc,
467
/*
468
* In a potentially invalid state. Must leak (if *to_leak is non-NULL),
469
* and salvage what's still salvageable (if *to_salvage is non-NULL).
470
* None of lead, edata, or trail are valid.
471
*/
472
extent_split_interior_error
473
} extent_split_interior_result_t;
474
475
static extent_split_interior_result_t
476
extent_split_interior(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
477
/* The result of splitting, in case of success. */
478
edata_t **edata, edata_t **lead, edata_t **trail,
479
/* The mess to clean up, in case of error. */
480
edata_t **to_leak, edata_t **to_salvage,
481
edata_t *expand_edata, size_t size, size_t alignment) {
482
size_t leadsize = ALIGNMENT_CEILING((uintptr_t)edata_base_get(*edata),
483
PAGE_CEILING(alignment)) - (uintptr_t)edata_base_get(*edata);
484
assert(expand_edata == NULL || leadsize == 0);
485
if (edata_size_get(*edata) < leadsize + size) {
486
return extent_split_interior_cant_alloc;
487
}
488
size_t trailsize = edata_size_get(*edata) - leadsize - size;
489
490
*lead = NULL;
491
*trail = NULL;
492
*to_leak = NULL;
493
*to_salvage = NULL;
494
495
/* Split the lead. */
496
if (leadsize != 0) {
497
assert(!edata_guarded_get(*edata));
498
*lead = *edata;
499
*edata = extent_split_impl(tsdn, pac, ehooks, *lead, leadsize,
500
size + trailsize, /* holding_core_locks*/ true);
501
if (*edata == NULL) {
502
*to_leak = *lead;
503
*lead = NULL;
504
return extent_split_interior_error;
505
}
506
}
507
508
/* Split the trail. */
509
if (trailsize != 0) {
510
assert(!edata_guarded_get(*edata));
511
*trail = extent_split_impl(tsdn, pac, ehooks, *edata, size,
512
trailsize, /* holding_core_locks */ true);
513
if (*trail == NULL) {
514
*to_leak = *edata;
515
*to_salvage = *lead;
516
*lead = NULL;
517
*edata = NULL;
518
return extent_split_interior_error;
519
}
520
}
521
522
return extent_split_interior_ok;
523
}
524
525
/*
526
* This fulfills the indicated allocation request out of the given extent (which
527
* the caller should have ensured was big enough). If there's any unused space
528
* before or after the resulting allocation, that space is given its own extent
529
* and put back into ecache.
530
*/
531
static edata_t *
532
extent_recycle_split(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
533
ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
534
edata_t *edata, bool growing_retained) {
535
assert(!edata_guarded_get(edata) || size == edata_size_get(edata));
536
malloc_mutex_assert_owner(tsdn, &ecache->mtx);
537
538
edata_t *lead;
539
edata_t *trail;
540
edata_t *to_leak JEMALLOC_CC_SILENCE_INIT(NULL);
541
edata_t *to_salvage JEMALLOC_CC_SILENCE_INIT(NULL);
542
543
extent_split_interior_result_t result = extent_split_interior(
544
tsdn, pac, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage,
545
expand_edata, size, alignment);
546
547
if (!maps_coalesce && result != extent_split_interior_ok
548
&& !opt_retain) {
549
/*
550
* Split isn't supported (implies Windows w/o retain). Avoid
551
* leaking the extent.
552
*/
553
assert(to_leak != NULL && lead == NULL && trail == NULL);
554
extent_deactivate_locked(tsdn, pac, ecache, to_leak);
555
return NULL;
556
}
557
558
if (result == extent_split_interior_ok) {
559
if (lead != NULL) {
560
extent_deactivate_locked(tsdn, pac, ecache, lead);
561
}
562
if (trail != NULL) {
563
extent_deactivate_locked(tsdn, pac, ecache, trail);
564
}
565
return edata;
566
} else {
567
/*
568
* We should have picked an extent that was large enough to
569
* fulfill our allocation request.
570
*/
571
assert(result == extent_split_interior_error);
572
if (to_salvage != NULL) {
573
extent_deregister(tsdn, pac, to_salvage);
574
}
575
if (to_leak != NULL) {
576
extent_deregister_no_gdump_sub(tsdn, pac, to_leak);
577
/*
578
* May go down the purge path (which assume no ecache
579
* locks). Only happens with OOM caused split failures.
580
*/
581
malloc_mutex_unlock(tsdn, &ecache->mtx);
582
extents_abandon_vm(tsdn, pac, ehooks, ecache, to_leak,
583
growing_retained);
584
malloc_mutex_lock(tsdn, &ecache->mtx);
585
}
586
return NULL;
587
}
588
unreachable();
589
}
590
591
/*
592
* Tries to satisfy the given allocation request by reusing one of the extents
593
* in the given ecache_t.
594
*/
595
static edata_t *
596
extent_recycle(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
597
edata_t *expand_edata, size_t size, size_t alignment, bool zero,
598
bool *commit, bool growing_retained, bool guarded) {
599
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
600
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
601
assert(!guarded || expand_edata == NULL);
602
assert(!guarded || alignment <= PAGE);
603
604
malloc_mutex_lock(tsdn, &ecache->mtx);
605
606
edata_t *edata = extent_recycle_extract(tsdn, pac, ehooks, ecache,
607
expand_edata, size, alignment, guarded);
608
if (edata == NULL) {
609
malloc_mutex_unlock(tsdn, &ecache->mtx);
610
return NULL;
611
}
612
613
edata = extent_recycle_split(tsdn, pac, ehooks, ecache, expand_edata,
614
size, alignment, edata, growing_retained);
615
malloc_mutex_unlock(tsdn, &ecache->mtx);
616
if (edata == NULL) {
617
return NULL;
618
}
619
620
assert(edata_state_get(edata) == extent_state_active);
621
if (extent_commit_zero(tsdn, ehooks, edata, *commit, zero,
622
growing_retained)) {
623
extent_record(tsdn, pac, ehooks, ecache, edata);
624
return NULL;
625
}
626
if (edata_committed_get(edata)) {
627
/*
628
* This reverses the purpose of this variable - previously it
629
* was treated as an input parameter, now it turns into an
630
* output parameter, reporting if the edata has actually been
631
* committed.
632
*/
633
*commit = true;
634
}
635
return edata;
636
}
637
638
/*
639
* If virtual memory is retained, create increasingly larger extents from which
640
* to split requested extents in order to limit the total number of disjoint
641
* virtual memory ranges retained by each shard.
642
*/
643
static edata_t *
644
extent_grow_retained(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
645
size_t size, size_t alignment, bool zero, bool *commit) {
646
malloc_mutex_assert_owner(tsdn, &pac->grow_mtx);
647
648
size_t alloc_size_min = size + PAGE_CEILING(alignment) - PAGE;
649
/* Beware size_t wrap-around. */
650
if (alloc_size_min < size) {
651
goto label_err;
652
}
653
/*
654
* Find the next extent size in the series that would be large enough to
655
* satisfy this request.
656
*/
657
size_t alloc_size;
658
pszind_t exp_grow_skip;
659
bool err = exp_grow_size_prepare(&pac->exp_grow, alloc_size_min,
660
&alloc_size, &exp_grow_skip);
661
if (err) {
662
goto label_err;
663
}
664
665
edata_t *edata = edata_cache_get(tsdn, pac->edata_cache);
666
if (edata == NULL) {
667
goto label_err;
668
}
669
bool zeroed = false;
670
bool committed = false;
671
672
void *ptr = ehooks_alloc(tsdn, ehooks, NULL, alloc_size, PAGE, &zeroed,
673
&committed);
674
675
if (ptr == NULL) {
676
edata_cache_put(tsdn, pac->edata_cache, edata);
677
goto label_err;
678
}
679
680
edata_init(edata, ecache_ind_get(&pac->ecache_retained), ptr,
681
alloc_size, false, SC_NSIZES, extent_sn_next(pac),
682
extent_state_active, zeroed, committed, EXTENT_PAI_PAC,
683
EXTENT_IS_HEAD);
684
685
if (extent_register_no_gdump_add(tsdn, pac, edata)) {
686
edata_cache_put(tsdn, pac->edata_cache, edata);
687
goto label_err;
688
}
689
690
if (edata_committed_get(edata)) {
691
*commit = true;
692
}
693
694
edata_t *lead;
695
edata_t *trail;
696
edata_t *to_leak JEMALLOC_CC_SILENCE_INIT(NULL);
697
edata_t *to_salvage JEMALLOC_CC_SILENCE_INIT(NULL);
698
699
extent_split_interior_result_t result = extent_split_interior(tsdn,
700
pac, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage, NULL,
701
size, alignment);
702
703
if (result == extent_split_interior_ok) {
704
if (lead != NULL) {
705
extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
706
lead);
707
}
708
if (trail != NULL) {
709
extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
710
trail);
711
}
712
} else {
713
/*
714
* We should have allocated a sufficiently large extent; the
715
* cant_alloc case should not occur.
716
*/
717
assert(result == extent_split_interior_error);
718
if (to_salvage != NULL) {
719
if (config_prof) {
720
extent_gdump_add(tsdn, to_salvage);
721
}
722
extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
723
to_salvage);
724
}
725
if (to_leak != NULL) {
726
extent_deregister_no_gdump_sub(tsdn, pac, to_leak);
727
extents_abandon_vm(tsdn, pac, ehooks,
728
&pac->ecache_retained, to_leak, true);
729
}
730
goto label_err;
731
}
732
733
if (*commit && !edata_committed_get(edata)) {
734
if (extent_commit_impl(tsdn, ehooks, edata, 0,
735
edata_size_get(edata), true)) {
736
extent_record(tsdn, pac, ehooks,
737
&pac->ecache_retained, edata);
738
goto label_err;
739
}
740
/* A successful commit should return zeroed memory. */
741
if (config_debug) {
742
void *addr = edata_addr_get(edata);
743
size_t *p = (size_t *)(uintptr_t)addr;
744
/* Check the first page only. */
745
for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
746
assert(p[i] == 0);
747
}
748
}
749
}
750
751
/*
752
* Increment extent_grow_next if doing so wouldn't exceed the allowed
753
* range.
754
*/
755
/* All opportunities for failure are past. */
756
exp_grow_size_commit(&pac->exp_grow, exp_grow_skip);
757
malloc_mutex_unlock(tsdn, &pac->grow_mtx);
758
759
if (config_prof) {
760
/* Adjust gdump stats now that extent is final size. */
761
extent_gdump_add(tsdn, edata);
762
}
763
if (zero && !edata_zeroed_get(edata)) {
764
ehooks_zero(tsdn, ehooks, edata_base_get(edata),
765
edata_size_get(edata));
766
}
767
return edata;
768
label_err:
769
malloc_mutex_unlock(tsdn, &pac->grow_mtx);
770
return NULL;
771
}
772
773
static edata_t *
774
extent_alloc_retained(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
775
edata_t *expand_edata, size_t size, size_t alignment, bool zero,
776
bool *commit, bool guarded) {
777
assert(size != 0);
778
assert(alignment != 0);
779
780
malloc_mutex_lock(tsdn, &pac->grow_mtx);
781
782
edata_t *edata = extent_recycle(tsdn, pac, ehooks,
783
&pac->ecache_retained, expand_edata, size, alignment, zero, commit,
784
/* growing_retained */ true, guarded);
785
if (edata != NULL) {
786
malloc_mutex_unlock(tsdn, &pac->grow_mtx);
787
if (config_prof) {
788
extent_gdump_add(tsdn, edata);
789
}
790
} else if (opt_retain && expand_edata == NULL && !guarded) {
791
edata = extent_grow_retained(tsdn, pac, ehooks, size,
792
alignment, zero, commit);
793
/* extent_grow_retained() always releases pac->grow_mtx. */
794
} else {
795
malloc_mutex_unlock(tsdn, &pac->grow_mtx);
796
}
797
malloc_mutex_assert_not_owner(tsdn, &pac->grow_mtx);
798
799
return edata;
800
}
801
802
static bool
803
extent_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
804
edata_t *inner, edata_t *outer, bool forward) {
805
extent_assert_can_coalesce(inner, outer);
806
eset_remove(&ecache->eset, outer);
807
808
bool err = extent_merge_impl(tsdn, pac, ehooks,
809
forward ? inner : outer, forward ? outer : inner,
810
/* holding_core_locks */ true);
811
if (err) {
812
extent_deactivate_check_state_locked(tsdn, pac, ecache, outer,
813
extent_state_merging);
814
}
815
816
return err;
817
}
818
819
static edata_t *
820
extent_try_coalesce_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
821
ecache_t *ecache, edata_t *edata, bool *coalesced) {
822
assert(!edata_guarded_get(edata));
823
/*
824
* We avoid checking / locking inactive neighbors for large size
825
* classes, since they are eagerly coalesced on deallocation which can
826
* cause lock contention.
827
*/
828
/*
829
* Continue attempting to coalesce until failure, to protect against
830
* races with other threads that are thwarted by this one.
831
*/
832
bool again;
833
do {
834
again = false;
835
836
/* Try to coalesce forward. */
837
edata_t *next = emap_try_acquire_edata_neighbor(tsdn, pac->emap,
838
edata, EXTENT_PAI_PAC, ecache->state, /* forward */ true);
839
if (next != NULL) {
840
if (!extent_coalesce(tsdn, pac, ehooks, ecache, edata,
841
next, true)) {
842
if (ecache->delay_coalesce) {
843
/* Do minimal coalescing. */
844
*coalesced = true;
845
return edata;
846
}
847
again = true;
848
}
849
}
850
851
/* Try to coalesce backward. */
852
edata_t *prev = emap_try_acquire_edata_neighbor(tsdn, pac->emap,
853
edata, EXTENT_PAI_PAC, ecache->state, /* forward */ false);
854
if (prev != NULL) {
855
if (!extent_coalesce(tsdn, pac, ehooks, ecache, edata,
856
prev, false)) {
857
edata = prev;
858
if (ecache->delay_coalesce) {
859
/* Do minimal coalescing. */
860
*coalesced = true;
861
return edata;
862
}
863
again = true;
864
}
865
}
866
} while (again);
867
868
if (ecache->delay_coalesce) {
869
*coalesced = false;
870
}
871
return edata;
872
}
873
874
static edata_t *
875
extent_try_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
876
ecache_t *ecache, edata_t *edata, bool *coalesced) {
877
return extent_try_coalesce_impl(tsdn, pac, ehooks, ecache, edata,
878
coalesced);
879
}
880
881
static edata_t *
882
extent_try_coalesce_large(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
883
ecache_t *ecache, edata_t *edata, bool *coalesced) {
884
return extent_try_coalesce_impl(tsdn, pac, ehooks, ecache, edata,
885
coalesced);
886
}
887
888
/* Purge a single extent to retained / unmapped directly. */
889
static void
890
extent_maximally_purge(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
891
edata_t *edata) {
892
size_t extent_size = edata_size_get(edata);
893
extent_dalloc_wrapper(tsdn, pac, ehooks, edata);
894
if (config_stats) {
895
/* Update stats accordingly. */
896
LOCKEDINT_MTX_LOCK(tsdn, *pac->stats_mtx);
897
locked_inc_u64(tsdn,
898
LOCKEDINT_MTX(*pac->stats_mtx),
899
&pac->stats->decay_dirty.nmadvise, 1);
900
locked_inc_u64(tsdn,
901
LOCKEDINT_MTX(*pac->stats_mtx),
902
&pac->stats->decay_dirty.purged,
903
extent_size >> LG_PAGE);
904
LOCKEDINT_MTX_UNLOCK(tsdn, *pac->stats_mtx);
905
atomic_fetch_sub_zu(&pac->stats->pac_mapped, extent_size,
906
ATOMIC_RELAXED);
907
}
908
}
909
910
/*
911
* Does the metadata management portions of putting an unused extent into the
912
* given ecache_t (coalesces and inserts into the eset).
913
*/
914
void
915
extent_record(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
916
edata_t *edata) {
917
assert((ecache->state != extent_state_dirty &&
918
ecache->state != extent_state_muzzy) ||
919
!edata_zeroed_get(edata));
920
921
malloc_mutex_lock(tsdn, &ecache->mtx);
922
923
emap_assert_mapped(tsdn, pac->emap, edata);
924
925
if (edata_guarded_get(edata)) {
926
goto label_skip_coalesce;
927
}
928
if (!ecache->delay_coalesce) {
929
edata = extent_try_coalesce(tsdn, pac, ehooks, ecache, edata,
930
NULL);
931
} else if (edata_size_get(edata) >= SC_LARGE_MINCLASS) {
932
assert(ecache == &pac->ecache_dirty);
933
/* Always coalesce large extents eagerly. */
934
bool coalesced;
935
do {
936
assert(edata_state_get(edata) == extent_state_active);
937
edata = extent_try_coalesce_large(tsdn, pac, ehooks,
938
ecache, edata, &coalesced);
939
} while (coalesced);
940
if (edata_size_get(edata) >=
941
atomic_load_zu(&pac->oversize_threshold, ATOMIC_RELAXED)
942
&& extent_may_force_decay(pac)) {
943
/* Shortcut to purge the oversize extent eagerly. */
944
malloc_mutex_unlock(tsdn, &ecache->mtx);
945
extent_maximally_purge(tsdn, pac, ehooks, edata);
946
return;
947
}
948
}
949
label_skip_coalesce:
950
extent_deactivate_locked(tsdn, pac, ecache, edata);
951
952
malloc_mutex_unlock(tsdn, &ecache->mtx);
953
}
954
955
void
956
extent_dalloc_gap(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
957
edata_t *edata) {
958
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
959
WITNESS_RANK_CORE, 0);
960
961
if (extent_register(tsdn, pac, edata)) {
962
edata_cache_put(tsdn, pac->edata_cache, edata);
963
return;
964
}
965
extent_dalloc_wrapper(tsdn, pac, ehooks, edata);
966
}
967
968
static bool
969
extent_dalloc_wrapper_try(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
970
edata_t *edata) {
971
bool err;
972
973
assert(edata_base_get(edata) != NULL);
974
assert(edata_size_get(edata) != 0);
975
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
976
WITNESS_RANK_CORE, 0);
977
978
edata_addr_set(edata, edata_base_get(edata));
979
980
/* Try to deallocate. */
981
err = ehooks_dalloc(tsdn, ehooks, edata_base_get(edata),
982
edata_size_get(edata), edata_committed_get(edata));
983
984
if (!err) {
985
edata_cache_put(tsdn, pac->edata_cache, edata);
986
}
987
988
return err;
989
}
990
991
edata_t *
992
extent_alloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
993
void *new_addr, size_t size, size_t alignment, bool zero, bool *commit,
994
bool growing_retained) {
995
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
996
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
997
998
edata_t *edata = edata_cache_get(tsdn, pac->edata_cache);
999
if (edata == NULL) {
1000
return NULL;
1001
}
1002
size_t palignment = ALIGNMENT_CEILING(alignment, PAGE);
1003
void *addr = ehooks_alloc(tsdn, ehooks, new_addr, size, palignment,
1004
&zero, commit);
1005
if (addr == NULL) {
1006
edata_cache_put(tsdn, pac->edata_cache, edata);
1007
return NULL;
1008
}
1009
edata_init(edata, ecache_ind_get(&pac->ecache_dirty), addr,
1010
size, /* slab */ false, SC_NSIZES, extent_sn_next(pac),
1011
extent_state_active, zero, *commit, EXTENT_PAI_PAC,
1012
opt_retain ? EXTENT_IS_HEAD : EXTENT_NOT_HEAD);
1013
/*
1014
* Retained memory is not counted towards gdump. Only if an extent is
1015
* allocated as a separate mapping, i.e. growing_retained is false, then
1016
* gdump should be updated.
1017
*/
1018
bool gdump_add = !growing_retained;
1019
if (extent_register_impl(tsdn, pac, edata, gdump_add)) {
1020
edata_cache_put(tsdn, pac->edata_cache, edata);
1021
return NULL;
1022
}
1023
1024
return edata;
1025
}
1026
1027
void
1028
extent_dalloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
1029
edata_t *edata) {
1030
assert(edata_pai_get(edata) == EXTENT_PAI_PAC);
1031
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1032
WITNESS_RANK_CORE, 0);
1033
1034
/* Avoid calling the default extent_dalloc unless have to. */
1035
if (!ehooks_dalloc_will_fail(ehooks)) {
1036
/* Remove guard pages for dalloc / unmap. */
1037
if (edata_guarded_get(edata)) {
1038
assert(ehooks_are_default(ehooks));
1039
san_unguard_pages_two_sided(tsdn, ehooks, edata,
1040
pac->emap);
1041
}
1042
/*
1043
* Deregister first to avoid a race with other allocating
1044
* threads, and reregister if deallocation fails.
1045
*/
1046
extent_deregister(tsdn, pac, edata);
1047
if (!extent_dalloc_wrapper_try(tsdn, pac, ehooks, edata)) {
1048
return;
1049
}
1050
extent_reregister(tsdn, pac, edata);
1051
}
1052
1053
/* Try to decommit; purge if that fails. */
1054
bool zeroed;
1055
if (!edata_committed_get(edata)) {
1056
zeroed = true;
1057
} else if (!extent_decommit_wrapper(tsdn, ehooks, edata, 0,
1058
edata_size_get(edata))) {
1059
zeroed = true;
1060
} else if (!ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata),
1061
edata_size_get(edata), 0, edata_size_get(edata))) {
1062
zeroed = true;
1063
} else if (edata_state_get(edata) == extent_state_muzzy ||
1064
!ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata),
1065
edata_size_get(edata), 0, edata_size_get(edata))) {
1066
zeroed = false;
1067
} else {
1068
zeroed = false;
1069
}
1070
edata_zeroed_set(edata, zeroed);
1071
1072
if (config_prof) {
1073
extent_gdump_sub(tsdn, edata);
1074
}
1075
1076
extent_record(tsdn, pac, ehooks, &pac->ecache_retained, edata);
1077
}
1078
1079
void
1080
extent_destroy_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
1081
edata_t *edata) {
1082
assert(edata_base_get(edata) != NULL);
1083
assert(edata_size_get(edata) != 0);
1084
extent_state_t state = edata_state_get(edata);
1085
assert(state == extent_state_retained || state == extent_state_active);
1086
assert(emap_edata_is_acquired(tsdn, pac->emap, edata));
1087
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1088
WITNESS_RANK_CORE, 0);
1089
1090
if (edata_guarded_get(edata)) {
1091
assert(opt_retain);
1092
san_unguard_pages_pre_destroy(tsdn, ehooks, edata, pac->emap);
1093
}
1094
edata_addr_set(edata, edata_base_get(edata));
1095
1096
/* Try to destroy; silently fail otherwise. */
1097
ehooks_destroy(tsdn, ehooks, edata_base_get(edata),
1098
edata_size_get(edata), edata_committed_get(edata));
1099
1100
edata_cache_put(tsdn, pac->edata_cache, edata);
1101
}
1102
1103
static bool
1104
extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1105
size_t offset, size_t length, bool growing_retained) {
1106
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1107
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
1108
bool err = ehooks_commit(tsdn, ehooks, edata_base_get(edata),
1109
edata_size_get(edata), offset, length);
1110
edata_committed_set(edata, edata_committed_get(edata) || !err);
1111
return err;
1112
}
1113
1114
bool
1115
extent_commit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1116
size_t offset, size_t length) {
1117
return extent_commit_impl(tsdn, ehooks, edata, offset, length,
1118
/* growing_retained */ false);
1119
}
1120
1121
bool
1122
extent_decommit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1123
size_t offset, size_t length) {
1124
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1125
WITNESS_RANK_CORE, 0);
1126
bool err = ehooks_decommit(tsdn, ehooks, edata_base_get(edata),
1127
edata_size_get(edata), offset, length);
1128
edata_committed_set(edata, edata_committed_get(edata) && err);
1129
return err;
1130
}
1131
1132
static bool
1133
extent_purge_lazy_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1134
size_t offset, size_t length, bool growing_retained) {
1135
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1136
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
1137
bool err = ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata),
1138
edata_size_get(edata), offset, length);
1139
return err;
1140
}
1141
1142
bool
1143
extent_purge_lazy_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1144
size_t offset, size_t length) {
1145
return extent_purge_lazy_impl(tsdn, ehooks, edata, offset,
1146
length, false);
1147
}
1148
1149
static bool
1150
extent_purge_forced_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1151
size_t offset, size_t length, bool growing_retained) {
1152
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1153
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
1154
bool err = ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata),
1155
edata_size_get(edata), offset, length);
1156
return err;
1157
}
1158
1159
bool
1160
extent_purge_forced_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1161
size_t offset, size_t length) {
1162
return extent_purge_forced_impl(tsdn, ehooks, edata, offset, length,
1163
false);
1164
}
1165
1166
/*
1167
* Accepts the extent to split, and the characteristics of each side of the
1168
* split. The 'a' parameters go with the 'lead' of the resulting pair of
1169
* extents (the lower addressed portion of the split), and the 'b' parameters go
1170
* with the trail (the higher addressed portion). This makes 'extent' the lead,
1171
* and returns the trail (except in case of error).
1172
*/
1173
static edata_t *
1174
extent_split_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
1175
edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks) {
1176
assert(edata_size_get(edata) == size_a + size_b);
1177
/* Only the shrink path may split w/o holding core locks. */
1178
if (holding_core_locks) {
1179
witness_assert_positive_depth_to_rank(
1180
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE);
1181
} else {
1182
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1183
WITNESS_RANK_CORE, 0);
1184
}
1185
1186
if (ehooks_split_will_fail(ehooks)) {
1187
return NULL;
1188
}
1189
1190
edata_t *trail = edata_cache_get(tsdn, pac->edata_cache);
1191
if (trail == NULL) {
1192
goto label_error_a;
1193
}
1194
1195
edata_init(trail, edata_arena_ind_get(edata),
1196
(void *)((uintptr_t)edata_base_get(edata) + size_a), size_b,
1197
/* slab */ false, SC_NSIZES, edata_sn_get(edata),
1198
edata_state_get(edata), edata_zeroed_get(edata),
1199
edata_committed_get(edata), EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
1200
emap_prepare_t prepare;
1201
bool err = emap_split_prepare(tsdn, pac->emap, &prepare, edata,
1202
size_a, trail, size_b);
1203
if (err) {
1204
goto label_error_b;
1205
}
1206
1207
/*
1208
* No need to acquire trail or edata, because: 1) trail was new (just
1209
* allocated); and 2) edata is either an active allocation (the shrink
1210
* path), or in an acquired state (extracted from the ecache on the
1211
* extent_recycle_split path).
1212
*/
1213
assert(emap_edata_is_acquired(tsdn, pac->emap, edata));
1214
assert(emap_edata_is_acquired(tsdn, pac->emap, trail));
1215
1216
err = ehooks_split(tsdn, ehooks, edata_base_get(edata), size_a + size_b,
1217
size_a, size_b, edata_committed_get(edata));
1218
1219
if (err) {
1220
goto label_error_b;
1221
}
1222
1223
edata_size_set(edata, size_a);
1224
emap_split_commit(tsdn, pac->emap, &prepare, edata, size_a, trail,
1225
size_b);
1226
1227
return trail;
1228
label_error_b:
1229
edata_cache_put(tsdn, pac->edata_cache, trail);
1230
label_error_a:
1231
return NULL;
1232
}
1233
1234
edata_t *
1235
extent_split_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata,
1236
size_t size_a, size_t size_b, bool holding_core_locks) {
1237
return extent_split_impl(tsdn, pac, ehooks, edata, size_a, size_b,
1238
holding_core_locks);
1239
}
1240
1241
static bool
1242
extent_merge_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *a,
1243
edata_t *b, bool holding_core_locks) {
1244
/* Only the expanding path may merge w/o holding ecache locks. */
1245
if (holding_core_locks) {
1246
witness_assert_positive_depth_to_rank(
1247
tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE);
1248
} else {
1249
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1250
WITNESS_RANK_CORE, 0);
1251
}
1252
1253
assert(edata_base_get(a) < edata_base_get(b));
1254
assert(edata_arena_ind_get(a) == edata_arena_ind_get(b));
1255
assert(edata_arena_ind_get(a) == ehooks_ind_get(ehooks));
1256
emap_assert_mapped(tsdn, pac->emap, a);
1257
emap_assert_mapped(tsdn, pac->emap, b);
1258
1259
bool err = ehooks_merge(tsdn, ehooks, edata_base_get(a),
1260
edata_size_get(a), edata_base_get(b), edata_size_get(b),
1261
edata_committed_get(a));
1262
1263
if (err) {
1264
return true;
1265
}
1266
1267
/*
1268
* The rtree writes must happen while all the relevant elements are
1269
* owned, so the following code uses decomposed helper functions rather
1270
* than extent_{,de}register() to do things in the right order.
1271
*/
1272
emap_prepare_t prepare;
1273
emap_merge_prepare(tsdn, pac->emap, &prepare, a, b);
1274
1275
assert(edata_state_get(a) == extent_state_active ||
1276
edata_state_get(a) == extent_state_merging);
1277
edata_state_set(a, extent_state_active);
1278
edata_size_set(a, edata_size_get(a) + edata_size_get(b));
1279
edata_sn_set(a, (edata_sn_get(a) < edata_sn_get(b)) ?
1280
edata_sn_get(a) : edata_sn_get(b));
1281
edata_zeroed_set(a, edata_zeroed_get(a) && edata_zeroed_get(b));
1282
1283
emap_merge_commit(tsdn, pac->emap, &prepare, a, b);
1284
1285
edata_cache_put(tsdn, pac->edata_cache, b);
1286
1287
return false;
1288
}
1289
1290
bool
1291
extent_merge_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
1292
edata_t *a, edata_t *b) {
1293
return extent_merge_impl(tsdn, pac, ehooks, a, b,
1294
/* holding_core_locks */ false);
1295
}
1296
1297
bool
1298
extent_commit_zero(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
1299
bool commit, bool zero, bool growing_retained) {
1300
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
1301
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
1302
1303
if (commit && !edata_committed_get(edata)) {
1304
if (extent_commit_impl(tsdn, ehooks, edata, 0,
1305
edata_size_get(edata), growing_retained)) {
1306
return true;
1307
}
1308
}
1309
if (zero && !edata_zeroed_get(edata)) {
1310
void *addr = edata_base_get(edata);
1311
size_t size = edata_size_get(edata);
1312
ehooks_zero(tsdn, ehooks, addr, size);
1313
}
1314
return false;
1315
}
1316
1317
bool
1318
extent_boot(void) {
1319
assert(sizeof(slab_data_t) >= sizeof(e_prof_info_t));
1320
1321
if (have_dss) {
1322
extent_dss_boot();
1323
}
1324
1325
return false;
1326
}
1327
1328