1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2002-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21
22 /*
23 * Description: A memory allocator utility. This utility provides
24 * management of (multiple) memory segments, coalescing
25 * of free blocks, etc. Allocators are implemented by
26 * implementing a callback-interface which is called by
27 * this utility. The only task the callback-module has to
28 * perform is to supervise the free blocks.
29 *
30 * Author: Rickard Green
31 */
32
33 /*
34 * Alloc util will enforce 8 byte alignment if sys_alloc and mseg_alloc at
35 * least enforces 8 byte alignment. If sys_alloc only enforces 4 byte
36 * alignment then alloc util will do so too.
37 */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include "global.h"
44 #include "big.h"
45 #include "erl_mmap.h"
46 #include "erl_mtrace.h"
47 #define GET_ERL_ALLOC_UTIL_IMPL
48 #include "erl_alloc_util.h"
49 #include "erl_mseg.h"
50 #include "erl_threads.h"
51 #include "erl_thr_progress.h"
52 #include "erl_bif_unique.h"
53 #include "erl_nif.h"
54
55 #ifdef ERTS_ENABLE_LOCK_COUNT
56 #include "erl_lock_count.h"
57 #endif
58 #include "lttng-wrapper.h"
59
60 #if defined(ERTS_ALLOC_UTIL_HARD_DEBUG) && defined(__GNUC__)
61 #warning "* * * * * * * * * *"
62 #warning "* * * * * * * * * *"
63 #warning "* * NOTE: * *"
64 #warning "* * Hard debug * *"
65 #warning "* * is enabled! * *"
66 #warning "* * * * * * * * * *"
67 #warning "* * * * * * * * * *"
68 #endif
69
70 #define ERTS_ALCU_DD_OPS_LIM_HIGH 20
71 #define ERTS_ALCU_DD_OPS_LIM_LOW 2
72
73 /* Fix alloc limit */
74 #define ERTS_ALCU_FIX_MAX_LIST_SZ 1000
75 #define ERTS_ALC_FIX_MAX_SHRINK_OPS 30
76
77 #define ALLOC_ZERO_EQ_NULL 0
78
79 #ifndef ERTS_MSEG_FLG_2POW
80 # define ERTS_MSEG_FLG_2POW 0
81 #endif
82 #ifndef ERTS_MSEG_FLG_NONE
83 # define ERTS_MSEG_FLG_NONE 0
84 #endif
85
86 static int atoms_initialized = 0;
87 static int initialized = 0;
88
89 #define INV_SYS_ALLOC_CARRIER_MASK ((UWord) (sys_alloc_carrier_size - 1))
90 #define SYS_ALLOC_CARRIER_MASK (~INV_SYS_ALLOC_CARRIER_MASK)
91 #define SYS_ALLOC_CARRIER_FLOOR(X) ((X) & SYS_ALLOC_CARRIER_MASK)
92 #define SYS_ALLOC_CARRIER_CEILING(X) \
93 SYS_ALLOC_CARRIER_FLOOR((X) + INV_SYS_ALLOC_CARRIER_MASK)
94 #define SYS_PAGE_SIZE (sys_page_size)
95 #define SYS_PAGE_SZ_MASK ((UWord)(SYS_PAGE_SIZE - 1))
96
97 #if 0
98 /* Can be useful for debugging */
99 #define MBC_REALLOC_ALWAYS_MOVES
100 #endif
101
102 /* alloc_util global parameters */
103 static Uint sys_alloc_carrier_size;
104 static Uint sys_page_size;
105
106 #if HAVE_ERTS_MSEG
107 static Uint max_mseg_carriers;
108 #endif
109 static int allow_sys_alloc_carriers;
110
111 #define ONE_GIGA (1000000000)
112
113 #define ERTS_ALC_CC_GIGA_VAL(CC) ((CC) / ONE_GIGA)
114 #define ERTS_ALC_CC_VAL(CC) ((CC) % ONE_GIGA)
115
116 #define INC_CC(CC) ((CC)++)
117
118 #define DEC_CC(CC) ((CC)--)
119
120 /* Multi block carrier (MBC) memory layout in OTP 22:
121
122 Empty MBC:
123 [Carrier_t|pad|Block_t L0T0|fhdr| free... ]
124
125 MBC after allocating first block:
126 [Carrier_t|pad|Block_t 0000| udata |pad|Block_t L0T0|fhdr| free... ]
127
128 MBC after allocating second block:
129 [Carrier_t|pad|Block_t 0000| udata |pad|Block_t 0000| udata |pad|Block_t L0T0|fhdr| free... ]
130
131 MBC after deallocating first block:
132 [Carrier_t|pad|Block_t 00T0|fhdr| free |FreeBlkFtr_t|Block_t 0P00| udata |pad|Block_t L0T0|fhdr| free... ]
133
134 MBC after allocating first block, with allocation tagging enabled:
135 [Carrier_t|pad|Block_t 000A| udata |atag|pad|Block_t L0T0|fhdr| free... ]
136
137 udata = Allocated user data
138 atag = A tag with basic metadata about this allocation
139 pad = Padding to ensure correct alignment for user data
140 fhdr = Allocator specific header to keep track of free block
141 free = Unused free memory
142 T = This block is free (THIS_FREE_BLK_HDR_FLG)
143 P = Previous block is free (PREV_FREE_BLK_HDR_FLG)
144 L = Last block in carrier (LAST_BLK_HDR_FLG)
145 A = Block has an allocation tag footer, only valid for allocated blocks
146 (ATAG_BLK_HDR_FLG)
147 */
148
149 /* Single block carrier (SBC):
150 [Carrier_t|pad|Block_t 1110| udata... ]
151 [Carrier_t|pad|Block_t 111A| udata | atag]
152 */
153
154 /* Allocation tags ...
155 *
156 * These are added to the footer of every block when enabled. Currently they
157 * consist of the allocation type and an atom identifying the allocating
158 * driver/nif (or 'system' if that can't be determined), but the format is not
159 * supposed to be set in stone.
160 *
161 * The packing scheme requires that the atom values are small enough to fit
162 * into a word with ERTS_ALC_N_BITS to spare. Users must check for overflow
163 * before MAKE_ATAG(). */
164
165 typedef UWord alcu_atag_t;
166
167 #define MAKE_ATAG(IdAtom, TypeNum) \
168 (ASSERT((TypeNum) >= ERTS_ALC_N_MIN && (TypeNum) <= ERTS_ALC_N_MAX), \
169 ASSERT(atom_val(IdAtom) <= MAX_ATAG_ATOM_ID), \
170 (atom_val(IdAtom) << ERTS_ALC_N_BITS) | (TypeNum))
171
172 #define ATAG_ID(AT) (make_atom((AT) >> ERTS_ALC_N_BITS))
173 #define ATAG_TYPE(AT) ((AT) & ERTS_ALC_N_MASK)
174
175 #define MAX_ATAG_ATOM_ID (ERTS_UWORD_MAX >> ERTS_ALC_N_BITS)
176
177 #define DBG_IS_VALID_ATAG(AT) \
178 (ATAG_TYPE(AT) >= ERTS_ALC_N_MIN && \
179 ATAG_TYPE(AT) <= ERTS_ALC_N_MAX && \
180 ATAG_ID(AT) <= MAX_ATAG_ATOM_ID)
181
182 /* Blocks ... */
183
184 #define UNUSED0_BLK_FTR_FLG (((UWord) 1) << 0)
185 #define UNUSED1_BLK_FTR_FLG (((UWord) 1) << 1)
186 #define UNUSED2_BLK_FTR_FLG (((UWord) 1) << 2)
187
188 #if MBC_ABLK_OFFSET_BITS
189 # define ABLK_HDR_SZ (offsetof(Block_t,u))
190 #else
191 # define ABLK_HDR_SZ (sizeof(Block_t))
192 #endif
193 #define FBLK_FTR_SZ (sizeof(FreeBlkFtr_t))
194
195 #define BLK_HAS_ATAG(B) \
196 (!!((B)->bhdr & ATAG_BLK_HDR_FLG))
197
198 #define GET_BLK_ATAG(B) \
199 (ASSERT(BLK_HAS_ATAG(B)), \
200 ((alcu_atag_t *) (((char *) (B)) + (BLK_SZ(B))))[-1])
201 #define SET_BLK_ATAG(B, T) \
202 ((B)->bhdr |= ATAG_BLK_HDR_FLG, \
203 ((alcu_atag_t *) (((char *) (B)) + (BLK_SZ(B))))[-1] = (T))
204
205 #define BLK_ATAG_SZ(AP) ((AP)->atags ? sizeof(alcu_atag_t) : 0)
206
207 #define UMEMSZ2BLKSZ(AP, SZ) \
208 (ABLK_HDR_SZ + BLK_ATAG_SZ(AP) + (SZ) <= (AP)->min_block_size \
209 ? (AP)->min_block_size \
210 : UNIT_CEILING(ABLK_HDR_SZ + BLK_ATAG_SZ(AP) + (SZ)))
211
212 #define UMEM2BLK(P) ((Block_t *) (((char *) (P)) - ABLK_HDR_SZ))
213 #define BLK2UMEM(P) ((void *) (((char *) (P)) + ABLK_HDR_SZ))
214
215 #define PREV_BLK_SZ(B) ((UWord) (((FreeBlkFtr_t *)(B))[-1]))
216
217 #define SET_BLK_SZ_FTR(B, SZ) \
218 (((FreeBlkFtr_t *) (((char *) (B)) + (SZ)))[-1] = (SZ))
219
220 #define SET_MBC_ABLK_SZ(B, SZ) \
221 (ASSERT(((SZ) & BLK_FLG_MASK) == 0), \
222 (B)->bhdr = (((B)->bhdr) & ~MBC_ABLK_SZ_MASK) | (SZ))
223 #define SET_MBC_FBLK_SZ(B, SZ) \
224 (ASSERT(((SZ) & BLK_FLG_MASK) == 0), \
225 (B)->bhdr = (((B)->bhdr) & ~MBC_FBLK_SZ_MASK) | (SZ))
226 #define SET_SBC_BLK_SZ(B, SZ) \
227 (ASSERT(((SZ) & BLK_FLG_MASK) == 0), \
228 (B)->bhdr = (((B)->bhdr) & ~SBC_BLK_SZ_MASK) | (SZ))
229 #define SET_PREV_BLK_FREE(AP,B) \
230 (ASSERT(!IS_MBC_FIRST_BLK(AP,B)), \
231 ASSERT(!IS_FREE_BLK(B)), \
232 (B)->bhdr |= PREV_FREE_BLK_HDR_FLG)
233 #define SET_PREV_BLK_ALLOCED(B) \
234 ((B)->bhdr &= ~PREV_FREE_BLK_HDR_FLG)
235 #define SET_LAST_BLK(B) \
236 ((B)->bhdr |= LAST_BLK_HDR_FLG)
237 #define SET_NOT_LAST_BLK(B) \
238 ((B)->bhdr &= ~LAST_BLK_HDR_FLG)
239
240 #define SBH_THIS_FREE THIS_FREE_BLK_HDR_FLG
241 #define SBH_PREV_FREE PREV_FREE_BLK_HDR_FLG
242 #define SBH_LAST_BLK LAST_BLK_HDR_FLG
243
244
245 #if MBC_ABLK_OFFSET_BITS
246
247 # define MBC_SZ_MAX_LIMIT ((((UWord)1 << MBC_ABLK_OFFSET_BITS) - 1) << ERTS_SUPER_ALIGN_BITS)
248
249 # define BLK_CARRIER_OFFSET(B, C) (((char*)(B) - (char*)(C)) >> ERTS_SACRR_UNIT_SHIFT)
250
251 # define SET_MBC_ABLK_HDR(B, Sz, F, C) \
252 (ASSERT(((Sz) & ~MBC_ABLK_SZ_MASK) == 0), \
253 ASSERT(!((UWord)(F) & (~BLK_FLG_MASK|THIS_FREE_BLK_HDR_FLG))), \
254 (B)->bhdr = ((Sz) | (F) | (BLK_CARRIER_OFFSET(B,C) << MBC_ABLK_OFFSET_SHIFT)))
255
256 # define SET_MBC_FBLK_HDR(B, Sz, F, C) \
257 (ASSERT(((Sz) & ~MBC_FBLK_SZ_MASK) == 0), \
258 ASSERT(((UWord)(F) & (~BLK_FLG_MASK|THIS_FREE_BLK_HDR_FLG|PREV_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \
259 (B)->bhdr = ((Sz) | (F)), \
260 (B)->u.carrier = (C))
261
262 # define IS_MBC_FIRST_ABLK(AP,B) \
263 ((((UWord)(B) & ~ERTS_SACRR_UNIT_MASK) == MBC_HEADER_SIZE(AP)) \
264 && ((B)->bhdr & MBC_ABLK_OFFSET_MASK) == 0)
265
266 # define IS_MBC_FIRST_FBLK(AP,B) \
267 ((char*)(B) == (char*)((B)->u.carrier) + MBC_HEADER_SIZE(AP))
268
269 # define IS_MBC_FIRST_BLK(AP,B) \
270 (IS_FREE_BLK(B) ? IS_MBC_FIRST_FBLK(AP,B) : IS_MBC_FIRST_ABLK(AP,B))
271
272 # define SET_BLK_FREE(B) \
273 (ASSERT(!IS_PREV_BLK_FREE(B)), \
274 (B)->u.carrier = ABLK_TO_MBC(B), \
275 (B)->bhdr &= (MBC_ABLK_SZ_MASK|LAST_BLK_HDR_FLG), \
276 (B)->bhdr |= THIS_FREE_BLK_HDR_FLG)
277
278 # define SET_BLK_ALLOCED(B) \
279 (ASSERT(((B)->bhdr & (MBC_ABLK_OFFSET_MASK|THIS_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \
280 (B)->bhdr &= ~THIS_FREE_BLK_HDR_FLG, \
281 (B)->bhdr |= (BLK_CARRIER_OFFSET(B,(B)->u.carrier) << MBC_ABLK_OFFSET_SHIFT))
282
283 #else /* !MBC_ABLK_OFFSET_BITS */
284
285 # define MBC_SZ_MAX_LIMIT ((UWord)~0)
286
287 # define SET_MBC_ABLK_HDR(B, Sz, F, C) \
288 (ASSERT(((Sz) & BLK_FLG_MASK) == 0), \
289 ASSERT(((F) & ~BLK_FLG_MASK) == 0), \
290 ASSERT(!((UWord)(F) & (~BLK_FLG_MASK|THIS_FREE_BLK_HDR_FLG))), \
291 (B)->bhdr = ((Sz) | (F)), \
292 (B)->carrier = (C))
293
294 # define SET_MBC_FBLK_HDR(B, Sz, F, C) \
295 (ASSERT(((Sz) & BLK_FLG_MASK) == 0), \
296 ASSERT(((F) & ~BLK_FLG_MASK) == 0), \
297 ASSERT(((UWord)(F) & (~BLK_FLG_MASK|THIS_FREE_BLK_HDR_FLG|PREV_FREE_BLK_HDR_FLG)) == THIS_FREE_BLK_HDR_FLG), \
298 (B)->bhdr = ((Sz) | (F)), \
299 (B)->carrier = (C))
300
301 # define IS_MBC_FIRST_BLK(AP,B) \
302 ((char*)(B) == (char*)((B)->carrier) + MBC_HEADER_SIZE(AP))
303 # define IS_MBC_FIRST_ABLK(AP,B) IS_MBC_FIRST_BLK(AP,B)
304 # define IS_MBC_FIRST_FBLK(AP,B) IS_MBC_FIRST_BLK(AP,B)
305
306 # define SET_BLK_FREE(B) \
307 (ASSERT(!IS_PREV_BLK_FREE(B)), \
308 (B)->bhdr |= THIS_FREE_BLK_HDR_FLG)
309
310 # define SET_BLK_ALLOCED(B) \
311 ((B)->bhdr &= ~THIS_FREE_BLK_HDR_FLG)
312
313 #endif /* !MBC_ABLK_OFFSET_BITS */
314
315 #define SET_SBC_BLK_HDR(B, Sz) \
316 (ASSERT(((Sz) & BLK_FLG_MASK) == 0), (B)->bhdr = ((Sz) | (SBC_BLK_HDR_FLG)))
317
318
319 #define BLK_UMEM_SZ(B) \
320 (BLK_SZ(B) - (ABLK_HDR_SZ))
321 #define IS_PREV_BLK_FREE(B) \
322 ((B)->bhdr & PREV_FREE_BLK_HDR_FLG)
323 #define IS_PREV_BLK_ALLOCED(B) \
324 (!IS_PREV_BLK_FREE((B)))
325 #define IS_ALLOCED_BLK(B) \
326 (!IS_FREE_BLK((B)))
327 #define IS_LAST_BLK(B) \
328 ((B)->bhdr & LAST_BLK_HDR_FLG)
329 #define IS_NOT_LAST_BLK(B) \
330 (!IS_LAST_BLK((B)))
331
332 #define GET_LAST_BLK_HDR_FLG(B) \
333 ((B)->bhdr & LAST_BLK_HDR_FLG)
334 #define GET_THIS_FREE_BLK_HDR_FLG(B) \
335 ((B)->bhdr & THIS_FREE_BLK_HDR_FLG)
336 #define GET_PREV_FREE_BLK_HDR_FLG(B) \
337 ((B)->bhdr & PREV_FREE_BLK_HDR_FLG)
338 #define GET_BLK_HDR_FLGS(B) \
339 ((B)->bhdr & BLK_FLG_MASK)
340
341 #define NXT_BLK(B) \
342 (ASSERT(IS_MBC_BLK(B)), \
343 (Block_t *) (((char *) (B)) + MBC_BLK_SZ((B))))
344 #define PREV_BLK(B) \
345 ((Block_t *) (((char *) (B)) - PREV_BLK_SZ((B))))
346
347 #define BLK_AFTER(B,Sz) \
348 ((Block_t *) (((char *) (B)) + (Sz)))
349
350 #define BLK_SZ(B) ((B)->bhdr & (((B)->bhdr & THIS_FREE_BLK_HDR_FLG) ? MBC_FBLK_SZ_MASK : MBC_ABLK_SZ_MASK))
351
352 /* Carriers ... */
353
354 /* #define ERTS_ALC_CPOOL_DEBUG */
355
356 #if defined(DEBUG) && !defined(ERTS_ALC_CPOOL_DEBUG)
357 # define ERTS_ALC_CPOOL_DEBUG
358 #endif
359
360
361 #ifdef ERTS_ALC_CPOOL_DEBUG
362 # define ERTS_ALC_CPOOL_ASSERT(A) \
363 ((void) ((A) \
364 ? 1 \
365 : (erts_alcu_assert_failed(#A, \
366 (char *) __FILE__, \
367 __LINE__, \
368 (char *) __func__), \
369 0)))
370 #else
371 # define ERTS_ALC_CPOOL_ASSERT(A) ((void) 1)
372 #endif
373
374 #define ERTS_ALC_IS_CPOOL_ENABLED(A) ((A)->cpool.util_limit)
375
376
377 #define ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON 1000
378 #define ERTS_ALC_CPOOL_ALLOC_OP_INC 8
379 #define ERTS_ALC_CPOOL_FREE_OP_DEC 10
380
381 #define ERTS_ALC_CPOOL_ALLOC_OP(A) \
382 do { \
383 if ((A)->cpool.disable_abandon < ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON) { \
384 (A)->cpool.disable_abandon += ERTS_ALC_CPOOL_ALLOC_OP_INC; \
385 if ((A)->cpool.disable_abandon > ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON) \
386 (A)->cpool.disable_abandon = ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON; \
387 } \
388 } while (0)
389
390
391 #if ERTS_ALC_CPOOL_ALLOC_OP_INC >= ERTS_ALC_CPOOL_FREE_OP_DEC
392 # error "Implementation assume ERTS_ALC_CPOOL_ALLOC_OP_INC < ERTS_ALC_CPOOL_FREE_OP_DEC"
393 #endif
394
395 #define ERTS_ALC_CPOOL_REALLOC_OP(A) \
396 do { \
397 if ((A)->cpool.disable_abandon) { \
398 (A)->cpool.disable_abandon -= (ERTS_ALC_CPOOL_FREE_OP_DEC \
399 - ERTS_ALC_CPOOL_ALLOC_OP_INC); \
400 if ((A)->cpool.disable_abandon < 0) \
401 (A)->cpool.disable_abandon = 0; \
402 } \
403 } while (0)
404
405 #define ERTS_ALC_CPOOL_FREE_OP(A) \
406 do { \
407 if ((A)->cpool.disable_abandon) { \
408 (A)->cpool.disable_abandon -= ERTS_ALC_CPOOL_FREE_OP_DEC; \
409 if ((A)->cpool.disable_abandon < 0) \
410 (A)->cpool.disable_abandon = 0; \
411 } \
412 } while (0)
413
414
415 #define ERTS_CRR_ALCTR_FLG_IN_POOL (((erts_aint_t) 1) << 0)
416 #define ERTS_CRR_ALCTR_FLG_BUSY (((erts_aint_t) 1) << 1)
417 #define ERTS_CRR_ALCTR_FLG_HOMECOMING (((erts_aint_t) 1) << 2)
418 #define ERTS_CRR_ALCTR_FLG_MASK (ERTS_CRR_ALCTR_FLG_IN_POOL | \
419 ERTS_CRR_ALCTR_FLG_BUSY | \
420 ERTS_CRR_ALCTR_FLG_HOMECOMING)
421
422 #define SBC_HEADER_SIZE \
423 (UNIT_CEILING(offsetof(Carrier_t, cpool) \
424 + ABLK_HDR_SZ) \
425 - ABLK_HDR_SZ)
426 #define MBC_HEADER_SIZE(AP) ((AP)->mbc_header_size)
427
428
429 #define MSEG_CARRIER_HDR_FLAG (((UWord) 1) << 0)
430 #define SBC_CARRIER_HDR_FLAG (((UWord) 1) << 1)
431
432 #define SCH_SYS_ALLOC 0
433 #define SCH_MSEG MSEG_CARRIER_HDR_FLAG
434 #define SCH_MBC 0
435 #define SCH_SBC SBC_CARRIER_HDR_FLAG
436
437 #define SET_CARRIER_HDR(C, Sz, F, AP) \
438 (ASSERT(((Sz) & CRR_FLG_MASK) == 0), (C)->chdr = ((Sz) | (F)), \
439 erts_atomic_init_nob(&(C)->allctr, (erts_aint_t) (AP)))
440
441 #define BLK_TO_SBC(B) \
442 ((Carrier_t *) (((char *) (B)) - SBC_HEADER_SIZE))
443 #define FIRST_BLK_TO_MBC(AP, B) \
444 ((Carrier_t *) (((char *) (B)) - MBC_HEADER_SIZE(AP)))
445
446 #define MBC_TO_FIRST_BLK(AP, P) \
447 ((Block_t *) (((char *) (P)) + MBC_HEADER_SIZE(AP)))
448 #define SBC2BLK(AP, P) \
449 ((Block_t *) (((char *) (P)) + SBC_HEADER_SIZE))
450 #define SBC2UMEM(AP, P) \
451 ((void *) (((char *) (P)) + (SBC_HEADER_SIZE + ABLK_HDR_SZ)))
452
453 #define IS_MSEG_CARRIER(C) \
454 ((C)->chdr & MSEG_CARRIER_HDR_FLAG)
455 #define IS_SYS_ALLOC_CARRIER(C) \
456 (!IS_MSEG_CARRIER((C)))
457 #define IS_SB_CARRIER(C) \
458 ((C)->chdr & SBC_CARRIER_HDR_FLAG)
459 #define IS_MB_CARRIER(C) \
460 (!IS_SB_CARRIER((C)))
461
462 #define SET_CARRIER_SZ(C, SZ) \
463 (ASSERT(((SZ) & CRR_FLG_MASK) == 0), \
464 ((C)->chdr = ((C)->chdr & CRR_FLG_MASK) | (SZ)))
465
466 #define CFLG_SBC (1 << 0)
467 #define CFLG_MBC (1 << 1)
468 #define CFLG_FORCE_MSEG (1 << 2)
469 #define CFLG_FORCE_SYS_ALLOC (1 << 3)
470 #define CFLG_FORCE_SIZE (1 << 4)
471 #define CFLG_MAIN_CARRIER (1 << 5)
472 #define CFLG_NO_CPOOL (1 << 6)
473
474 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
475 static void check_blk_carrier(Allctr_t *, Block_t *);
476 #define HARD_CHECK_BLK_CARRIER(A, B) check_blk_carrier((A), (B))
477 #else
478 #define HARD_CHECK_BLK_CARRIER(A, B)
479 #endif
480
481 /* Statistics updating ... */
482
483 #ifdef DEBUG
484
485 #define DEBUG_CHECK_CARRIER_NO_SZ_1(CSTATS) \
486 do { \
487 int ix__; \
488 for (ix__ = ERTS_CRR_ALLOC_MIN; ix__ <= ERTS_CRR_ALLOC_MAX; ix__++) { \
489 UWord no__ = (CSTATS)->carriers[ix__].no; \
490 UWord size__= (CSTATS)->carriers[ix__].size; \
491 ASSERT((no__ > 0 && size__ > 0) || (no__ == 0 && size__ == 0)); \
492 } \
493 } while (0)
494
495 #define DEBUG_CHECK_CARRIER_NO_SZ(AP) \
496 do { \
497 DEBUG_CHECK_CARRIER_NO_SZ_1(&(AP)->sbcs); \
498 DEBUG_CHECK_CARRIER_NO_SZ_1(&(AP)->mbcs); \
499 } while (0)
500
501 #else
502 #define DEBUG_CHECK_CARRIER_NO_SZ(AP)
503 #endif
504
505 /* Carrier statistics */
506
507 #define STAT_CRR_UPDATED_1(CSTATS) \
508 do { \
509 UWord no_sum__, size_sum__; \
510 int i__; \
511 no_sum__ = size_sum__ = 0; \
512 for (i__ = ERTS_CRR_ALLOC_MIN; i__ <= ERTS_CRR_ALLOC_MAX; i__++) { \
513 ASSERT(ERTS_UWORD_MAX - no_sum__ > (CSTATS)->carriers[i__].no); \
514 ASSERT(ERTS_UWORD_MAX - size_sum__ > (CSTATS)->carriers[i__].size); \
515 no_sum__ += (CSTATS)->carriers[i__].no; \
516 size_sum__ += (CSTATS)->carriers[i__].size; \
517 } \
518 if ((CSTATS)->max.no < no_sum__) { \
519 (CSTATS)->max.no = no_sum__; \
520 } \
521 if ((CSTATS)->max.size < size_sum__) { \
522 (CSTATS)->max.size = size_sum__; \
523 } \
524 } while (0)
525
526 #define STAT_CRR_ALLOC_1(TYPE, CSTATS, SIZE) \
527 do { \
528 ASSERT(ERTS_UWORD_MAX - (CSTATS)->carriers[(TYPE)].no > 1); \
529 ASSERT(ERTS_UWORD_MAX - (CSTATS)->carriers[(TYPE)].size > (SIZE)); \
530 (CSTATS)->carriers[(TYPE)].no += 1; \
531 (CSTATS)->carriers[(TYPE)].size += (SIZE); \
532 STAT_CRR_UPDATED_1(CSTATS); \
533 } while (0)
534
535 #define STAT_CRR_FREE_1(TYPE, CSTATS, SIZE) \
536 do { \
537 ASSERT((CSTATS)->carriers[(TYPE)].no >= 1); \
538 ASSERT((CSTATS)->carriers[(TYPE)].size >= (SIZE)); \
539 (CSTATS)->carriers[(TYPE)].no -= 1; \
540 (CSTATS)->carriers[(TYPE)].size -= (SIZE); \
541 STAT_CRR_UPDATED_1(CSTATS); \
542 } while (0)
543
544 #define STAT_MSEG_SBC_ALLOC(AP, CSZ, BSZ) \
545 do { \
546 CarriersStats_t *cstats__ = &(AP)->sbcs; \
547 STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
548 STAT_BLK_ALLOC_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
549 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
550 } while (0)
551
552 #define STAT_SYS_ALLOC_SBC_ALLOC(AP, CSZ, BSZ) \
553 do { \
554 CarriersStats_t *cstats__ = &(AP)->sbcs; \
555 STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
556 STAT_BLK_ALLOC_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
557 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
558 } while (0)
559
560 #define STAT_MSEG_SBC_FREE(AP, CSZ, BSZ) \
561 do { \
562 CarriersStats_t *cstats__ = &(AP)->sbcs; \
563 STAT_CRR_FREE_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
564 STAT_BLK_FREE_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
565 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
566 } while (0)
567
568 #define STAT_SYS_ALLOC_SBC_FREE(AP, CSZ, BSZ) \
569 do { \
570 CarriersStats_t *cstats__ = &(AP)->sbcs; \
571 STAT_CRR_FREE_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
572 STAT_BLK_FREE_1(cstats__, (AP)->alloc_no, 1, (BSZ)); \
573 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
574 } while (0)
575
576 #define STAT_MSEG_MBC_ALLOC(AP, CSZ) \
577 do { \
578 CarriersStats_t *cstats__ = &(AP)->mbcs; \
579 STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
580 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
581 } while (0)
582
583 #define STAT_SYS_ALLOC_MBC_ALLOC(AP, CSZ) \
584 do { \
585 CarriersStats_t *cstats__ = &(AP)->mbcs; \
586 STAT_CRR_ALLOC_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
587 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
588 } while (0)
589
590 #define STAT_MSEG_MBC_FREE(AP, CSZ) \
591 do { \
592 CarriersStats_t *cstats__ = &(AP)->mbcs; \
593 STAT_CRR_FREE_1(ERTS_CRR_ALLOC_MSEG, cstats__, CSZ); \
594 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
595 } while (0)
596
597 #define STAT_SYS_ALLOC_MBC_FREE(AP, CSZ) \
598 do { \
599 CarriersStats_t *cstats__ = &(AP)->mbcs; \
600 STAT_CRR_FREE_1(ERTS_CRR_ALLOC_SYS, cstats__, CSZ); \
601 DEBUG_CHECK_CARRIER_NO_SZ((AP)); \
602 } while (0)
603
604 #define STAT_MBC_FREE(AP, CRR) \
605 do { \
606 UWord csz__ = CARRIER_SZ((CRR)); \
607 if (IS_MSEG_CARRIER((CRR))) { \
608 STAT_MSEG_MBC_FREE((AP), csz__); \
609 } else { \
610 STAT_SYS_ALLOC_MBC_FREE((AP), csz__); \
611 } \
612 set_new_allctr_abandon_limit(AP); \
613 } while (0)
614
615 /* Block statistics */
616
617 #define STAT_BLK_UPDATED_1(BSTATS) \
618 do { \
619 if ((BSTATS)->max.no < (BSTATS)->curr.no) { \
620 (BSTATS)->max.no = (BSTATS)->curr.no; \
621 } \
622 if ((BSTATS)->max.size < (BSTATS)->curr.size) { \
623 (BSTATS)->max.size = (BSTATS)->curr.size; \
624 } \
625 } while (0)
626
627 #define STAT_BLK_ALLOC_1(CSTATS, ALLOC_NO, COUNT, SIZE) \
628 do { \
629 BlockStats_t *bstats__ = \
630 &(CSTATS)->blocks[(ALLOC_NO) - ERTS_ALC_A_MIN]; \
631 ASSERT(ERTS_UWORD_MAX - bstats__->curr.no > (COUNT)); \
632 ASSERT(ERTS_UWORD_MAX - bstats__->curr.size > (SIZE)); \
633 bstats__->curr.no += (COUNT); \
634 bstats__->curr.size += (SIZE); \
635 STAT_BLK_UPDATED_1(bstats__); \
636 } while (0)
637
638 #define STAT_BLK_FREE_1(CSTATS, ALLOC_NO, COUNT, SIZE) \
639 do { \
640 BlockStats_t *bstats__ = \
641 &(CSTATS)->blocks[(ALLOC_NO) - ERTS_ALC_A_MIN]; \
642 ASSERT(bstats__->curr.no >= (COUNT)); \
643 ASSERT(bstats__->curr.size >= (SIZE)); \
644 bstats__->curr.no -= (COUNT); \
645 bstats__->curr.size -= (SIZE); \
646 STAT_BLK_UPDATED_1(bstats__); \
647 } while (0)
648
649 #define STAT_MBC_CPOOL_FETCH(AP, CRR) \
650 do { \
651 CarriersStats_t *cstats__ = &(AP)->mbcs; \
652 UWord csz__ = CARRIER_SZ((CRR)); \
653 int alloc_no__; \
654 if (IS_MSEG_CARRIER((CRR))) { \
655 STAT_MSEG_MBC_ALLOC((AP), csz__); \
656 } else { \
657 STAT_SYS_ALLOC_MBC_ALLOC((AP), csz__); \
658 } \
659 set_new_allctr_abandon_limit(AP); \
660 for (alloc_no__ = ERTS_ALC_A_MIN; \
661 alloc_no__ <= ERTS_ALC_A_MAX; \
662 alloc_no__++) { \
663 int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
664 UWord count = (CRR)->cpool.blocks[ix__]; \
665 UWord size = (CRR)->cpool.blocks_size[ix__]; \
666 STAT_BLK_ALLOC_1(cstats__, alloc_no__, count, size); \
667 } \
668 } while (0)
669
670 #define STAT_MBC_CPOOL_ABANDON(AP, CRR) \
671 do { \
672 CarriersStats_t *cstats__ = &(AP)->mbcs; \
673 int alloc_no__; \
674 STAT_MBC_FREE(AP, CRR); \
675 for (alloc_no__ = ERTS_ALC_A_MIN; \
676 alloc_no__ <= ERTS_ALC_A_MAX; \
677 alloc_no__++) { \
678 int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
679 UWord count = (CRR)->cpool.blocks[ix__]; \
680 UWord size = (CRR)->cpool.blocks_size[ix__]; \
681 STAT_BLK_FREE_1(cstats__, alloc_no__, count, size); \
682 } \
683 } while (0)
684
685 static ERTS_INLINE void
stat_cpool_mbc_blk_free(Allctr_t * allctr,int alloc_no,Carrier_t * crr,Carrier_t ** busy_pcrr_pp,UWord blksz)686 stat_cpool_mbc_blk_free(Allctr_t *allctr,
687 int alloc_no,
688 Carrier_t *crr,
689 Carrier_t **busy_pcrr_pp,
690 UWord blksz)
691 {
692 if (!busy_pcrr_pp || !*busy_pcrr_pp) {
693 /* This is a local block, so we should not update the pool
694 * statistics. */
695 CarriersStats_t *cstats__ = &allctr->mbcs;
696 STAT_BLK_FREE_1(cstats__, alloc_no, 1, blksz);
697 } else {
698 Allctr_t *orig_allctr = crr->cpool.orig_allctr;
699 int ix = alloc_no - ERTS_ALC_A_MIN;
700
701 ERTS_ALC_CPOOL_ASSERT(crr == *busy_pcrr_pp && allctr == orig_allctr);
702
703 #ifdef ERTS_ALC_CPOOL_DEBUG
704 ERTS_ALC_CPOOL_ASSERT(
705 erts_atomic_dec_read_nob(
706 &orig_allctr->cpool.stat.no_blocks[ix]) >= 0);
707 ERTS_ALC_CPOOL_ASSERT(
708 erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[ix],
709 -((erts_aint_t) blksz)) >= 0);
710 #else
711 erts_atomic_dec_nob(&orig_allctr->cpool.stat.no_blocks[ix]);
712 erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
713 -((erts_aint_t) blksz));
714 #endif
715 }
716 }
717
718 #define STAT_MBC_BLK_ALLOC(AP, CRR, BSZ) \
719 do { \
720 int alloc_no__ = (AP)->alloc_no; \
721 int ix__ = alloc_no__ - ERTS_ALC_A_MIN; \
722 ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.blocks[ix__] > 1); \
723 ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.blocks_size[ix__] > (BSZ)); \
724 ASSERT(ERTS_UWORD_MAX - (CRR)->cpool.total_blocks_size > (BSZ)); \
725 (CRR)->cpool.blocks[ix__] += 1; \
726 (CRR)->cpool.blocks_size[ix__] += (BSZ); \
727 (CRR)->cpool.total_blocks_size += (BSZ); \
728 STAT_BLK_ALLOC_1(&(AP)->mbcs, alloc_no__, 1, (BSZ)); \
729 } while (0)
730
731 #define STAT_MBC_BLK_FREE(AP, TYPE, CRR, BPCRRPP, BSZ) \
732 do { \
733 int alloc_no__ = ERTS_ALC_T2A(TYPE); \
734 int ix__ = (alloc_no__) - ERTS_ALC_A_MIN; \
735 ASSERT((CRR)->cpool.blocks[ix__] >= 1); \
736 ASSERT((CRR)->cpool.blocks_size[ix__] >= (BSZ)); \
737 ASSERT((CRR)->cpool.total_blocks_size >= (BSZ)); \
738 (CRR)->cpool.blocks[ix__] -= 1; \
739 (CRR)->cpool.blocks_size[ix__] -= (BSZ); \
740 (CRR)->cpool.total_blocks_size -= (BSZ); \
741 stat_cpool_mbc_blk_free((AP), alloc_no__, (CRR), (BPCRRPP), (BSZ)); \
742 } while (0)
743
744 /* Debug stuff... */
745 #ifdef DEBUG
746 static UWord carrier_alignment;
747 #define DEBUG_SAVE_ALIGNMENT(C) \
748 do { \
749 UWord algnmnt__ = sizeof(Unit_t) - (((UWord) (C)) % sizeof(Unit_t));\
750 carrier_alignment = MIN(carrier_alignment, algnmnt__); \
751 ASSERT(((UWord) (C)) % sizeof(UWord) == 0); \
752 } while (0)
753 #define DEBUG_CHECK_ALIGNMENT(P) \
754 do { \
755 ASSERT(sizeof(Unit_t) - (((UWord) (P)) % sizeof(Unit_t)) \
756 >= carrier_alignment); \
757 ASSERT(((UWord) (P)) % sizeof(UWord) == 0); \
758 } while (0)
759
760 #else
761 #define DEBUG_SAVE_ALIGNMENT(C)
762 #define DEBUG_CHECK_ALIGNMENT(P)
763 #endif
764
765 #ifdef DEBUG
766 # define IS_ACTUALLY_BLOCKING (erts_thr_progress_is_blocking())
767 #define ERTS_ALCU_DBG_CHK_THR_ACCESS(A) \
768 do { \
769 if (!(A)->thread_safe && !IS_ACTUALLY_BLOCKING) { \
770 if (!(A)->debug.saved_tid) { \
771 (A)->debug.tid = erts_thr_self(); \
772 (A)->debug.saved_tid = 1; \
773 } \
774 else { \
775 ERTS_LC_ASSERT( \
776 ethr_equal_tids((A)->debug.tid, erts_thr_self())); \
777 } \
778 } \
779 } while (0)
780 #else
781 #define ERTS_ALCU_DBG_CHK_THR_ACCESS(A)
782 #endif
783
784 static void make_name_atoms(Allctr_t *allctr);
785
786 static Block_t *create_carrier(Allctr_t *, Uint, UWord);
787 static void destroy_carrier(Allctr_t *, Block_t *, Carrier_t **);
788 static void mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp);
789 static void dealloc_block(Allctr_t *, ErtsAlcType_t, Uint32, void *, ErtsAlcFixList_t *);
790
determine_alloc_tag(Allctr_t * allocator,ErtsAlcType_t type)791 static alcu_atag_t determine_alloc_tag(Allctr_t *allocator, ErtsAlcType_t type)
792 {
793 ErtsSchedulerData *esdp;
794 Eterm id;
795
796 ERTS_CT_ASSERT(_unchecked_atom_val(am_system) <= MAX_ATAG_ATOM_ID);
797 ASSERT(allocator->atags);
798
799 esdp = erts_get_scheduler_data();
800 id = am_system;
801
802 if (esdp) {
803 if (esdp->current_nif) {
804 Module *mod = erts_nif_get_module((esdp->current_nif)->mod_nif);
805
806 /* Mod can be NULL if a resource destructor allocates memory after
807 * the module has been unloaded. */
808 if (mod) {
809 id = make_atom(mod->module);
810 }
811 } else if (esdp->current_port) {
812 Port *p = esdp->current_port;
813 id = (p->drv_ptr)->name_atom;
814 }
815
816 /* We fall back to 'system' if we can't pack the driver/NIF name into
817 * the tag. This may be a bit misleading but we've made no promises
818 * that the information is complete.
819 *
820 * This can only happen on 32-bit emulators when a new driver/NIF has
821 * been loaded *after* 16 million atoms have been used, and supporting
822 * that fringe case is not worth an extra word. 64-bit emulators are
823 * unaffected since the atom cache limits atom indexes to 32 bits. */
824 if(MAX_ATOM_TABLE_SIZE > MAX_ATAG_ATOM_ID) {
825 if (atom_val(id) > MAX_ATAG_ATOM_ID) {
826 id = am_system;
827 }
828 }
829 }
830
831 return MAKE_ATAG(id, ERTS_ALC_T2N(type));
832 }
833
set_alloc_tag(Allctr_t * allocator,void * p,alcu_atag_t tag)834 static void set_alloc_tag(Allctr_t *allocator, void *p, alcu_atag_t tag)
835 {
836 Block_t *block;
837
838 ASSERT(DBG_IS_VALID_ATAG(tag));
839 ASSERT(allocator->atags && p);
840 (void)allocator;
841
842 block = UMEM2BLK(p);
843
844 SET_BLK_ATAG(block, tag);
845 }
846
847 /* internal data... */
848
849 #if 0
850
851 static ERTS_INLINE void *
852 internal_alloc(UWord size)
853 {
854 void *res = erts_sys_alloc(0, NULL, size);
855 if (!res)
856 erts_alloc_enomem(ERTS_ALC_T_UNDEF, size);
857 return res;
858 }
859
860 static ERTS_INLINE void *
861 internal_realloc(void *ptr, UWord size)
862 {
863 void *res = erts_sys_realloc(0, NULL, ptr, size);
864 if (!res)
865 erts_alloc_enomem(ERTS_ALC_T_UNDEF, size);
866 return res;
867 }
868
869 static ERTS_INLINE void
870 internal_free(void *ptr)
871 {
872 erts_sys_free(0, NULL, ptr);
873 }
874
875 #endif
876
877 #ifdef ARCH_32
878
879 /*
880 * Bit vector for the entire 32-bit virtual address space
881 * with one bit for each super aligned memory segment.
882 */
883
884 #define VSPACE_MAP_BITS (1 << (32 - ERTS_MMAP_SUPERALIGNED_BITS))
885 #define VSPACE_MAP_SZ (VSPACE_MAP_BITS / ERTS_VSPACE_WORD_BITS)
886
set_bit(UWord * map,Uint ix)887 static ERTS_INLINE void set_bit(UWord* map, Uint ix)
888 {
889 ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ);
890 map[ix / ERTS_VSPACE_WORD_BITS]
891 |= ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
892 }
893
clr_bit(UWord * map,Uint ix)894 static ERTS_INLINE void clr_bit(UWord* map, Uint ix)
895 {
896 ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ);
897 map[ix / ERTS_VSPACE_WORD_BITS]
898 &= ~((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
899 }
900
901 #ifdef DEBUG
902
is_bit_set(UWord * map,Uint ix)903 static ERTS_INLINE int is_bit_set(UWord* map, Uint ix)
904 {
905 ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ);
906 return map[ix / ERTS_VSPACE_WORD_BITS]
907 & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS));
908 }
909
910 #endif
911
912 UWord erts_literal_vspace_map[VSPACE_MAP_SZ];
913
set_literal_range(void * start,Uint size)914 static void set_literal_range(void* start, Uint size)
915 {
916 Uint ix = (UWord)start >> ERTS_MMAP_SUPERALIGNED_BITS;
917 Uint n = size >> ERTS_MMAP_SUPERALIGNED_BITS;
918
919 ASSERT(!((UWord)start & ERTS_INV_SUPERALIGNED_MASK));
920 ASSERT(!((UWord)size & ERTS_INV_SUPERALIGNED_MASK));
921 ASSERT(n);
922 while (n--) {
923 ASSERT(!is_bit_set(erts_literal_vspace_map, ix));
924 set_bit(erts_literal_vspace_map, ix);
925 ix++;
926 }
927 }
928
clear_literal_range(void * start,Uint size)929 static void clear_literal_range(void* start, Uint size)
930 {
931 Uint ix = (UWord)start >> ERTS_MMAP_SUPERALIGNED_BITS;
932 Uint n = size >> ERTS_MMAP_SUPERALIGNED_BITS;
933
934 ASSERT(!((UWord)start & ERTS_INV_SUPERALIGNED_MASK));
935 ASSERT(!((UWord)size & ERTS_INV_SUPERALIGNED_MASK));
936 ASSERT(n);
937 while (n--) {
938 ASSERT(is_bit_set(erts_literal_vspace_map, ix));
939 clr_bit(erts_literal_vspace_map, ix);
940 ix++;
941 }
942 }
943
944 #endif /* ARCH_32 */
945
946 /* mseg ... */
947
948 #if HAVE_ERTS_MSEG
949
950 static void*
erts_alcu_mseg_alloc(Allctr_t * allctr,Uint * size_p,Uint flags)951 erts_alcu_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
952 {
953 void *res;
954 UWord size = (UWord) *size_p;
955 res = erts_mseg_alloc_opt(allctr->alloc_no, &size, flags, &allctr->mseg_opt);
956 *size_p = (Uint) size;
957 INC_CC(allctr->calls.mseg_alloc);
958 return res;
959 }
960
961 static void*
erts_alcu_mseg_realloc(Allctr_t * allctr,void * seg,Uint old_size,Uint * new_size_p)962 erts_alcu_mseg_realloc(Allctr_t *allctr, void *seg,
963 Uint old_size, Uint *new_size_p)
964 {
965 void *res;
966 UWord new_size = (UWord) *new_size_p;
967 res = erts_mseg_realloc_opt(allctr->alloc_no, seg, (UWord) old_size, &new_size,
968 ERTS_MSEG_FLG_NONE, &allctr->mseg_opt);
969 *new_size_p = (Uint) new_size;
970 INC_CC(allctr->calls.mseg_realloc);
971 return res;
972 }
973
974 static void
erts_alcu_mseg_dealloc(Allctr_t * allctr,void * seg,Uint size,Uint flags)975 erts_alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
976 {
977 erts_mseg_dealloc_opt(allctr->alloc_no, seg, (UWord) size, flags, &allctr->mseg_opt);
978 INC_CC(allctr->calls.mseg_dealloc);
979 }
980
981
982 #if defined(ARCH_32)
983
984 void*
erts_alcu_literal_32_mseg_alloc(Allctr_t * allctr,Uint * size_p,Uint flags)985 erts_alcu_literal_32_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
986 {
987 void* res;
988 Uint sz = ERTS_SUPERALIGNED_CEILING(*size_p);
989 ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
990 allctr->t == 0);
991 ERTS_LC_ASSERT(allctr->thread_safe);
992
993 res = erts_alcu_mseg_alloc(allctr, &sz, flags);
994 if (res) {
995 set_literal_range(res, sz);
996 *size_p = sz;
997 }
998 return res;
999 }
1000
1001 void*
erts_alcu_literal_32_mseg_realloc(Allctr_t * allctr,void * seg,Uint old_size,Uint * new_size_p)1002 erts_alcu_literal_32_mseg_realloc(Allctr_t *allctr, void *seg,
1003 Uint old_size, Uint *new_size_p)
1004 {
1005 void* res;
1006 Uint new_sz = ERTS_SUPERALIGNED_CEILING(*new_size_p);
1007 ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
1008 allctr->t == 0);
1009 ERTS_LC_ASSERT(allctr->thread_safe);
1010
1011 if (seg && old_size)
1012 clear_literal_range(seg, old_size);
1013 res = erts_alcu_mseg_realloc(allctr, seg, old_size, &new_sz);
1014 if (res) {
1015 set_literal_range(res, new_sz);
1016 *new_size_p = new_sz;
1017 }
1018 return res;
1019 }
1020
1021 void
erts_alcu_literal_32_mseg_dealloc(Allctr_t * allctr,void * seg,Uint size,Uint flags)1022 erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
1023 Uint flags)
1024 {
1025 ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
1026 allctr->t == 0);
1027 ERTS_LC_ASSERT(allctr->thread_safe);
1028
1029 erts_alcu_mseg_dealloc(allctr, seg, size, flags);
1030
1031 clear_literal_range(seg, size);
1032 }
1033
1034 #elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
1035
1036 /* For allocators that have their own mmapper (super carrier),
1037 * like literal_alloc.
1038 */
1039 void*
erts_alcu_mmapper_mseg_alloc(Allctr_t * allctr,Uint * size_p,Uint flags)1040 erts_alcu_mmapper_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
1041 {
1042 void* res;
1043 UWord size = (UWord) *size_p;
1044 Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY;
1045 if (flags & ERTS_MSEG_FLG_2POW)
1046 mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
1047
1048 res = erts_mmap(allctr->mseg_mmapper, mmap_flags, &size);
1049 *size_p = (Uint)size;
1050 INC_CC(allctr->calls.mseg_alloc);
1051 return res;
1052 }
1053
1054 void*
erts_alcu_mmapper_mseg_realloc(Allctr_t * allctr,void * seg,Uint old_size,Uint * new_size_p)1055 erts_alcu_mmapper_mseg_realloc(Allctr_t *allctr, void *seg,
1056 Uint old_size, Uint *new_size_p)
1057 {
1058 void *res;
1059 UWord new_size = (UWord) *new_size_p;
1060 res = erts_mremap(allctr->mseg_mmapper, ERTS_MSEG_FLG_NONE, seg, old_size, &new_size);
1061 *new_size_p = (Uint) new_size;
1062 INC_CC(allctr->calls.mseg_realloc);
1063 return res;
1064 }
1065
1066 void
erts_alcu_mmapper_mseg_dealloc(Allctr_t * allctr,void * seg,Uint size,Uint flags)1067 erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size,
1068 Uint flags)
1069 {
1070 Uint32 mmap_flags = ERTS_MMAPFLG_SUPERCARRIER_ONLY;
1071 if (flags & ERTS_MSEG_FLG_2POW)
1072 mmap_flags |= ERTS_MMAPFLG_SUPERALIGNED;
1073
1074 erts_munmap(allctr->mseg_mmapper, mmap_flags, seg, (UWord)size);
1075 INC_CC(allctr->calls.mseg_dealloc);
1076 }
1077 #endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */
1078
1079 #if defined(ERTS_ALC_A_EXEC)
1080
1081 /*
1082 * For exec_alloc that need memory with PROT_EXEC
1083 */
1084 void*
erts_alcu_exec_mseg_alloc(Allctr_t * allctr,Uint * size_p,Uint flags)1085 erts_alcu_exec_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags)
1086 {
1087 void* res = erts_alcu_mseg_alloc(allctr, size_p, flags);
1088
1089 if (res) {
1090 int r = mprotect(res, *size_p, PROT_EXEC | PROT_READ | PROT_WRITE);
1091 ASSERT(r == 0); (void)r;
1092 }
1093 return res;
1094 }
1095
1096 void*
erts_alcu_exec_mseg_realloc(Allctr_t * allctr,void * seg,Uint old_size,Uint * new_size_p)1097 erts_alcu_exec_mseg_realloc(Allctr_t *allctr, void *seg,
1098 Uint old_size, Uint *new_size_p)
1099 {
1100 void *res;
1101
1102 if (seg && old_size) {
1103 int r = mprotect(seg, old_size, PROT_READ | PROT_WRITE);
1104 ASSERT(r == 0); (void)r;
1105 }
1106 res = erts_alcu_mseg_realloc(allctr, seg, old_size, new_size_p);
1107 if (res) {
1108 int r = mprotect(res, *new_size_p, PROT_EXEC | PROT_READ | PROT_WRITE);
1109 ASSERT(r == 0); (void)r;
1110 }
1111 return res;
1112 }
1113
1114 void
erts_alcu_exec_mseg_dealloc(Allctr_t * allctr,void * seg,Uint size,Uint flags)1115 erts_alcu_exec_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
1116 {
1117 int r = mprotect(seg, size, PROT_READ | PROT_WRITE);
1118 ASSERT(r == 0); (void)r;
1119 erts_alcu_mseg_dealloc(allctr, seg, size, flags);
1120 }
1121 #endif /* ERTS_ALC_A_EXEC */
1122
1123 #endif /* HAVE_ERTS_MSEG */
1124
1125 static void*
erts_alcu_sys_alloc(Allctr_t * allctr,Uint * size_p,int superalign)1126 erts_alcu_sys_alloc(Allctr_t *allctr, Uint* size_p, int superalign)
1127 {
1128 void *res;
1129 const Uint size = *size_p;
1130 #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
1131 if (superalign)
1132 res = erts_sys_aligned_alloc(ERTS_SACRR_UNIT_SZ, size);
1133 else
1134 #endif
1135 res = erts_sys_alloc(0, NULL, size);
1136 INC_CC(allctr->calls.sys_alloc);
1137 if (erts_mtrace_enabled)
1138 erts_mtrace_crr_alloc(res, allctr->alloc_no, ERTS_ALC_A_SYSTEM, size);
1139 return res;
1140 }
1141
1142 static void*
erts_alcu_sys_realloc(Allctr_t * allctr,void * ptr,Uint * size_p,Uint old_size,int superalign)1143 erts_alcu_sys_realloc(Allctr_t *allctr, void *ptr, Uint *size_p, Uint old_size, int superalign)
1144 {
1145 void *res;
1146 const Uint size = *size_p;
1147
1148 #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
1149 if (superalign)
1150 res = erts_sys_aligned_realloc(ERTS_SACRR_UNIT_SZ, ptr, size, old_size);
1151 else
1152 #endif
1153 res = erts_sys_realloc(0, NULL, ptr, size);
1154 INC_CC(allctr->calls.sys_realloc);
1155 if (erts_mtrace_enabled)
1156 erts_mtrace_crr_realloc(res,
1157 allctr->alloc_no,
1158 ERTS_ALC_A_SYSTEM,
1159 ptr,
1160 size);
1161 return res;
1162 }
1163
1164 static void
erts_alcu_sys_dealloc(Allctr_t * allctr,void * ptr,Uint size,int superalign)1165 erts_alcu_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign)
1166 {
1167 #if ERTS_SA_MB_CARRIERS && ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
1168 if (superalign)
1169 erts_sys_aligned_free(ERTS_SACRR_UNIT_SZ, ptr);
1170 else
1171 #endif
1172 erts_sys_free(0, NULL, ptr);
1173 INC_CC(allctr->calls.sys_free);
1174 if (erts_mtrace_enabled)
1175 erts_mtrace_crr_free(allctr->alloc_no, ERTS_ALC_A_SYSTEM, ptr);
1176 }
1177
1178 #ifdef ARCH_32
1179
1180 void*
erts_alcu_literal_32_sys_alloc(Allctr_t * allctr,Uint * size_p,int superalign)1181 erts_alcu_literal_32_sys_alloc(Allctr_t *allctr, Uint* size_p, int superalign)
1182 {
1183 void* res;
1184 Uint size = ERTS_SUPERALIGNED_CEILING(*size_p);
1185 ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
1186 allctr->t == 0);
1187 ERTS_LC_ASSERT(allctr->thread_safe);
1188
1189 res = erts_alcu_sys_alloc(allctr, &size, 1);
1190 if (res) {
1191 set_literal_range(res, size);
1192 *size_p = size;
1193 }
1194 return res;
1195 }
1196
1197 void*
erts_alcu_literal_32_sys_realloc(Allctr_t * allctr,void * ptr,Uint * size_p,Uint old_size,int superalign)1198 erts_alcu_literal_32_sys_realloc(Allctr_t *allctr, void *ptr, Uint* size_p, Uint old_size, int superalign)
1199 {
1200 void* res;
1201 Uint size = ERTS_SUPERALIGNED_CEILING(*size_p);
1202
1203 ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
1204 allctr->t == 0);
1205 ERTS_LC_ASSERT(allctr->thread_safe);
1206
1207 if (ptr && old_size)
1208 clear_literal_range(ptr, old_size);
1209 res = erts_alcu_sys_realloc(allctr, ptr, &size, old_size, 1);
1210 if (res) {
1211 set_literal_range(res, size);
1212 *size_p = size;
1213 }
1214 return res;
1215 }
1216
1217 void
erts_alcu_literal_32_sys_dealloc(Allctr_t * allctr,void * ptr,Uint size,int superalign)1218 erts_alcu_literal_32_sys_dealloc(Allctr_t *allctr, void *ptr, Uint size, int superalign)
1219 {
1220 ERTS_LC_ASSERT(allctr->alloc_no == ERTS_ALC_A_LITERAL &&
1221 allctr->t == 0);
1222 ERTS_LC_ASSERT(allctr->thread_safe);
1223
1224 erts_alcu_sys_dealloc(allctr, ptr, size, 1);
1225
1226 clear_literal_range(ptr, size);
1227 }
1228
1229 #endif /* ARCH_32 */
1230
1231 static Uint
get_next_mbc_size(Allctr_t * allctr)1232 get_next_mbc_size(Allctr_t *allctr)
1233 {
1234 Uint size;
1235 int cs;
1236 int i;
1237
1238 cs = 0;
1239 for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
1240 cs += allctr->mbcs.carriers[i].no;
1241 }
1242 cs -= (allctr->main_carrier ? 1 : 0);
1243
1244 ASSERT(cs >= 0);
1245 ASSERT(allctr->largest_mbc_size >= allctr->smallest_mbc_size);
1246
1247 if (cs >= allctr->mbc_growth_stages)
1248 size = allctr->largest_mbc_size;
1249 else
1250 size = ((cs*(allctr->largest_mbc_size - allctr->smallest_mbc_size)
1251 / allctr->mbc_growth_stages)
1252 + allctr->smallest_mbc_size);
1253
1254 if (size < allctr->min_mbc_size)
1255 size = allctr->min_mbc_size;
1256
1257 return size;
1258 }
1259
1260 static ERTS_INLINE void
link_carrier(CarrierList_t * cl,Carrier_t * crr)1261 link_carrier(CarrierList_t *cl, Carrier_t *crr)
1262 {
1263 crr->next = NULL;
1264 if (!cl->last) {
1265 ASSERT(!cl->first);
1266 cl->first = cl->last = crr;
1267 crr->prev = NULL;
1268 }
1269 else {
1270 ASSERT(cl->first);
1271 ASSERT(!cl->first->prev);
1272 ASSERT(cl->last);
1273 ASSERT(!cl->last->next);
1274 crr->prev = cl->last;
1275 cl->last->next = crr;
1276 cl->last = crr;
1277 }
1278 ASSERT(crr->next != crr);
1279 ASSERT(crr->prev != crr);
1280 }
1281
1282 static ERTS_INLINE void
relink_carrier(CarrierList_t * cl,Carrier_t * crr)1283 relink_carrier(CarrierList_t *cl, Carrier_t *crr)
1284 {
1285 if (crr->next) {
1286 if (crr->next->prev != crr)
1287 crr->next->prev = crr;
1288 }
1289 else if (cl->last != crr)
1290 cl->last = crr;
1291
1292 if (crr->prev) {
1293 if (crr->prev->next != crr)
1294 crr->prev->next = crr;
1295 }
1296 else if (cl->first != crr)
1297 cl->first = crr;
1298 }
1299
1300 static ERTS_INLINE void
unlink_carrier(CarrierList_t * cl,Carrier_t * crr)1301 unlink_carrier(CarrierList_t *cl, Carrier_t *crr)
1302 {
1303 ASSERT(crr->next != crr);
1304 ASSERT(crr->prev != crr);
1305
1306 if (cl->first == crr) {
1307 ASSERT(!crr->prev);
1308 cl->first = crr->next;
1309 }
1310 else {
1311 ASSERT(crr->prev);
1312 crr->prev->next = crr->next;
1313 }
1314
1315 if (cl->last == crr) {
1316 ASSERT(!crr->next);
1317 cl->last = crr->prev;
1318 }
1319 else {
1320 ASSERT(crr->next);
1321 crr->next->prev = crr->prev;
1322 }
1323 #ifdef DEBUG
1324 crr->next = crr;
1325 crr->prev = crr;
1326 #endif
1327 }
1328
is_abandoned(Carrier_t * crr)1329 static ERTS_INLINE int is_abandoned(Carrier_t *crr)
1330 {
1331 return crr->cpool.state != ERTS_MBC_IS_HOME;
1332 }
1333
1334 static ERTS_INLINE void
unlink_abandoned_carrier(Carrier_t * crr)1335 unlink_abandoned_carrier(Carrier_t *crr)
1336 {
1337 if (crr->cpool.state == ERTS_MBC_WAS_POOLED) {
1338 aoff_remove_pooled_mbc(crr->cpool.orig_allctr, crr);
1339 }
1340 }
1341
1342 static ERTS_INLINE void
clear_busy_pool_carrier(Allctr_t * allctr,Carrier_t * crr)1343 clear_busy_pool_carrier(Allctr_t *allctr, Carrier_t *crr)
1344 {
1345 if (crr) {
1346 erts_aint_t max_size;
1347 erts_aint_t iallctr;
1348
1349 max_size = (erts_aint_t) allctr->largest_fblk_in_mbc(allctr, crr);
1350 erts_atomic_set_nob(&crr->cpool.max_size, max_size);
1351
1352 iallctr = erts_atomic_read_nob(&crr->allctr);
1353 ERTS_ALC_CPOOL_ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
1354 == ((erts_aint_t)allctr |
1355 ERTS_CRR_ALCTR_FLG_IN_POOL |
1356 ERTS_CRR_ALCTR_FLG_BUSY));
1357
1358 iallctr &= ~ERTS_CRR_ALCTR_FLG_BUSY;
1359 erts_atomic_set_relb(&crr->allctr, iallctr);
1360 }
1361 }
1362
1363
1364 #if 0
1365 #define ERTS_DBG_CHK_FIX_LIST(A, FIX, IX, B) \
1366 do { if ((FIX)) chk_fix_list((A), (FIX), (IX), (B)); } while (0)
1367 static void
1368 chk_fix_list(Allctr_t *allctr, ErtsAlcFixList_t *fix, int ix, int before)
1369 {
1370 void *p;
1371 int n;
1372 for (n = 0, p = fix[ix].list; p; p = *((void **) p))
1373 n++;
1374 if (n != fix[ix].list_size) {
1375 erts_fprintf(stderr, "FOUND IT ts=%d, sched=%d, ix=%d, n=%d, ls=%d %s!\n",
1376 allctr->thread_safe, allctr->ix, ix, n, fix[ix].list_size, before ? "before" : "after");
1377 abort();
1378 }
1379 }
1380 #else
1381 #define ERTS_DBG_CHK_FIX_LIST(A, FIX, IX, B)
1382 #endif
1383
1384 static ERTS_INLINE Allctr_t *get_pref_allctr(void *extra);
1385 static void *mbc_alloc(Allctr_t *allctr, Uint size);
1386
1387 static ERTS_INLINE void
sched_fix_shrink(Allctr_t * allctr,int on)1388 sched_fix_shrink(Allctr_t *allctr, int on)
1389 {
1390 if (on && !allctr->fix_shrink_scheduled) {
1391 allctr->fix_shrink_scheduled = 1;
1392 erts_set_aux_work_timeout(allctr->ix,
1393 (ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM
1394 | ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC),
1395 1);
1396 }
1397 else if (!on && allctr->fix_shrink_scheduled) {
1398 allctr->fix_shrink_scheduled = 0;
1399 erts_set_aux_work_timeout(allctr->ix,
1400 (ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM
1401 | ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC),
1402 0);
1403 }
1404 }
1405
1406 static ERTS_INLINE void
fix_cpool_check_shrink(Allctr_t * allctr,ErtsAlcType_t type,ErtsAlcFixList_t * fix,Carrier_t ** busy_pcrr_pp)1407 fix_cpool_check_shrink(Allctr_t *allctr,
1408 ErtsAlcType_t type,
1409 ErtsAlcFixList_t *fix,
1410 Carrier_t **busy_pcrr_pp)
1411 {
1412 if (fix->u.cpool.shrink_list > 0) {
1413 if (fix->list_size == 0)
1414 fix->u.cpool.shrink_list = 0;
1415 else {
1416 void *p;
1417 if (busy_pcrr_pp) {
1418 clear_busy_pool_carrier(allctr, *busy_pcrr_pp);
1419 *busy_pcrr_pp = NULL;
1420 }
1421 fix->u.cpool.shrink_list--;
1422 p = fix->list;
1423 fix->list = *((void **) p);
1424 fix->list_size--;
1425 if (fix->u.cpool.min_list_size > fix->list_size)
1426 fix->u.cpool.min_list_size = fix->list_size;
1427
1428 dealloc_block(allctr, type, DEALLOC_FLG_FIX_SHRINK, p, fix);
1429 }
1430 }
1431 }
1432
1433 static ERTS_INLINE void *
fix_cpool_alloc(Allctr_t * allctr,ErtsAlcType_t type,Uint size)1434 fix_cpool_alloc(Allctr_t *allctr, ErtsAlcType_t type, Uint size)
1435 {
1436 void *res;
1437 ErtsAlcFixList_t *fix;
1438
1439 fix = &allctr->fix[ERTS_ALC_FIX_TYPE_IX(type)];
1440 ASSERT(type == fix->type && size == fix->type_size);
1441 ASSERT(size >= sizeof(ErtsAllctrDDBlock_t));
1442
1443 res = fix->list;
1444 if (res) {
1445 fix->list = *((void **) res);
1446 fix->list_size--;
1447 if (fix->u.cpool.min_list_size > fix->list_size)
1448 fix->u.cpool.min_list_size = fix->list_size;
1449 fix->u.cpool.used++;
1450 fix_cpool_check_shrink(allctr, type, fix, NULL);
1451 return res;
1452 }
1453 if (size >= allctr->sbc_threshold) {
1454 Block_t *blk;
1455 blk = create_carrier(allctr, size, CFLG_SBC);
1456 res = blk ? BLK2UMEM(blk) : NULL;
1457 }
1458 else
1459 res = mbc_alloc(allctr, size);
1460 if (res) {
1461 fix->u.cpool.used++;
1462 fix->u.cpool.allocated++;
1463 }
1464 return res;
1465 }
1466
1467 static ERTS_INLINE void
fix_cpool_free(Allctr_t * allctr,ErtsAlcType_t type,Uint32 flags,void * p,Carrier_t ** busy_pcrr_pp)1468 fix_cpool_free(Allctr_t *allctr,
1469 ErtsAlcType_t type,
1470 Uint32 flags,
1471 void *p,
1472 Carrier_t **busy_pcrr_pp)
1473 {
1474 ErtsAlcFixList_t *fix;
1475 Allctr_t *fix_allctr;
1476
1477 /* If this isn't a fix allocator we need to update the fix list of our
1478 * neighboring fix_alloc to keep the statistics consistent. */
1479 if (!allctr->fix) {
1480 ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[ERTS_ALC_A_FIXED_SIZE];
1481 fix_allctr = get_pref_allctr(tspec);
1482 ASSERT(!fix_allctr->thread_safe);
1483 ASSERT(allctr != fix_allctr);
1484 }
1485 else {
1486 fix_allctr = allctr;
1487 }
1488
1489 ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(fix_allctr));
1490 ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr));
1491
1492 fix = &fix_allctr->fix[ERTS_ALC_FIX_TYPE_IX(type)];
1493 ASSERT(type == fix->type);
1494
1495 if (!(flags & DEALLOC_FLG_FIX_SHRINK)) {
1496 fix->u.cpool.used--;
1497 }
1498
1499 /* We don't want foreign blocks to be long-lived, so we skip recycling if
1500 * allctr != fix_allctr. */
1501 if (allctr == fix_allctr
1502 && (!busy_pcrr_pp || !*busy_pcrr_pp)
1503 && !fix->u.cpool.shrink_list
1504 && fix->list_size < ERTS_ALCU_FIX_MAX_LIST_SZ) {
1505 *((void **) p) = fix->list;
1506 fix->list = p;
1507 fix->list_size++;
1508 sched_fix_shrink(allctr, 1);
1509 }
1510 else {
1511 Block_t *blk = UMEM2BLK(p);
1512 if (IS_SBC_BLK(blk))
1513 destroy_carrier(allctr, blk, NULL);
1514 else
1515 mbc_free(allctr, type, p, busy_pcrr_pp);
1516 fix->u.cpool.allocated--;
1517 fix_cpool_check_shrink(allctr, type, fix, busy_pcrr_pp);
1518 }
1519 }
1520
1521 static ERTS_INLINE erts_aint32_t
fix_cpool_alloc_shrink(Allctr_t * allctr,erts_aint32_t flgs)1522 fix_cpool_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs)
1523 {
1524 int all_empty = 1;
1525 erts_aint32_t res = 0;
1526 int ix, o;
1527 int flush = flgs == 0;
1528
1529 if (allctr->thread_safe)
1530 erts_mtx_lock(&allctr->mutex);
1531
1532 for (ix = 0; ix < ERTS_ALC_NO_FIXED_SIZES; ix++) {
1533 ErtsAlcFixList_t *fix = &allctr->fix[ix];
1534 ErtsAlcType_t type;
1535 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 1);
1536 if (flush)
1537 fix->u.cpool.shrink_list = fix->list_size;
1538 else if (flgs & ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM) {
1539 fix->u.cpool.shrink_list = fix->u.cpool.min_list_size;
1540 fix->u.cpool.min_list_size = fix->list_size;
1541 }
1542 type = ERTS_ALC_N2T((ErtsAlcType_t) (ix + ERTS_ALC_N_MIN_A_FIXED_SIZE));
1543 for (o = 0; o < ERTS_ALC_FIX_MAX_SHRINK_OPS || flush; o++) {
1544 void *ptr;
1545
1546 if (fix->u.cpool.shrink_list == 0)
1547 break;
1548 if (fix->list_size == 0) {
1549 fix->u.cpool.shrink_list = 0;
1550 break;
1551 }
1552 ptr = fix->list;
1553 fix->list = *((void **) ptr);
1554 fix->list_size--;
1555 fix->u.cpool.shrink_list--;
1556 dealloc_block(allctr, type, DEALLOC_FLG_FIX_SHRINK, ptr, fix);
1557 }
1558 if (fix->u.cpool.min_list_size > fix->list_size)
1559 fix->u.cpool.min_list_size = fix->list_size;
1560 if (fix->list_size != 0) {
1561 if (fix->u.cpool.shrink_list > 0)
1562 res |= ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC;
1563 all_empty = 0;
1564 }
1565 }
1566
1567 if (all_empty)
1568 sched_fix_shrink(allctr, 0);
1569
1570 if (allctr->thread_safe)
1571 erts_mtx_unlock(&allctr->mutex);
1572
1573 return res;
1574 }
1575
1576 static ERTS_INLINE void *
fix_nocpool_alloc(Allctr_t * allctr,ErtsAlcType_t type,Uint size)1577 fix_nocpool_alloc(Allctr_t *allctr, ErtsAlcType_t type, Uint size)
1578 {
1579 ErtsAlcFixList_t *fix;
1580 void *res;
1581
1582 fix = &allctr->fix[ERTS_ALC_FIX_TYPE_IX(type)];
1583 ASSERT(type == fix->type && size == fix->type_size);
1584 ASSERT(size >= sizeof(ErtsAllctrDDBlock_t));
1585
1586 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 1);
1587 fix->u.nocpool.used++;
1588 res = fix->list;
1589 if (res) {
1590 fix->list_size--;
1591 fix->list = *((void **) res);
1592 if (fix->list && fix->u.nocpool.allocated > fix->u.nocpool.limit) {
1593 Block_t *blk;
1594 void *p = fix->list;
1595 fix->list = *((void **) p);
1596 fix->list_size--;
1597 blk = UMEM2BLK(p);
1598 if (IS_SBC_BLK(blk))
1599 destroy_carrier(allctr, blk, NULL);
1600 else
1601 mbc_free(allctr, type, p, NULL);
1602 fix->u.nocpool.allocated--;
1603 }
1604 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 0);
1605 return res;
1606 }
1607 if (fix->u.nocpool.limit < fix->u.nocpool.used)
1608 fix->u.nocpool.limit = fix->u.nocpool.used;
1609 if (fix->u.nocpool.max_used < fix->u.nocpool.used)
1610 fix->u.nocpool.max_used = fix->u.nocpool.used;
1611 fix->u.nocpool.allocated++;
1612
1613 if (size >= allctr->sbc_threshold) {
1614 Block_t *blk;
1615 blk = create_carrier(allctr, size, CFLG_SBC);
1616 res = blk ? BLK2UMEM(blk) : NULL;
1617 }
1618 else
1619 res = mbc_alloc(allctr, size);
1620
1621 if (!res) {
1622 fix->u.nocpool.allocated--;
1623 fix->u.nocpool.used--;
1624 }
1625 return res;
1626 }
1627
1628 static ERTS_INLINE void
fix_nocpool_free(Allctr_t * allctr,ErtsAlcType_t type,void * p)1629 fix_nocpool_free(Allctr_t *allctr,
1630 ErtsAlcType_t type,
1631 void *p)
1632 {
1633 Block_t *blk;
1634 ErtsAlcFixList_t *fix;
1635
1636 fix = &allctr->fix[ERTS_ALC_T2N(type) - ERTS_ALC_N_MIN_A_FIXED_SIZE];
1637 ASSERT(fix->type == type);
1638
1639 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 1);
1640 fix->u.nocpool.used--;
1641 if (fix->u.nocpool.allocated < fix->u.nocpool.limit
1642 && fix->list_size < ERTS_ALCU_FIX_MAX_LIST_SZ) {
1643 *((void **) p) = fix->list;
1644 fix->list = p;
1645 fix->list_size++;
1646 sched_fix_shrink(allctr, 1);
1647 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 0);
1648 return;
1649 }
1650 fix->u.nocpool.allocated--;
1651 if (fix->list && fix->u.nocpool.allocated > fix->u.nocpool.limit) {
1652 blk = UMEM2BLK(p);
1653 if (IS_SBC_BLK(blk))
1654 destroy_carrier(allctr, blk, NULL);
1655 else
1656 mbc_free(allctr, type, p, NULL);
1657 p = fix->list;
1658 fix->list = *((void **) p);
1659 fix->list_size--;
1660 fix->u.nocpool.allocated--;
1661 }
1662
1663 blk = UMEM2BLK(p);
1664 if (IS_SBC_BLK(blk))
1665 destroy_carrier(allctr, blk, NULL);
1666 else
1667 mbc_free(allctr, type, p, NULL);
1668 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 0);
1669 }
1670
1671 static ERTS_INLINE erts_aint32_t
fix_nocpool_alloc_shrink(Allctr_t * allctr,erts_aint32_t flgs)1672 fix_nocpool_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs)
1673 {
1674 int all_empty = 1;
1675 erts_aint32_t res = 0;
1676 int ix, o;
1677 int flush = flgs == 0;
1678
1679 if (allctr->thread_safe)
1680 erts_mtx_lock(&allctr->mutex);
1681
1682 for (ix = 0; ix < ERTS_ALC_NO_FIXED_SIZES; ix++) {
1683 ErtsAlcFixList_t *fix = &allctr->fix[ix];
1684 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 1);
1685 if (flgs & ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM) {
1686 fix->u.nocpool.limit = fix->u.nocpool.max_used;
1687 if (fix->u.nocpool.limit < fix->u.nocpool.used)
1688 fix->u.nocpool.limit = fix->u.nocpool.used;
1689 fix->u.nocpool.max_used = fix->u.nocpool.used;
1690 ASSERT(fix->u.nocpool.limit >= 0);
1691
1692 }
1693 if (flush) {
1694 fix->u.nocpool.limit = 0;
1695 fix->u.nocpool.max_used = fix->u.nocpool.used;
1696 ASSERT(fix->u.nocpool.limit >= 0);
1697 }
1698 for (o = 0; o < ERTS_ALC_FIX_MAX_SHRINK_OPS || flush; o++) {
1699 void *ptr;
1700
1701 if (!flush && fix->u.nocpool.limit >= fix->u.nocpool.allocated)
1702 break;
1703 if (fix->list_size == 0)
1704 break;
1705 ptr = fix->list;
1706 fix->list = *((void **) ptr);
1707 fix->list_size--;
1708 dealloc_block(allctr, fix->type, 0, ptr, NULL);
1709 fix->u.nocpool.allocated--;
1710 }
1711 if (fix->list_size != 0) {
1712 if (fix->u.nocpool.limit < fix->u.nocpool.allocated)
1713 res |= ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC;
1714 all_empty = 0;
1715 }
1716 ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 0);
1717 }
1718
1719 if (all_empty)
1720 sched_fix_shrink(allctr, 0);
1721
1722 if (allctr->thread_safe)
1723 erts_mtx_unlock(&allctr->mutex);
1724
1725 return res;
1726 }
1727
1728 erts_aint32_t
erts_alcu_fix_alloc_shrink(Allctr_t * allctr,erts_aint32_t flgs)1729 erts_alcu_fix_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs)
1730 {
1731 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
1732 return fix_cpool_alloc_shrink(allctr, flgs);
1733 else
1734 return fix_nocpool_alloc_shrink(allctr, flgs);
1735 }
1736
1737 static void dealloc_carrier(Allctr_t *allctr, Carrier_t *crr, int superaligned);
1738
1739 static ERTS_INLINE void
dealloc_mbc(Allctr_t * allctr,Carrier_t * crr)1740 dealloc_mbc(Allctr_t *allctr, Carrier_t *crr)
1741 {
1742 ASSERT(IS_MB_CARRIER(crr));
1743 if (allctr->destroying_mbc)
1744 allctr->destroying_mbc(allctr, crr);
1745
1746 dealloc_carrier(allctr, crr, 1);
1747 }
1748
1749
1750 static UWord allctr_abandon_limit(Allctr_t *allctr);
1751 static void set_new_allctr_abandon_limit(Allctr_t*);
1752 static void abandon_carrier(Allctr_t*, Carrier_t*);
1753 static void poolify_my_carrier(Allctr_t*, Carrier_t*);
1754 static void enqueue_homecoming(Allctr_t*, Carrier_t*);
1755
1756 static ERTS_INLINE Allctr_t*
get_pref_allctr(void * extra)1757 get_pref_allctr(void *extra)
1758 {
1759 ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
1760 int pref_ix;
1761
1762 pref_ix = ERTS_ALC_GET_THR_IX();
1763
1764 ERTS_CT_ASSERT(sizeof(UWord) == sizeof(Allctr_t *));
1765 ASSERT(0 <= pref_ix && pref_ix < tspec->size);
1766
1767 return tspec->allctr[pref_ix];
1768 }
1769
1770 #define ERTS_ALC_TS_PREF_LOCK_IF_USED (1)
1771 #define ERTS_ALC_TS_PREF_LOCK_NO (0)
1772
1773 /* SMP note:
1774 * get_used_allctr() must be safe WITHOUT locking the allocator while
1775 * concurrent threads may be updating adjacent blocks.
1776 * We rely on getting a consistent result (without atomic op) when reading
1777 * the block header word even if a concurrent thread is updating
1778 * the "PREV_FREE" flag bit.
1779 */
1780 static ERTS_INLINE Allctr_t*
get_used_allctr(Allctr_t * pref_allctr,int pref_lock,void * p,UWord * sizep,Carrier_t ** busy_pcrr_pp)1781 get_used_allctr(Allctr_t *pref_allctr, int pref_lock, void *p, UWord *sizep,
1782 Carrier_t **busy_pcrr_pp)
1783 {
1784 Block_t* blk = UMEM2BLK(p);
1785 Carrier_t *crr;
1786 erts_aint_t iallctr;
1787 Allctr_t *used_allctr;
1788
1789 *busy_pcrr_pp = NULL;
1790
1791 if (IS_SBC_BLK(blk)) {
1792 crr = BLK_TO_SBC(blk);
1793 if (sizep)
1794 *sizep = SBC_BLK_SZ(blk) - ABLK_HDR_SZ;
1795 iallctr = erts_atomic_read_dirty(&crr->allctr);
1796 }
1797 else {
1798 crr = ABLK_TO_MBC(blk);
1799
1800 if (sizep)
1801 *sizep = MBC_ABLK_SZ(blk) - ABLK_HDR_SZ;
1802 if (!ERTS_ALC_IS_CPOOL_ENABLED(pref_allctr))
1803 iallctr = erts_atomic_read_dirty(&crr->allctr);
1804 else {
1805 int locked_pref_allctr = 0;
1806 iallctr = erts_atomic_read_ddrb(&crr->allctr);
1807
1808 if (ERTS_ALC_TS_PREF_LOCK_IF_USED == pref_lock
1809 && pref_allctr->thread_safe) {
1810 used_allctr = (Allctr_t *) (iallctr & ~ERTS_CRR_ALCTR_FLG_MASK);
1811 if (pref_allctr == used_allctr) {
1812 erts_mtx_lock(&pref_allctr->mutex);
1813 locked_pref_allctr = 1;
1814 }
1815 }
1816
1817 while ((iallctr & ((~ERTS_CRR_ALCTR_FLG_MASK)|ERTS_CRR_ALCTR_FLG_IN_POOL))
1818 == (((erts_aint_t) pref_allctr)|ERTS_CRR_ALCTR_FLG_IN_POOL)) {
1819 erts_aint_t act;
1820
1821 ERTS_ALC_CPOOL_ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
1822 if (iallctr & ERTS_CRR_ALCTR_FLG_HOMECOMING) {
1823 /*
1824 * This carrier has just been given back to us by writing
1825 * to crr->allctr with a write barrier (see abandon_carrier).
1826 *
1827 * We need a mathing read barrier to guarantee a correct view
1828 * of the carrier for deallocation work.
1829 */
1830 act = erts_atomic_cmpxchg_rb(&crr->allctr,
1831 iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
1832 iallctr);
1833 }
1834 else {
1835 act = erts_atomic_cmpxchg_ddrb(&crr->allctr,
1836 iallctr|ERTS_CRR_ALCTR_FLG_BUSY,
1837 iallctr);
1838 }
1839 if (act == iallctr) {
1840 *busy_pcrr_pp = crr;
1841 break;
1842 }
1843 iallctr = act;
1844 }
1845
1846 used_allctr = (Allctr_t *) (iallctr & ~ERTS_CRR_ALCTR_FLG_MASK);
1847
1848 if (ERTS_ALC_TS_PREF_LOCK_IF_USED == pref_lock) {
1849 if (locked_pref_allctr && used_allctr != pref_allctr) {
1850 /* Was taken out of pool; now owned by someone else */
1851 erts_mtx_unlock(&pref_allctr->mutex);
1852 }
1853 }
1854 return used_allctr;
1855 }
1856 }
1857
1858 used_allctr = (Allctr_t *) (iallctr & ~ERTS_CRR_ALCTR_FLG_MASK);
1859
1860 if (ERTS_ALC_TS_PREF_LOCK_IF_USED == pref_lock
1861 && used_allctr == pref_allctr
1862 && pref_allctr->thread_safe) {
1863 erts_mtx_lock(&pref_allctr->mutex);
1864 }
1865
1866 return used_allctr;
1867 }
1868
1869 static void
init_dd_queue(ErtsAllctrDDQueue_t * ddq)1870 init_dd_queue(ErtsAllctrDDQueue_t *ddq)
1871 {
1872 erts_atomic_init_nob(&ddq->tail.data.marker.u.atmc_next, ERTS_AINT_NULL);
1873 erts_atomic_init_nob(&ddq->tail.data.last,
1874 (erts_aint_t) &ddq->tail.data.marker);
1875 erts_atomic_init_nob(&ddq->tail.data.um_refc[0], 0);
1876 erts_atomic_init_nob(&ddq->tail.data.um_refc[1], 0);
1877 erts_atomic32_init_nob(&ddq->tail.data.um_refc_ix, 0);
1878 ddq->head.first = &ddq->tail.data.marker;
1879 ddq->head.unref_end = &ddq->tail.data.marker;
1880 ddq->head.next.thr_progress = erts_thr_progress_current();
1881 ddq->head.next.thr_progress_reached = 1;
1882 ddq->head.next.um_refc_ix = 1;
1883 ddq->head.next.unref_end = &ddq->tail.data.marker;
1884 ddq->head.used_marker = 1;
1885 }
1886
1887 static ERTS_INLINE int
ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t * ddq,void * ptr,int cinit)1888 ddq_managed_thread_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit)
1889 {
1890 erts_aint_t itmp;
1891 ErtsAllctrDDBlock_t *enq, *this = ptr;
1892
1893 erts_atomic_init_nob(&this->u.atmc_next, ERTS_AINT_NULL);
1894 /* Enqueue at end of list... */
1895
1896 enq = (ErtsAllctrDDBlock_t *) erts_atomic_read_nob(&ddq->tail.data.last);
1897 itmp = erts_atomic_cmpxchg_relb(&enq->u.atmc_next,
1898 (erts_aint_t) this,
1899 ERTS_AINT_NULL);
1900 if (itmp == ERTS_AINT_NULL) {
1901 /* We are required to move last pointer */
1902 #ifdef DEBUG
1903 ASSERT(ERTS_AINT_NULL == erts_atomic_read_nob(&this->u.atmc_next));
1904 ASSERT(((erts_aint_t) enq)
1905 == erts_atomic_xchg_relb(&ddq->tail.data.last,
1906 (erts_aint_t) this));
1907 #else
1908 erts_atomic_set_relb(&ddq->tail.data.last, (erts_aint_t) this);
1909 #endif
1910 return 1;
1911 }
1912 else {
1913 /*
1914 * We *need* to insert element somewhere in between the
1915 * last element we read earlier and the actual last element.
1916 */
1917 int i = cinit;
1918
1919 while (1) {
1920 erts_aint_t itmp2;
1921 erts_atomic_set_nob(&this->u.atmc_next, itmp);
1922 itmp2 = erts_atomic_cmpxchg_relb(&enq->u.atmc_next,
1923 (erts_aint_t) this,
1924 itmp);
1925 if (itmp == itmp2)
1926 return 0; /* inserted this */
1927 if ((i & 1) == 0)
1928 itmp = itmp2;
1929 else {
1930 enq = (ErtsAllctrDDBlock_t *) itmp2;
1931 itmp = erts_atomic_read_acqb(&enq->u.atmc_next);
1932 ASSERT(itmp != ERTS_AINT_NULL);
1933 }
1934 i++;
1935 }
1936 }
1937 }
1938
1939 static ERTS_INLINE erts_aint_t
check_insert_marker(ErtsAllctrDDQueue_t * ddq,erts_aint_t ilast)1940 check_insert_marker(ErtsAllctrDDQueue_t *ddq, erts_aint_t ilast)
1941 {
1942 if (!ddq->head.used_marker
1943 && ddq->head.unref_end == (ErtsAllctrDDBlock_t *) ilast) {
1944 erts_aint_t itmp;
1945 ErtsAllctrDDBlock_t *last = (ErtsAllctrDDBlock_t *) ilast;
1946
1947 erts_atomic_init_nob(&ddq->tail.data.marker.u.atmc_next, ERTS_AINT_NULL);
1948 itmp = erts_atomic_cmpxchg_relb(&last->u.atmc_next,
1949 (erts_aint_t) &ddq->tail.data.marker,
1950 ERTS_AINT_NULL);
1951 if (itmp == ERTS_AINT_NULL) {
1952 ilast = (erts_aint_t) &ddq->tail.data.marker;
1953 ddq->head.used_marker = !0;
1954 erts_atomic_set_relb(&ddq->tail.data.last, ilast);
1955 }
1956 }
1957 return ilast;
1958 }
1959
1960 static ERTS_INLINE int
ddq_enqueue(ErtsAllctrDDQueue_t * ddq,void * ptr,int cinit)1961 ddq_enqueue(ErtsAllctrDDQueue_t *ddq, void *ptr, int cinit)
1962 {
1963 int last_elem;
1964 int um_refc_ix = 0;
1965 int managed_thread = erts_thr_progress_is_managed_thread();
1966 if (!managed_thread) {
1967 um_refc_ix = erts_atomic32_read_acqb(&ddq->tail.data.um_refc_ix);
1968 while (1) {
1969 int tmp_um_refc_ix;
1970 erts_atomic_inc_acqb(&ddq->tail.data.um_refc[um_refc_ix]);
1971 tmp_um_refc_ix = erts_atomic32_read_acqb(&ddq->tail.data.um_refc_ix);
1972 if (tmp_um_refc_ix == um_refc_ix)
1973 break;
1974 erts_atomic_dec_relb(&ddq->tail.data.um_refc[um_refc_ix]);
1975 um_refc_ix = tmp_um_refc_ix;
1976 }
1977 }
1978
1979 last_elem = ddq_managed_thread_enqueue(ddq, ptr, cinit);
1980
1981 if (!managed_thread)
1982 erts_atomic_dec_relb(&ddq->tail.data.um_refc[um_refc_ix]);
1983 return last_elem;
1984 }
1985
1986 static ERTS_INLINE void *
ddq_dequeue(ErtsAllctrDDQueue_t * ddq)1987 ddq_dequeue(ErtsAllctrDDQueue_t *ddq)
1988 {
1989 ErtsAllctrDDBlock_t *blk;
1990
1991 if (ddq->head.first == ddq->head.unref_end)
1992 return NULL;
1993
1994 blk = ddq->head.first;
1995 if (blk == &ddq->tail.data.marker) {
1996 ASSERT(ddq->head.used_marker);
1997 ddq->head.used_marker = 0;
1998 blk = ((ErtsAllctrDDBlock_t *)
1999 erts_atomic_read_nob(&blk->u.atmc_next));
2000 if (blk == ddq->head.unref_end) {
2001 ddq->head.first = blk;
2002 return NULL;
2003 }
2004 }
2005
2006 ddq->head.first = ((ErtsAllctrDDBlock_t *)
2007 erts_atomic_read_nob(&blk->u.atmc_next));
2008
2009 ASSERT(ddq->head.first);
2010
2011 return (void *) blk;
2012 }
2013
2014 static int
ddq_check_incoming(ErtsAllctrDDQueue_t * ddq)2015 ddq_check_incoming(ErtsAllctrDDQueue_t *ddq)
2016 {
2017 erts_aint_t ilast = erts_atomic_read_nob(&ddq->tail.data.last);
2018 if (((ErtsAllctrDDBlock_t *) ilast) == &ddq->tail.data.marker
2019 && ddq->head.first == &ddq->tail.data.marker) {
2020 /* Nothing more to do... */
2021 return 0;
2022 }
2023
2024 if (ddq->head.next.thr_progress_reached
2025 || erts_thr_progress_has_reached(ddq->head.next.thr_progress)) {
2026 int um_refc_ix;
2027 ddq->head.next.thr_progress_reached = 1;
2028 um_refc_ix = ddq->head.next.um_refc_ix;
2029 if (erts_atomic_read_nob(&ddq->tail.data.um_refc[um_refc_ix]) == 0) {
2030 /* Move unreferenced end pointer forward... */
2031
2032 ETHR_MEMBAR(ETHR_LoadLoad|ETHR_LoadStore);
2033
2034 ddq->head.unref_end = ddq->head.next.unref_end;
2035
2036 ilast = check_insert_marker(ddq, ilast);
2037
2038 if (ddq->head.unref_end != (ErtsAllctrDDBlock_t *) ilast) {
2039 ddq->head.next.unref_end = (ErtsAllctrDDBlock_t *) ilast;
2040 ddq->head.next.thr_progress = erts_thr_progress_later(NULL);
2041 erts_atomic32_set_relb(&ddq->tail.data.um_refc_ix,
2042 um_refc_ix);
2043 ddq->head.next.um_refc_ix = um_refc_ix == 0 ? 1 : 0;
2044 ddq->head.next.thr_progress_reached = 0;
2045 }
2046 }
2047 }
2048 return 1;
2049 }
2050
2051 static ERTS_INLINE void
store_earliest_thr_prgr(ErtsThrPrgrVal * prev_val,ErtsAllctrDDQueue_t * ddq)2052 store_earliest_thr_prgr(ErtsThrPrgrVal *prev_val, ErtsAllctrDDQueue_t *ddq)
2053 {
2054 if (!ddq->head.next.thr_progress_reached
2055 && (*prev_val == ERTS_THR_PRGR_INVALID
2056 || erts_thr_progress_cmp(ddq->head.next.thr_progress,
2057 *prev_val) < 0)) {
2058 *prev_val = ddq->head.next.thr_progress;
2059 }
2060 }
2061
2062 static void
2063 check_pending_dealloc_carrier(Allctr_t *allctr,
2064 int *need_thr_progress,
2065 ErtsThrPrgrVal *thr_prgr_p,
2066 int *need_more_work);
2067
2068 static void
handle_delayed_fix_dealloc(Allctr_t * allctr,ErtsAlcType_t type,Uint32 flags,void * ptr)2069 handle_delayed_fix_dealloc(Allctr_t *allctr, ErtsAlcType_t type, Uint32 flags,
2070 void *ptr)
2071 {
2072 ASSERT(ERTS_ALC_IS_FIX_TYPE(type));
2073
2074 if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
2075 fix_nocpool_free(allctr, type, ptr);
2076 else {
2077 Block_t *blk = UMEM2BLK(ptr);
2078 Carrier_t *busy_pcrr_p;
2079 Allctr_t *used_allctr;
2080
2081 if (IS_SBC_BLK(blk)) {
2082 busy_pcrr_p = NULL;
2083 goto doit;
2084 }
2085
2086 used_allctr = get_used_allctr(allctr, ERTS_ALC_TS_PREF_LOCK_NO, ptr,
2087 NULL, &busy_pcrr_p);
2088 if (used_allctr == allctr) {
2089 doit:
2090 fix_cpool_free(allctr, type, flags, ptr, &busy_pcrr_p);
2091 clear_busy_pool_carrier(allctr, busy_pcrr_p);
2092 }
2093 else {
2094 /* Carrier migrated; need to redirect block to new owner... */
2095 ErtsAllctrDDBlock_t *dd_block;
2096 int cinit;
2097
2098 dd_block = (ErtsAllctrDDBlock_t*)ptr;
2099 dd_block->flags = flags;
2100 dd_block->type = type;
2101
2102 ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
2103
2104 DEC_CC(allctr->calls.this_free);
2105
2106 cinit = used_allctr->dd.ix - allctr->dd.ix;
2107
2108 if (ddq_enqueue(&used_allctr->dd.q, ptr, cinit))
2109 erts_alloc_notify_delayed_dealloc(used_allctr->ix);
2110 }
2111 }
2112 }
2113
2114 static void schedule_dealloc_carrier(Allctr_t*, Carrier_t*);
2115 static void dealloc_my_carrier(Allctr_t*, Carrier_t*);
2116
2117
2118 static ERTS_INLINE int
handle_delayed_dealloc(Allctr_t * allctr,int allctr_locked,int use_limit,int ops_limit,int * need_thr_progress,ErtsThrPrgrVal * thr_prgr_p,int * need_more_work)2119 handle_delayed_dealloc(Allctr_t *allctr,
2120 int allctr_locked,
2121 int use_limit,
2122 int ops_limit,
2123 int *need_thr_progress,
2124 ErtsThrPrgrVal *thr_prgr_p,
2125 int *need_more_work)
2126 {
2127 int need_thr_prgr = 0;
2128 int need_mr_wrk = 0;
2129 int have_checked_incoming = 0;
2130 int ops = 0;
2131 int res;
2132 ErtsAllctrDDQueue_t *ddq;
2133
2134 if (allctr->thread_safe && !allctr_locked)
2135 erts_mtx_lock(&allctr->mutex);
2136
2137 ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
2138
2139 ddq = &allctr->dd.q;
2140
2141 res = 0;
2142
2143 while (1) {
2144 Block_t *blk;
2145 void *ptr;
2146
2147 if (use_limit && ++ops > ops_limit) {
2148 if (ddq->head.first != ddq->head.unref_end) {
2149 need_mr_wrk = 1;
2150 if (need_more_work)
2151 *need_more_work |= 1;
2152 }
2153 break;
2154 }
2155
2156 dequeue:
2157 ptr = ddq_dequeue(ddq);
2158 if (!ptr) {
2159 if (have_checked_incoming)
2160 break;
2161 need_thr_prgr = ddq_check_incoming(ddq);
2162 if (need_thr_progress) {
2163 *need_thr_progress |= need_thr_prgr;
2164 if (need_thr_prgr)
2165 store_earliest_thr_prgr(thr_prgr_p, ddq);
2166
2167 }
2168 have_checked_incoming = 1;
2169 goto dequeue;
2170 }
2171
2172 res = 1;
2173
2174 blk = UMEM2BLK(ptr);
2175 if (blk->bhdr == HOMECOMING_MBC_BLK_HDR) {
2176 /*
2177 * A multiblock carrier that previously has been migrated away
2178 * from us, was sent back to us either because
2179 * - it became empty and we need to deallocated it, or
2180 * - it was inserted into the pool and we need to update our pooled_tree
2181 */
2182 Carrier_t *crr = ErtsContainerStruct(blk, Carrier_t,
2183 cpool.homecoming_dd.blk);
2184 Block_t* first_blk = MBC_TO_FIRST_BLK(allctr, crr);
2185 erts_aint_t iallctr;
2186
2187 ERTS_ALC_CPOOL_ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr));
2188 ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
2189
2190 iallctr = erts_atomic_read_nob(&crr->allctr);
2191 ASSERT(iallctr & ERTS_CRR_ALCTR_FLG_HOMECOMING);
2192 while (1) {
2193 if ((iallctr & (~ERTS_CRR_ALCTR_FLG_MASK |
2194 ERTS_CRR_ALCTR_FLG_IN_POOL))
2195 == (erts_aint_t)allctr) {
2196 /*
2197 * Carrier is home (mine and not in pool)
2198 */
2199 ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
2200 erts_atomic_set_nob(&crr->allctr, (erts_aint_t)allctr);
2201 if (IS_FREE_LAST_MBC_BLK(first_blk))
2202 dealloc_my_carrier(allctr, crr);
2203 else
2204 ASSERT(crr->cpool.state == ERTS_MBC_IS_HOME);
2205 }
2206 else {
2207 erts_aint_t exp = iallctr;
2208 erts_aint_t want = iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING;
2209
2210 iallctr = erts_atomic_cmpxchg_nob(&crr->allctr,
2211 want,
2212 exp);
2213 if (iallctr != exp)
2214 continue; /* retry */
2215
2216 ASSERT(crr->cpool.state != ERTS_MBC_IS_HOME);
2217 unlink_abandoned_carrier(crr);
2218 if (iallctr & ERTS_CRR_ALCTR_FLG_IN_POOL)
2219 poolify_my_carrier(allctr, crr);
2220 else
2221 crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
2222 }
2223 break;
2224 }
2225 }
2226 else {
2227 ErtsAllctrDDBlock_t *dd_block;
2228 ErtsAlcType_t type;
2229 Uint32 flags;
2230
2231 dd_block = (ErtsAllctrDDBlock_t*)ptr;
2232 flags = dd_block->flags;
2233 type = dd_block->type;
2234
2235 flags |= DEALLOC_FLG_REDIRECTED;
2236
2237 ASSERT(IS_SBC_BLK(blk) || (ABLK_TO_MBC(blk) !=
2238 ErtsContainerStruct(blk, Carrier_t,
2239 cpool.homecoming_dd.blk)));
2240
2241 INC_CC(allctr->calls.this_free);
2242
2243 if (ERTS_ALC_IS_FIX_TYPE(type)) {
2244 handle_delayed_fix_dealloc(allctr, type, flags, ptr);
2245 } else {
2246 dealloc_block(allctr, type, flags, ptr, NULL);
2247 }
2248 }
2249 }
2250
2251 if (need_thr_progress && !(need_thr_prgr | need_mr_wrk)) {
2252 need_thr_prgr = ddq_check_incoming(ddq);
2253 *need_thr_progress |= need_thr_prgr;
2254 if (need_thr_prgr)
2255 store_earliest_thr_prgr(thr_prgr_p, ddq);
2256 }
2257
2258 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
2259 check_pending_dealloc_carrier(allctr,
2260 need_thr_progress,
2261 thr_prgr_p,
2262 need_more_work);
2263
2264 if (allctr->thread_safe && !allctr_locked)
2265 erts_mtx_unlock(&allctr->mutex);
2266 return res;
2267 }
2268
2269 static ERTS_INLINE void
enqueue_dealloc_other_instance(ErtsAlcType_t type,Allctr_t * allctr,void * ptr,int cinit)2270 enqueue_dealloc_other_instance(ErtsAlcType_t type,
2271 Allctr_t *allctr,
2272 void *ptr,
2273 int cinit)
2274 {
2275 ErtsAllctrDDBlock_t *dd_block = ((ErtsAllctrDDBlock_t*)ptr);
2276
2277 dd_block->type = type;
2278 dd_block->flags = 0;
2279
2280 if (ddq_enqueue(&allctr->dd.q, ptr, cinit))
2281 erts_alloc_notify_delayed_dealloc(allctr->ix);
2282 }
2283
2284 static ERTS_INLINE void
update_pooled_tree(Allctr_t * allctr,Carrier_t * crr,Uint blk_sz)2285 update_pooled_tree(Allctr_t *allctr, Carrier_t *crr, Uint blk_sz)
2286 {
2287 if (allctr == crr->cpool.orig_allctr && crr->cpool.state == ERTS_MBC_WAS_POOLED) {
2288 /*
2289 * Update pooled_tree with a potentially new (larger) max_sz
2290 */
2291 AOFF_RBTree_t* crr_node = &crr->cpool.pooled;
2292 if (blk_sz > crr_node->hdr.bhdr) {
2293 crr_node->hdr.bhdr = blk_sz;
2294 erts_aoff_larger_max_size(crr_node);
2295 }
2296 }
2297 }
2298
2299 static ERTS_INLINE void
check_abandon_carrier(Allctr_t * allctr,Block_t * fblk,Carrier_t ** busy_pcrr_pp)2300 check_abandon_carrier(Allctr_t *allctr, Block_t *fblk, Carrier_t **busy_pcrr_pp)
2301 {
2302 Carrier_t *crr;
2303 UWord ncrr_in_pool, largest_fblk;
2304
2305 if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
2306 return;
2307
2308 ASSERT(allctr->cpool.abandon_limit == allctr_abandon_limit(allctr));
2309 ASSERT(erts_thr_progress_is_managed_thread());
2310
2311 if (allctr->cpool.disable_abandon)
2312 return;
2313
2314 {
2315 int ix = allctr->alloc_no - ERTS_ALC_A_MIN;
2316
2317 /* We only consider the current allocation type; . */
2318 if (allctr->mbcs.blocks[ix].curr.size > allctr->cpool.abandon_limit) {
2319 return;
2320 }
2321 }
2322
2323 ncrr_in_pool = erts_atomic_read_nob(&allctr->cpool.stat.no_carriers);
2324 if (ncrr_in_pool >= allctr->cpool.in_pool_limit)
2325 return;
2326
2327 crr = FBLK_TO_MBC(fblk);
2328
2329 if (allctr->main_carrier == crr)
2330 return;
2331
2332 if (crr->cpool.total_blocks_size > crr->cpool.abandon_limit)
2333 return;
2334
2335 if (crr->cpool.thr_prgr != ERTS_THR_PRGR_INVALID
2336 && !erts_thr_progress_has_reached(crr->cpool.thr_prgr))
2337 return;
2338
2339 largest_fblk = allctr->largest_fblk_in_mbc(allctr, crr);
2340 if (largest_fblk < allctr->cpool.fblk_min_limit)
2341 return;
2342
2343 erts_atomic_set_nob(&crr->cpool.max_size, largest_fblk);
2344 abandon_carrier(allctr, crr);
2345 }
2346
2347 void
erts_alcu_check_delayed_dealloc(Allctr_t * allctr,int limit,int * need_thr_progress,ErtsThrPrgrVal * thr_prgr_p,int * more_work)2348 erts_alcu_check_delayed_dealloc(Allctr_t *allctr,
2349 int limit,
2350 int *need_thr_progress,
2351 ErtsThrPrgrVal *thr_prgr_p,
2352 int *more_work)
2353 {
2354 handle_delayed_dealloc(allctr,
2355 0,
2356 limit,
2357 ERTS_ALCU_DD_OPS_LIM_HIGH,
2358 need_thr_progress,
2359 thr_prgr_p,
2360 more_work);
2361 }
2362
2363 #define ERTS_ALCU_HANDLE_DD_IN_OP(Allctr, Locked) \
2364 handle_delayed_dealloc((Allctr), (Locked), 1, \
2365 ERTS_ALCU_DD_OPS_LIM_LOW, NULL, NULL, NULL)
2366
2367 static void
dealloc_block(Allctr_t * allctr,ErtsAlcType_t type,Uint32 flags,void * ptr,ErtsAlcFixList_t * fix)2368 dealloc_block(Allctr_t *allctr, ErtsAlcType_t type, Uint32 flags, void *ptr,
2369 ErtsAlcFixList_t *fix)
2370 {
2371 Block_t *blk = UMEM2BLK(ptr);
2372
2373 ASSERT(!fix || type == fix->type);
2374
2375 ERTS_LC_ASSERT(!allctr->thread_safe
2376 || erts_lc_mtx_is_locked(&allctr->mutex));
2377
2378 if (IS_SBC_BLK(blk)) {
2379 destroy_carrier(allctr, blk, NULL);
2380 if (fix && ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
2381 if (!(flags & DEALLOC_FLG_FIX_SHRINK))
2382 fix->u.cpool.used--;
2383 fix->u.cpool.allocated--;
2384 }
2385 }
2386 else if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
2387 mbc_free(allctr, type, ptr, NULL);
2388 else {
2389 Carrier_t *busy_pcrr_p;
2390 Allctr_t *used_allctr;
2391
2392 used_allctr = get_used_allctr(allctr, ERTS_ALC_TS_PREF_LOCK_NO, ptr,
2393 NULL, &busy_pcrr_p);
2394 if (used_allctr == allctr) {
2395 if (fix) {
2396 if (!(flags & DEALLOC_FLG_FIX_SHRINK))
2397 fix->u.cpool.used--;
2398 fix->u.cpool.allocated--;
2399 }
2400 mbc_free(allctr, type, ptr, &busy_pcrr_p);
2401 clear_busy_pool_carrier(allctr, busy_pcrr_p);
2402 }
2403 else {
2404 /* Carrier migrated; need to redirect block to new owner... */
2405 ErtsAllctrDDBlock_t *dd_block;
2406 int cinit;
2407
2408 dd_block = (ErtsAllctrDDBlock_t*)ptr;
2409 dd_block->flags = flags;
2410 dd_block->type = type;
2411
2412 ERTS_ALC_CPOOL_ASSERT(!busy_pcrr_p);
2413
2414 if (flags & DEALLOC_FLG_REDIRECTED)
2415 DEC_CC(allctr->calls.this_free);
2416
2417 cinit = used_allctr->dd.ix - allctr->dd.ix;
2418
2419 if (ddq_enqueue(&used_allctr->dd.q, ptr, cinit))
2420 erts_alloc_notify_delayed_dealloc(used_allctr->ix);
2421 }
2422 }
2423 }
2424
2425 /* Multi block carrier alloc/realloc/free ... */
2426
2427 /* NOTE! mbc_alloc() may in case of memory shortage place the requested
2428 * block in a sbc.
2429 */
2430 static ERTS_INLINE void *
mbc_alloc_block(Allctr_t * allctr,Uint size,Uint * blk_szp)2431 mbc_alloc_block(Allctr_t *allctr, Uint size, Uint *blk_szp)
2432 {
2433 Block_t *blk;
2434 Uint get_blk_sz;
2435
2436 ASSERT(size);
2437 ASSERT(size < allctr->sbc_threshold);
2438
2439 *blk_szp = get_blk_sz = UMEMSZ2BLKSZ(allctr, size);
2440
2441 blk = (*allctr->get_free_block)(allctr, get_blk_sz, NULL, 0);
2442
2443 if (!blk) {
2444 blk = create_carrier(allctr, get_blk_sz, CFLG_MBC);
2445 #if !ERTS_SUPER_ALIGNED_MSEG_ONLY
2446 if (!blk) {
2447 /* Emergency! We couldn't create the carrier as we wanted.
2448 Try to place it in a sys_alloced sbc. */
2449 blk = create_carrier(allctr,
2450 size,
2451 (CFLG_SBC
2452 | CFLG_FORCE_SIZE
2453 | CFLG_FORCE_SYS_ALLOC));
2454 }
2455 #endif
2456 }
2457
2458 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
2459 if (IS_MBC_BLK(blk)) {
2460 (*allctr->link_free_block)(allctr, blk);
2461 HARD_CHECK_BLK_CARRIER(allctr, blk);
2462 (*allctr->unlink_free_block)(allctr, blk);
2463 }
2464 #endif
2465
2466 return blk;
2467 }
2468
2469 static ERTS_INLINE void
mbc_alloc_finalize(Allctr_t * allctr,Block_t * blk,Uint org_blk_sz,UWord flags,Carrier_t * crr,Uint want_blk_sz,int valid_blk_info)2470 mbc_alloc_finalize(Allctr_t *allctr,
2471 Block_t *blk,
2472 Uint org_blk_sz,
2473 UWord flags,
2474 Carrier_t *crr,
2475 Uint want_blk_sz,
2476 int valid_blk_info)
2477 {
2478 Uint blk_sz;
2479 Uint nxt_blk_sz;
2480 Block_t *nxt_blk;
2481 UWord prev_free_flg = flags & PREV_FREE_BLK_HDR_FLG;
2482
2483 ASSERT(org_blk_sz >= want_blk_sz);
2484 ASSERT(blk);
2485
2486 #ifdef DEBUG
2487 nxt_blk = NULL;
2488 #endif
2489
2490 if (org_blk_sz - allctr->min_block_size >= want_blk_sz) {
2491 /* Shrink block... */
2492 blk_sz = want_blk_sz;
2493 nxt_blk_sz = org_blk_sz - blk_sz;
2494 SET_MBC_ABLK_HDR(blk, blk_sz, prev_free_flg, crr);
2495
2496 nxt_blk = BLK_AFTER(blk, blk_sz);
2497 SET_MBC_FBLK_HDR(nxt_blk, nxt_blk_sz,
2498 SBH_THIS_FREE|(flags & LAST_BLK_HDR_FLG),
2499 crr);
2500
2501 if (!(flags & LAST_BLK_HDR_FLG)) {
2502 SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
2503 if (!valid_blk_info) {
2504 Block_t *nxt_nxt_blk = BLK_AFTER(nxt_blk, nxt_blk_sz);
2505 SET_PREV_BLK_FREE(allctr, nxt_nxt_blk);
2506 }
2507 }
2508 (*allctr->link_free_block)(allctr, nxt_blk);
2509
2510 ASSERT(IS_NOT_LAST_BLK(blk));
2511 ASSERT(IS_FREE_BLK(nxt_blk));
2512 ASSERT((flags & LAST_BLK_HDR_FLG)
2513 ? IS_LAST_BLK(nxt_blk)
2514 : IS_NOT_LAST_BLK(nxt_blk));
2515 ASSERT((flags & LAST_BLK_HDR_FLG)
2516 || nxt_blk == PREV_BLK(NXT_BLK(nxt_blk)));
2517 ASSERT((flags & LAST_BLK_HDR_FLG)
2518 || IS_PREV_BLK_FREE(NXT_BLK(nxt_blk)));
2519 ASSERT(nxt_blk_sz == MBC_BLK_SZ(nxt_blk));
2520 ASSERT(nxt_blk_sz % sizeof(Unit_t) == 0);
2521 ASSERT(nxt_blk_sz >= allctr->min_block_size);
2522 ASSERT(ABLK_TO_MBC(blk) == crr);
2523 ASSERT(FBLK_TO_MBC(nxt_blk) == crr);
2524 }
2525 else {
2526 ASSERT(org_blk_sz <= MBC_ABLK_SZ_MASK);
2527 blk_sz = org_blk_sz;
2528 if (flags & LAST_BLK_HDR_FLG) {
2529 if (valid_blk_info)
2530 SET_BLK_ALLOCED(blk);
2531 else
2532 SET_MBC_ABLK_HDR(blk, blk_sz, SBH_LAST_BLK|prev_free_flg, crr);
2533 }
2534 else {
2535 if (valid_blk_info)
2536 SET_BLK_ALLOCED(blk);
2537 else
2538 SET_MBC_ABLK_HDR(blk, blk_sz, prev_free_flg, crr);
2539 nxt_blk = BLK_AFTER(blk, blk_sz);
2540 SET_PREV_BLK_ALLOCED(nxt_blk);
2541 }
2542
2543 ASSERT((flags & LAST_BLK_HDR_FLG)
2544 ? IS_LAST_BLK(blk)
2545 : IS_NOT_LAST_BLK(blk));
2546 ASSERT(ABLK_TO_MBC(blk) == crr);
2547 }
2548
2549 ERTS_ALC_CPOOL_ALLOC_OP(allctr);
2550 STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
2551
2552 ASSERT(IS_ALLOCED_BLK(blk));
2553 ASSERT(blk_sz == MBC_BLK_SZ(blk));
2554 ASSERT(blk_sz % sizeof(Unit_t) == 0);
2555 ASSERT(blk_sz >= allctr->min_block_size);
2556 ASSERT(blk_sz >= want_blk_sz);
2557 ASSERT(IS_MBC_BLK(blk));
2558
2559 ASSERT(!nxt_blk || IS_PREV_BLK_ALLOCED(nxt_blk));
2560 ASSERT(!nxt_blk || IS_MBC_BLK(nxt_blk));
2561
2562 HARD_CHECK_BLK_CARRIER(allctr, blk);
2563 }
2564
2565 static void *
mbc_alloc(Allctr_t * allctr,Uint size)2566 mbc_alloc(Allctr_t *allctr, Uint size)
2567 {
2568 Block_t *blk;
2569 Uint blk_sz;
2570 blk = mbc_alloc_block(allctr, size, &blk_sz);
2571 if (!blk)
2572 return NULL;
2573 if (IS_MBC_BLK(blk))
2574 mbc_alloc_finalize(allctr,
2575 blk,
2576 MBC_FBLK_SZ(blk),
2577 GET_BLK_HDR_FLGS(blk),
2578 FBLK_TO_MBC(blk),
2579 blk_sz,
2580 1);
2581 return BLK2UMEM(blk);
2582 }
2583
2584 typedef struct {
2585 char *ptr;
2586 UWord size;
2587 } ErtsMemDiscardRegion;
2588
2589 /* Construct a discard region for the user memory of a free block, letting the
2590 * OS reclaim its physical memory when required.
2591 *
2592 * Note that we're ignoring both the footer and everything that comes before
2593 * the minimum block size as the allocator uses those areas to manage the
2594 * block. */
2595 static void ERTS_INLINE
mem_discard_start(Allctr_t * allocator,Block_t * block,ErtsMemDiscardRegion * out)2596 mem_discard_start(Allctr_t *allocator, Block_t *block,
2597 ErtsMemDiscardRegion *out)
2598 {
2599 UWord size = BLK_SZ(block);
2600
2601 ASSERT(size >= allocator->min_block_size);
2602
2603 if (size > (allocator->min_block_size + FBLK_FTR_SZ)) {
2604 out->size = size - allocator->min_block_size - FBLK_FTR_SZ;
2605 } else {
2606 out->size = 0;
2607 }
2608
2609 out->ptr = (char*)block + allocator->min_block_size;
2610 }
2611
2612 /* Expands a discard region into a neighboring free block, allowing us to
2613 * discard the block header and first page.
2614 *
2615 * This is very important in small-allocation scenarios where no single block
2616 * is large enough to be discarded on its own. */
2617 static void ERTS_INLINE
mem_discard_coalesce(Allctr_t * allocator,Block_t * neighbor,ErtsMemDiscardRegion * region)2618 mem_discard_coalesce(Allctr_t *allocator, Block_t *neighbor,
2619 ErtsMemDiscardRegion *region)
2620 {
2621 char *neighbor_start;
2622
2623 ASSERT(IS_FREE_BLK(neighbor));
2624
2625 neighbor_start = (char*)neighbor;
2626
2627 if (region->ptr >= neighbor_start) {
2628 char *region_start_page;
2629
2630 region_start_page = region->ptr - SYS_PAGE_SIZE;
2631 region_start_page = (char*)((UWord)region_start_page & ~SYS_PAGE_SZ_MASK);
2632
2633 /* Expand if our first page begins within the previous free block's
2634 * unused data. */
2635 if (region_start_page >= (neighbor_start + allocator->min_block_size)) {
2636 region->size += (region->ptr - region_start_page) - FBLK_FTR_SZ;
2637 region->ptr = region_start_page;
2638 }
2639 } else {
2640 char *region_end_page;
2641 UWord neighbor_size;
2642
2643 ASSERT(region->ptr <= neighbor_start);
2644
2645 region_end_page = region->ptr + region->size + SYS_PAGE_SIZE;
2646 region_end_page = (char*)((UWord)region_end_page & ~SYS_PAGE_SZ_MASK);
2647
2648 neighbor_size = BLK_SZ(neighbor) - FBLK_FTR_SZ;
2649
2650 /* Expand if our last page ends anywhere within the next free block,
2651 * sans the footer we'll inherit. */
2652 if (region_end_page < neighbor_start + neighbor_size) {
2653 region->size += region_end_page - (region->ptr + region->size);
2654 }
2655 }
2656 }
2657
2658 static void ERTS_INLINE
mem_discard_finish(Allctr_t * allocator,Block_t * block,ErtsMemDiscardRegion * region)2659 mem_discard_finish(Allctr_t *allocator, Block_t *block,
2660 ErtsMemDiscardRegion *region)
2661 {
2662 #ifdef DEBUG
2663 char *block_start, *block_end;
2664 UWord block_size;
2665
2666 block_size = BLK_SZ(block);
2667
2668 /* Ensure that the region is completely covered by the legal area of the
2669 * free block. This must hold even when the region is too small to be
2670 * discarded. */
2671 if (region->size > 0) {
2672 ASSERT(block_size > allocator->min_block_size + FBLK_FTR_SZ);
2673
2674 block_start = (char*)block + allocator->min_block_size;
2675 block_end = (char*)block + block_size - FBLK_FTR_SZ;
2676
2677 ASSERT(region->size == 0 ||
2678 (region->ptr + region->size <= block_end &&
2679 region->ptr >= block_start &&
2680 region->size <= block_size));
2681 }
2682 #else
2683 (void)allocator;
2684 (void)block;
2685 #endif
2686
2687 if (region->size > SYS_PAGE_SIZE) {
2688 UWord align_offset, size;
2689 char *ptr;
2690
2691 align_offset = SYS_PAGE_SIZE - ((UWord)region->ptr & SYS_PAGE_SZ_MASK);
2692
2693 size = (region->size - align_offset) & ~SYS_PAGE_SZ_MASK;
2694 ptr = region->ptr + align_offset;
2695
2696 if (size > 0) {
2697 ASSERT(!((UWord)ptr & SYS_PAGE_SZ_MASK));
2698 ASSERT(!(size & SYS_PAGE_SZ_MASK));
2699
2700 erts_mem_discard(ptr, size);
2701 }
2702 }
2703 }
2704
2705 static void
carrier_mem_discard_free_blocks(Allctr_t * allocator,Carrier_t * carrier)2706 carrier_mem_discard_free_blocks(Allctr_t *allocator, Carrier_t *carrier)
2707 {
2708 static const int MAX_BLOCKS_TO_DISCARD = 100;
2709 Block_t *block;
2710 int i;
2711
2712 block = allocator->first_fblk_in_mbc(allocator, carrier);
2713 i = 0;
2714
2715 while (block != NULL && i < MAX_BLOCKS_TO_DISCARD) {
2716 ErtsMemDiscardRegion region;
2717
2718 ASSERT(IS_FREE_BLK(block));
2719
2720 mem_discard_start(allocator, block, ®ion);
2721 mem_discard_finish(allocator, block, ®ion);
2722
2723 block = allocator->next_fblk_in_mbc(allocator, carrier, block);
2724 i++;
2725 }
2726 }
2727
2728 static void
mbc_free(Allctr_t * allctr,ErtsAlcType_t type,void * p,Carrier_t ** busy_pcrr_pp)2729 mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp)
2730 {
2731 ErtsMemDiscardRegion discard_region = {0};
2732 int discard;
2733 Uint is_first_blk;
2734 Uint is_last_blk;
2735 Uint blk_sz;
2736 Block_t *blk;
2737 Block_t *nxt_blk;
2738 Carrier_t *crr;
2739
2740 ASSERT(p);
2741
2742 blk = UMEM2BLK(p);
2743 blk_sz = MBC_ABLK_SZ(blk);
2744
2745 ASSERT(IS_MBC_BLK(blk));
2746 ASSERT(blk_sz >= allctr->min_block_size);
2747
2748 #ifndef DEBUG
2749 /* We want to mark freed blocks as reclaimable to the OS, but it's a fairly
2750 * expensive operation which doesn't do much good if we use it again soon
2751 * after, so we limit it to deallocations on pooled carriers. */
2752 discard = busy_pcrr_pp && *busy_pcrr_pp;
2753 #else
2754 /* Always discard in debug mode, regardless of whether we're in the pool or
2755 * not. */
2756 discard = 1;
2757 #endif
2758
2759 if (discard) {
2760 mem_discard_start(allctr, blk, &discard_region);
2761 }
2762
2763 HARD_CHECK_BLK_CARRIER(allctr, blk);
2764
2765 crr = ABLK_TO_MBC(blk);
2766
2767 ERTS_ALC_CPOOL_FREE_OP(allctr);
2768
2769 STAT_MBC_BLK_FREE(allctr, type, crr, busy_pcrr_pp, blk_sz);
2770
2771 is_first_blk = IS_MBC_FIRST_ABLK(allctr, blk);
2772 is_last_blk = IS_LAST_BLK(blk);
2773
2774 if (IS_PREV_BLK_FREE(blk)) {
2775 ASSERT(!is_first_blk);
2776 /* Coalesce with previous block... */
2777 blk = PREV_BLK(blk);
2778 (*allctr->unlink_free_block)(allctr, blk);
2779
2780 if (discard) {
2781 mem_discard_coalesce(allctr, blk, &discard_region);
2782 }
2783
2784 blk_sz += MBC_FBLK_SZ(blk);
2785 is_first_blk = IS_MBC_FIRST_FBLK(allctr, blk);
2786 SET_MBC_FBLK_SZ(blk, blk_sz);
2787 }
2788 else {
2789 SET_BLK_FREE(blk);
2790 }
2791
2792 if (is_last_blk)
2793 SET_LAST_BLK(blk);
2794 else {
2795 nxt_blk = BLK_AFTER(blk, blk_sz);
2796 if (IS_FREE_BLK(nxt_blk)) {
2797 /* Coalesce with next block... */
2798 (*allctr->unlink_free_block)(allctr, nxt_blk);
2799
2800 if (discard) {
2801 mem_discard_coalesce(allctr, nxt_blk, &discard_region);
2802 }
2803
2804 blk_sz += MBC_FBLK_SZ(nxt_blk);
2805 SET_MBC_FBLK_SZ(blk, blk_sz);
2806
2807 is_last_blk = IS_LAST_BLK(nxt_blk);
2808 if (is_last_blk)
2809 SET_LAST_BLK(blk);
2810 else {
2811 SET_NOT_LAST_BLK(blk);
2812 SET_BLK_SZ_FTR(blk, blk_sz);
2813 }
2814 }
2815 else {
2816 SET_PREV_BLK_FREE(allctr, nxt_blk);
2817 SET_NOT_LAST_BLK(blk);
2818 SET_BLK_SZ_FTR(blk, blk_sz);
2819 }
2820
2821 }
2822
2823 ASSERT(IS_FREE_BLK(blk));
2824 ASSERT(!is_last_blk == !IS_LAST_BLK(blk));
2825 ASSERT(!is_first_blk == !IS_MBC_FIRST_FBLK(allctr, blk));
2826 ASSERT(is_first_blk || IS_PREV_BLK_ALLOCED(blk));
2827 ASSERT(is_last_blk || IS_PREV_BLK_FREE(NXT_BLK(blk)));
2828 ASSERT(blk_sz == MBC_BLK_SZ(blk));
2829 ASSERT(is_last_blk || blk == PREV_BLK(NXT_BLK(blk)));
2830 ASSERT(blk_sz % sizeof(Unit_t) == 0);
2831 ASSERT(IS_MBC_BLK(blk));
2832
2833 if (is_first_blk && is_last_blk && crr != allctr->main_carrier) {
2834 destroy_carrier(allctr, blk, busy_pcrr_pp);
2835 }
2836 else {
2837 (*allctr->link_free_block)(allctr, blk);
2838 HARD_CHECK_BLK_CARRIER(allctr, blk);
2839
2840 if (discard) {
2841 mem_discard_finish(allctr, blk, &discard_region);
2842 }
2843
2844 if (busy_pcrr_pp && *busy_pcrr_pp) {
2845 update_pooled_tree(allctr, crr, blk_sz);
2846 } else {
2847 check_abandon_carrier(allctr, blk, busy_pcrr_pp);
2848 }
2849 }
2850 }
2851
2852 static void *
mbc_realloc(Allctr_t * allctr,ErtsAlcType_t type,void * p,Uint size,Uint32 alcu_flgs,Carrier_t ** busy_pcrr_pp)2853 mbc_realloc(Allctr_t *allctr, ErtsAlcType_t type, void *p, Uint size,
2854 Uint32 alcu_flgs, Carrier_t **busy_pcrr_pp)
2855 {
2856 void *new_p;
2857 Uint old_blk_sz;
2858 Block_t *blk;
2859 #ifndef MBC_REALLOC_ALWAYS_MOVES
2860 Block_t *new_blk, *cand_blk;
2861 Uint cand_blk_sz;
2862 Uint blk_sz, get_blk_sz;
2863 Block_t *nxt_blk;
2864 Uint nxt_blk_sz;
2865 Uint is_last_blk;
2866 #endif /* #ifndef MBC_REALLOC_ALWAYS_MOVES */
2867
2868 ASSERT(p);
2869 ASSERT(size);
2870 ASSERT(size < allctr->sbc_threshold);
2871
2872 blk = (Block_t *) UMEM2BLK(p);
2873 old_blk_sz = MBC_ABLK_SZ(blk);
2874
2875 ASSERT(old_blk_sz >= allctr->min_block_size);
2876
2877 #ifdef MBC_REALLOC_ALWAYS_MOVES
2878 if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
2879 return NULL;
2880 #else /* !MBC_REALLOC_ALWAYS_MOVES */
2881
2882 if (busy_pcrr_pp && *busy_pcrr_pp) {
2883 /*
2884 * Don't want to use carrier in pool
2885 */
2886 new_p = mbc_alloc(allctr, size);
2887 if (!new_p)
2888 return NULL;
2889 new_blk = UMEM2BLK(new_p);
2890 ASSERT(!(IS_MBC_BLK(new_blk) && ABLK_TO_MBC(new_blk) == *busy_pcrr_pp));
2891 sys_memcpy(new_p, p, MIN(size, old_blk_sz - ABLK_HDR_SZ));
2892 mbc_free(allctr, type, p, busy_pcrr_pp);
2893 return new_p;
2894 }
2895
2896 get_blk_sz = blk_sz = UMEMSZ2BLKSZ(allctr, size);
2897
2898 ASSERT(IS_ALLOCED_BLK(blk));
2899 ASSERT(IS_MBC_BLK(blk));
2900
2901 is_last_blk = IS_LAST_BLK(blk);
2902
2903 if (old_blk_sz == blk_sz)
2904 return p;
2905 else if (blk_sz < old_blk_sz) {
2906 /* Shrink block... */
2907 Carrier_t* crr;
2908 Block_t *nxt_nxt_blk;
2909 Uint diff_sz_val = old_blk_sz - blk_sz;
2910 Uint old_blk_sz_val = old_blk_sz;
2911
2912 if (get_blk_sz >= old_blk_sz)
2913 return p;
2914
2915 if (diff_sz_val >= (~((Uint) 0) / 100)) {
2916 /* div both by 128 */
2917 old_blk_sz_val >>= 7;
2918 diff_sz_val >>= 7;
2919 }
2920
2921 /* Avoid fragmentation by moving the block if it is shrunk much */
2922 if (100*diff_sz_val > allctr->mbc_move_threshold*old_blk_sz_val) {
2923 if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
2924 return NULL;
2925
2926 cand_blk_sz = old_blk_sz;
2927 if (!IS_PREV_BLK_FREE(blk)) {
2928 cand_blk = blk;
2929 }
2930 else {
2931 ASSERT(!IS_MBC_FIRST_ABLK(allctr, blk));
2932 cand_blk = PREV_BLK(blk);
2933 cand_blk_sz += PREV_BLK_SZ(blk);
2934 }
2935 if (!is_last_blk) {
2936 nxt_blk = BLK_AFTER(blk, old_blk_sz);
2937 if (IS_FREE_BLK(nxt_blk))
2938 cand_blk_sz += MBC_FBLK_SZ(nxt_blk);
2939 }
2940
2941 new_blk = (*allctr->get_free_block)(allctr,
2942 get_blk_sz,
2943 cand_blk,
2944 cand_blk_sz);
2945 if (new_blk || cand_blk != blk)
2946 goto move_into_new_blk;
2947 }
2948
2949 /* Shrink at current location */
2950
2951 nxt_blk_sz = old_blk_sz - blk_sz;
2952
2953 if ((is_last_blk || IS_ALLOCED_BLK(BLK_AFTER(blk,old_blk_sz)))
2954 && (nxt_blk_sz < allctr->min_block_size))
2955 return p;
2956
2957 HARD_CHECK_BLK_CARRIER(allctr, blk);
2958
2959 nxt_nxt_blk = BLK_AFTER(blk, old_blk_sz);
2960
2961 SET_MBC_ABLK_SZ(blk, blk_sz);
2962 SET_NOT_LAST_BLK(blk);
2963
2964 nxt_blk = BLK_AFTER(blk, blk_sz);
2965
2966 crr = ABLK_TO_MBC(blk);
2967
2968 ERTS_ALC_CPOOL_REALLOC_OP(allctr);
2969 STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
2970 STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
2971
2972 ASSERT(MBC_BLK_SZ(blk) >= allctr->min_block_size);
2973
2974 if (!is_last_blk) {
2975 if (IS_FREE_BLK(nxt_nxt_blk)) {
2976 /* Coalesce with next free block... */
2977 nxt_blk_sz += MBC_FBLK_SZ(nxt_nxt_blk);
2978 (*allctr->unlink_free_block)(allctr, nxt_nxt_blk);
2979
2980 is_last_blk = GET_LAST_BLK_HDR_FLG(nxt_nxt_blk);
2981 }
2982 else {
2983 SET_PREV_BLK_FREE(allctr, nxt_nxt_blk);
2984 }
2985 SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
2986 }
2987
2988 SET_MBC_FBLK_HDR(nxt_blk, nxt_blk_sz,
2989 SBH_THIS_FREE | (is_last_blk ? SBH_LAST_BLK : 0),
2990 crr);
2991
2992 (*allctr->link_free_block)(allctr, nxt_blk);
2993
2994
2995 ASSERT(IS_ALLOCED_BLK(blk));
2996 ASSERT(blk_sz == MBC_BLK_SZ(blk));
2997 ASSERT(blk_sz % sizeof(Unit_t) == 0);
2998 ASSERT(blk_sz >= allctr->min_block_size);
2999 ASSERT(blk_sz >= size + ABLK_HDR_SZ);
3000 ASSERT(IS_MBC_BLK(blk));
3001
3002 ASSERT(IS_FREE_BLK(nxt_blk));
3003 ASSERT(IS_PREV_BLK_ALLOCED(nxt_blk));
3004 ASSERT(nxt_blk_sz == MBC_BLK_SZ(nxt_blk));
3005 ASSERT(nxt_blk_sz % sizeof(Unit_t) == 0);
3006 ASSERT(nxt_blk_sz >= allctr->min_block_size);
3007 ASSERT(IS_MBC_BLK(nxt_blk));
3008 ASSERT(is_last_blk ? IS_LAST_BLK(nxt_blk) : IS_NOT_LAST_BLK(nxt_blk));
3009 ASSERT(is_last_blk || nxt_blk == PREV_BLK(NXT_BLK(nxt_blk)));
3010 ASSERT(is_last_blk || IS_PREV_BLK_FREE(NXT_BLK(nxt_blk)));
3011 ASSERT(FBLK_TO_MBC(nxt_blk) == crr);
3012
3013 HARD_CHECK_BLK_CARRIER(allctr, blk);
3014
3015 check_abandon_carrier(allctr, nxt_blk, NULL);
3016
3017 return p;
3018 }
3019
3020 /* Need larger block... */
3021
3022 if (!is_last_blk) {
3023 nxt_blk = BLK_AFTER(blk, old_blk_sz);
3024 nxt_blk_sz = MBC_BLK_SZ(nxt_blk);
3025 if (IS_FREE_BLK(nxt_blk) && get_blk_sz <= old_blk_sz + nxt_blk_sz) {
3026 Carrier_t* crr = ABLK_TO_MBC(blk);
3027 /* Grow into next block... */
3028
3029 HARD_CHECK_BLK_CARRIER(allctr, blk);
3030
3031 (*allctr->unlink_free_block)(allctr, nxt_blk);
3032 nxt_blk_sz -= blk_sz - old_blk_sz;
3033
3034 is_last_blk = IS_LAST_BLK(nxt_blk);
3035 if (nxt_blk_sz < allctr->min_block_size) {
3036 blk_sz += nxt_blk_sz;
3037
3038 SET_MBC_ABLK_SZ(blk, blk_sz);
3039
3040 if (is_last_blk) {
3041 SET_LAST_BLK(blk);
3042 #ifdef DEBUG
3043 nxt_blk = NULL;
3044 #endif
3045 }
3046 else {
3047 nxt_blk = BLK_AFTER(blk, blk_sz);
3048 SET_PREV_BLK_ALLOCED(nxt_blk);
3049 #ifdef DEBUG
3050 is_last_blk = IS_LAST_BLK(nxt_blk);
3051 nxt_blk_sz = MBC_BLK_SZ(nxt_blk);
3052 #endif
3053 }
3054 }
3055 else {
3056 SET_MBC_ABLK_SZ(blk, blk_sz);
3057
3058 nxt_blk = BLK_AFTER(blk, blk_sz);
3059 SET_MBC_FBLK_HDR(nxt_blk, nxt_blk_sz, SBH_THIS_FREE, crr);
3060
3061 if (is_last_blk)
3062 SET_LAST_BLK(nxt_blk);
3063 else
3064 SET_BLK_SZ_FTR(nxt_blk, nxt_blk_sz);
3065
3066 (*allctr->link_free_block)(allctr, nxt_blk);
3067
3068 ASSERT(IS_FREE_BLK(nxt_blk));
3069 ASSERT(FBLK_TO_MBC(nxt_blk) == crr);
3070 }
3071
3072 ERTS_ALC_CPOOL_REALLOC_OP(allctr);
3073 STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
3074 STAT_MBC_BLK_ALLOC(allctr, crr, blk_sz);
3075
3076 ASSERT(IS_ALLOCED_BLK(blk));
3077 ASSERT(blk_sz == MBC_BLK_SZ(blk));
3078 ASSERT(blk_sz % sizeof(Unit_t) == 0);
3079 ASSERT(blk_sz >= allctr->min_block_size);
3080 ASSERT(blk_sz >= size + ABLK_HDR_SZ);
3081 ASSERT(IS_MBC_BLK(blk));
3082
3083 ASSERT(!nxt_blk || IS_PREV_BLK_ALLOCED(nxt_blk));
3084 ASSERT(!nxt_blk || nxt_blk_sz == MBC_BLK_SZ(nxt_blk));
3085 ASSERT(!nxt_blk || nxt_blk_sz % sizeof(Unit_t) == 0);
3086 ASSERT(!nxt_blk || nxt_blk_sz >= allctr->min_block_size);
3087 ASSERT(!nxt_blk || IS_MBC_BLK(nxt_blk));
3088 ASSERT(!nxt_blk || (is_last_blk
3089 ? IS_LAST_BLK(nxt_blk)
3090 : IS_NOT_LAST_BLK(nxt_blk)));
3091 ASSERT(!nxt_blk || is_last_blk
3092 || IS_ALLOCED_BLK(nxt_blk)
3093 || nxt_blk == PREV_BLK(NXT_BLK(nxt_blk)));
3094 ASSERT(!nxt_blk || is_last_blk
3095 || IS_ALLOCED_BLK(nxt_blk)
3096 || IS_PREV_BLK_FREE(NXT_BLK(nxt_blk)));
3097
3098 HARD_CHECK_BLK_CARRIER(allctr, blk);
3099
3100 return p;
3101 }
3102 }
3103
3104 if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
3105 return NULL;
3106
3107 /* Need to grow in another block */
3108
3109 if (!IS_PREV_BLK_FREE(blk)) {
3110 cand_blk = NULL;
3111 cand_blk_sz = 0;
3112 }
3113 else {
3114 ASSERT(!IS_MBC_FIRST_ABLK(allctr, blk));
3115 cand_blk = PREV_BLK(blk);
3116 cand_blk_sz = old_blk_sz + PREV_BLK_SZ(blk);
3117
3118 if (!is_last_blk) {
3119 nxt_blk = BLK_AFTER(blk, old_blk_sz);
3120 if (IS_FREE_BLK(nxt_blk))
3121 cand_blk_sz += MBC_FBLK_SZ(nxt_blk);
3122 }
3123 }
3124
3125 if (cand_blk_sz < get_blk_sz) {
3126 /* We wont fit in cand_blk get a new one */
3127
3128 #endif /* !MBC_REALLOC_ALWAYS_MOVES */
3129
3130 new_p = mbc_alloc(allctr, size);
3131 if (!new_p)
3132 return NULL;
3133 sys_memcpy(new_p, p, MIN(size, old_blk_sz - ABLK_HDR_SZ));
3134 mbc_free(allctr, type, p, busy_pcrr_pp);
3135
3136 return new_p;
3137
3138 #ifndef MBC_REALLOC_ALWAYS_MOVES
3139
3140 }
3141 else {
3142 /* We will at least fit in cand_blk */
3143
3144 new_blk = (*allctr->get_free_block)(allctr,
3145 get_blk_sz,
3146 cand_blk,
3147 cand_blk_sz);
3148 move_into_new_blk:
3149 /*
3150 * new_blk, and cand_blk have to be correctly set
3151 * when jumping to this label.
3152 */
3153
3154 if (new_blk) {
3155 mbc_alloc_finalize(allctr,
3156 new_blk,
3157 MBC_FBLK_SZ(new_blk),
3158 GET_BLK_HDR_FLGS(new_blk),
3159 FBLK_TO_MBC(new_blk),
3160 blk_sz,
3161 1);
3162 new_p = BLK2UMEM(new_blk);
3163 sys_memcpy(new_p, p, MIN(size, old_blk_sz - ABLK_HDR_SZ));
3164 mbc_free(allctr, type, p, NULL);
3165 return new_p;
3166 }
3167 else {
3168 Carrier_t* crr;
3169 Uint new_blk_sz;
3170 UWord new_blk_flgs;
3171 Uint prev_blk_sz;
3172 Uint blk_cpy_sz;
3173
3174 ASSERT(IS_PREV_BLK_FREE(blk));
3175 ASSERT(cand_blk == PREV_BLK(blk));
3176
3177 prev_blk_sz = PREV_BLK_SZ(blk);
3178 new_blk = cand_blk;
3179 new_blk_sz = prev_blk_sz + old_blk_sz;
3180 new_blk_flgs = GET_BLK_HDR_FLGS(new_blk);
3181
3182 HARD_CHECK_BLK_CARRIER(allctr, blk);
3183
3184 (*allctr->unlink_free_block)(allctr, new_blk); /* prev */
3185
3186 if (is_last_blk)
3187 new_blk_flgs |= LAST_BLK_HDR_FLG;
3188 else {
3189 nxt_blk = BLK_AFTER(blk, old_blk_sz);
3190 if (IS_FREE_BLK(nxt_blk)) {
3191 new_blk_flgs |= GET_LAST_BLK_HDR_FLG(nxt_blk);
3192 new_blk_sz += MBC_FBLK_SZ(nxt_blk);
3193 (*allctr->unlink_free_block)(allctr, nxt_blk);
3194 }
3195 }
3196
3197 /*
3198 * Copy user-data then update new blocks in mbc_alloc_finalize().
3199 * mbc_alloc_finalize() may write headers at old location of
3200 * user data; therfore, order is important.
3201 */
3202
3203 new_p = BLK2UMEM(new_blk);
3204 blk_cpy_sz = MIN(blk_sz, old_blk_sz);
3205 crr = FBLK_TO_MBC(new_blk);
3206
3207 if (prev_blk_sz >= blk_cpy_sz)
3208 sys_memcpy(new_p, p, blk_cpy_sz - ABLK_HDR_SZ);
3209 else
3210 sys_memmove(new_p, p, blk_cpy_sz - ABLK_HDR_SZ);
3211
3212 mbc_alloc_finalize(allctr,
3213 new_blk,
3214 new_blk_sz,
3215 new_blk_flgs,
3216 crr,
3217 blk_sz,
3218 0);
3219
3220 ERTS_ALC_CPOOL_FREE_OP(allctr);
3221 STAT_MBC_BLK_FREE(allctr, type, crr, NULL, old_blk_sz);
3222
3223 return new_p;
3224 }
3225 }
3226 #endif /* !MBC_REALLOC_ALWAYS_MOVES */
3227 }
3228
3229
3230 #define ERTS_ALC_MAX_DEALLOC_CARRIER 10
3231 #define ERTS_ALC_CPOOL_MAX_FETCH_INSPECT 100
3232 #define ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS 3
3233
3234 #define ERTS_ALC_CPOOL_PTR_MOD_MRK (((erts_aint_t) 1) << 0)
3235 #define ERTS_ALC_CPOOL_PTR_DEL_MRK (((erts_aint_t) 1) << 1)
3236
3237 #define ERTS_ALC_CPOOL_PTR_MRKS \
3238 (ERTS_ALC_CPOOL_PTR_MOD_MRK | ERTS_ALC_CPOOL_PTR_DEL_MRK)
3239
3240 /*
3241 * When setting multiple mod markers we always
3242 * set mod markers in pointer order and always
3243 * on next pointers before prev pointers.
3244 */
3245
3246 typedef union {
3247 ErtsAlcCPoolData_t sentinel;
3248 char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsAlcCPoolData_t))];
3249 } ErtsAlcCrrPool_t;
3250
3251 static ErtsAlcCrrPool_t firstfit_carrier_pools[ERTS_ALC_NO_CPOOLS] erts_align_attribute(ERTS_CACHE_LINE_SIZE);
3252
3253 #define ERTS_ALC_CPOOL_MAX_BACKOFF (1 << 8)
3254
3255 static int
backoff(int n)3256 backoff(int n)
3257 {
3258 int i;
3259
3260 for (i = 0; i < n; i++)
3261 ERTS_SPIN_BODY;
3262
3263 if (n >= ERTS_ALC_CPOOL_MAX_BACKOFF)
3264 return ERTS_ALC_CPOOL_MAX_BACKOFF;
3265 else
3266 return n << 1;
3267 }
3268
3269 static int
cpool_dbg_is_in_pool(Allctr_t * allctr,Carrier_t * crr)3270 cpool_dbg_is_in_pool(Allctr_t *allctr, Carrier_t *crr)
3271 {
3272 ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
3273 ErtsAlcCPoolData_t *cpdp = sentinel;
3274 Carrier_t *tmp_crr;
3275
3276 while (1) {
3277 cpdp = (ErtsAlcCPoolData_t *) (erts_atomic_read_ddrb(&cpdp->next) & ~CRR_FLG_MASK);
3278 if (cpdp == sentinel)
3279 return 0;
3280 tmp_crr = (Carrier_t *) (((char *) cpdp) - offsetof(Carrier_t, cpool));
3281 if (tmp_crr == crr)
3282 return 1;
3283 }
3284 }
3285
3286 static int
cpool_is_empty(Allctr_t * allctr)3287 cpool_is_empty(Allctr_t *allctr)
3288 {
3289 ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
3290 return ((erts_atomic_read_rb(&sentinel->next) == (erts_aint_t) sentinel)
3291 && (erts_atomic_read_rb(&sentinel->prev) == (erts_aint_t) sentinel));
3292 }
3293
3294 static ERTS_INLINE ErtsAlcCPoolData_t *
cpool_aint2cpd(erts_aint_t aint)3295 cpool_aint2cpd(erts_aint_t aint)
3296 {
3297 return (ErtsAlcCPoolData_t *) (aint & ~ERTS_ALC_CPOOL_PTR_MRKS);
3298 }
3299
3300 static ERTS_INLINE erts_aint_t
cpool_read(erts_atomic_t * aptr)3301 cpool_read(erts_atomic_t *aptr)
3302 {
3303 return erts_atomic_read_acqb(aptr);
3304 }
3305
3306 static ERTS_INLINE void
cpool_init(erts_atomic_t * aptr,erts_aint_t val)3307 cpool_init(erts_atomic_t *aptr, erts_aint_t val)
3308 {
3309 erts_atomic_set_nob(aptr, val);
3310 }
3311
3312 static ERTS_INLINE void
cpool_set_mod_marked(erts_atomic_t * aptr,erts_aint_t new,erts_aint_t old)3313 cpool_set_mod_marked(erts_atomic_t *aptr, erts_aint_t new, erts_aint_t old)
3314 {
3315 #ifdef ERTS_ALC_CPOOL_DEBUG
3316 erts_aint_t act = erts_atomic_xchg_relb(aptr, new);
3317 ERTS_ALC_CPOOL_ASSERT(act == (old | ERTS_ALC_CPOOL_PTR_MOD_MRK));
3318 #else
3319 erts_atomic_set_relb(aptr, new);
3320 #endif
3321 }
3322
3323
3324 static ERTS_INLINE erts_aint_t
cpool_try_mod_mark_exp(erts_atomic_t * aptr,erts_aint_t exp)3325 cpool_try_mod_mark_exp(erts_atomic_t *aptr, erts_aint_t exp)
3326 {
3327 ERTS_ALC_CPOOL_ASSERT((exp & ERTS_ALC_CPOOL_PTR_MOD_MRK) == 0);
3328 return erts_atomic_cmpxchg_nob(aptr, exp | ERTS_ALC_CPOOL_PTR_MOD_MRK, exp);
3329 }
3330
3331 static ERTS_INLINE erts_aint_t
cpool_mod_mark_exp(erts_atomic_t * aptr,erts_aint_t exp)3332 cpool_mod_mark_exp(erts_atomic_t *aptr, erts_aint_t exp)
3333 {
3334 int b;
3335 erts_aint_t act;
3336 ERTS_ALC_CPOOL_ASSERT((exp & ERTS_ALC_CPOOL_PTR_MOD_MRK) == 0);
3337 while (1) {
3338 act = erts_atomic_cmpxchg_nob(aptr,
3339 exp | ERTS_ALC_CPOOL_PTR_MOD_MRK,
3340 exp);
3341 if (act == exp)
3342 return exp;
3343 b = 1;
3344 do {
3345 if ((act & ~ERTS_ALC_CPOOL_PTR_MOD_MRK) != exp)
3346 return act;
3347 b = backoff(b);
3348 act = erts_atomic_read_nob(aptr);
3349 } while (act != exp);
3350 }
3351 }
3352
3353 static ERTS_INLINE erts_aint_t
cpool_mod_mark(erts_atomic_t * aptr)3354 cpool_mod_mark(erts_atomic_t *aptr)
3355 {
3356 int b;
3357 erts_aint_t act, exp;
3358 act = cpool_read(aptr);
3359 while (1) {
3360 b = 1;
3361 while (act & ERTS_ALC_CPOOL_PTR_MOD_MRK) {
3362 b = backoff(b);
3363 act = erts_atomic_read_nob(aptr);
3364 }
3365 exp = act;
3366 act = erts_atomic_cmpxchg_acqb(aptr,
3367 exp | ERTS_ALC_CPOOL_PTR_MOD_MRK,
3368 exp);
3369 if (act == exp)
3370 return exp;
3371 }
3372 }
3373
3374 static void
cpool_insert(Allctr_t * allctr,Carrier_t * crr)3375 cpool_insert(Allctr_t *allctr, Carrier_t *crr)
3376 {
3377 ErtsAlcCPoolData_t *cpd1p, *cpd2p;
3378 erts_aint_t val;
3379 ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
3380 Allctr_t *orig_allctr = crr->cpool.orig_allctr;
3381 int ix;
3382
3383 ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
3384 || erts_thr_progress_is_managed_thread());
3385
3386 /* Add the carrier's block statistics to the pool. */
3387 for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
3388 erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
3389 ((erts_aint_t) crr->cpool.blocks_size[ix]));
3390 erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[ix],
3391 ((erts_aint_t) crr->cpool.blocks[ix]));
3392 }
3393
3394 erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
3395 (erts_aint_t) CARRIER_SZ(crr));
3396 erts_atomic_inc_nob(&orig_allctr->cpool.stat.no_carriers);
3397
3398 /*
3399 * We search in 'next' direction and begin by passing
3400 * one element before trying to insert. This in order to
3401 * avoid contention with threads fetching elements.
3402 */
3403
3404 val = cpool_read(&sentinel->next);
3405
3406 /* Find a predecessor to be, and set mod marker on its next ptr */
3407
3408 while (1) {
3409 cpd1p = cpool_aint2cpd(val);
3410 if (cpd1p == sentinel) {
3411 val = cpool_mod_mark(&cpd1p->next);
3412 break;
3413 }
3414 val = cpool_read(&cpd1p->next);
3415 if (!(val & ERTS_ALC_CPOOL_PTR_MRKS)) {
3416 erts_aint_t tmp = cpool_try_mod_mark_exp(&cpd1p->next, val);
3417 if (tmp == val) {
3418 val = tmp;
3419 break;
3420 }
3421 val = tmp;
3422 }
3423 }
3424
3425 /* Set mod marker on prev ptr of the to be successor */
3426
3427 cpd2p = cpool_aint2cpd(val);
3428
3429 cpool_init(&crr->cpool.next, (erts_aint_t) cpd2p);
3430 cpool_init(&crr->cpool.prev, (erts_aint_t) cpd1p);
3431
3432 val = (erts_aint_t) cpd1p;
3433
3434 while (1) {
3435 int b;
3436 erts_aint_t tmp;
3437
3438 tmp = cpool_mod_mark_exp(&cpd2p->prev, val);
3439 if (tmp == val)
3440 break;
3441 b = 1;
3442 do {
3443 b = backoff(b);
3444 tmp = cpool_read(&cpd2p->prev);
3445 } while (tmp != val);
3446 }
3447
3448 /* Write pointers to this element in successor and predecessor */
3449
3450 cpool_set_mod_marked(&cpd1p->next,
3451 (erts_aint_t) &crr->cpool,
3452 (erts_aint_t) cpd2p);
3453 cpool_set_mod_marked(&cpd2p->prev,
3454 (erts_aint_t) &crr->cpool,
3455 (erts_aint_t) cpd1p);
3456
3457 LTTNG3(carrier_pool_put, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, CARRIER_SZ(crr));
3458 }
3459
3460 static void
cpool_delete(Allctr_t * allctr,Allctr_t * prev_allctr,Carrier_t * crr)3461 cpool_delete(Allctr_t *allctr, Allctr_t *prev_allctr, Carrier_t *crr)
3462 {
3463 ErtsAlcCPoolData_t *cpd1p, *cpd2p;
3464 erts_aint_t val;
3465 #ifdef ERTS_ALC_CPOOL_DEBUG
3466 ErtsAlcCPoolData_t *sentinel = allctr->cpool.sentinel;
3467 #endif
3468 Allctr_t *orig_allctr = crr->cpool.orig_allctr;
3469 int ix;
3470
3471 ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
3472 || erts_thr_progress_is_managed_thread());
3473 ERTS_ALC_CPOOL_ASSERT(sentinel != &crr->cpool);
3474
3475 ERTS_ALC_CPOOL_ASSERT(orig_allctr == prev_allctr);
3476
3477 /* Set mod marker on next ptr of our predecessor */
3478
3479 val = (erts_aint_t) &crr->cpool;
3480 while (1) {
3481 erts_aint_t tmp;
3482 cpd1p = cpool_aint2cpd(cpool_read(&crr->cpool.prev));
3483 tmp = cpool_mod_mark_exp(&cpd1p->next, val);
3484 if (tmp == val)
3485 break;
3486 }
3487
3488 /* Set mod marker on our next ptr */
3489
3490 val = cpool_mod_mark(&crr->cpool.next);
3491
3492 /* Set mod marker on the prev ptr of our successor */
3493
3494 cpd2p = cpool_aint2cpd(val);
3495
3496 val = (erts_aint_t) &crr->cpool;
3497
3498 while (1) {
3499 int b;
3500 erts_aint_t tmp;
3501
3502 tmp = cpool_mod_mark_exp(&cpd2p->prev, val);
3503 if (tmp == val)
3504 break;
3505 b = 1;
3506 do {
3507 b = backoff(b);
3508 tmp = cpool_read(&cpd2p->prev);
3509 } while (tmp != val);
3510 }
3511
3512 /* Set mod marker on our prev ptr */
3513
3514 val = (erts_aint_t) cpd1p;
3515
3516 while (1) {
3517 int b;
3518 erts_aint_t tmp;
3519
3520 tmp = cpool_mod_mark_exp(&crr->cpool.prev, val);
3521 if (tmp == val)
3522 break;
3523 b = 1;
3524 do {
3525 b = backoff(b);
3526 tmp = cpool_read(&cpd2p->prev);
3527 } while (tmp != val);
3528 }
3529
3530 /* Write pointers past this element in predecessor and successor */
3531
3532 cpool_set_mod_marked(&cpd1p->next,
3533 (erts_aint_t) cpd2p,
3534 (erts_aint_t) &crr->cpool);
3535 cpool_set_mod_marked(&cpd2p->prev,
3536 (erts_aint_t) cpd1p,
3537 (erts_aint_t) &crr->cpool);
3538
3539 /* Repleace mod markers with delete markers on this element */
3540 cpool_set_mod_marked(&crr->cpool.next,
3541 ((erts_aint_t) cpd2p) | ERTS_ALC_CPOOL_PTR_DEL_MRK,
3542 ((erts_aint_t) cpd2p) | ERTS_ALC_CPOOL_PTR_MOD_MRK);
3543 cpool_set_mod_marked(&crr->cpool.prev,
3544 ((erts_aint_t) cpd1p) | ERTS_ALC_CPOOL_PTR_DEL_MRK,
3545 ((erts_aint_t) cpd1p) | ERTS_ALC_CPOOL_PTR_MOD_MRK);
3546
3547 crr->cpool.thr_prgr = erts_thr_progress_later(NULL);
3548
3549 /* Subtract the carrier's block statistics from the pool. */
3550 for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
3551 #ifdef ERTS_ALC_CPOOL_DEBUG
3552 SWord new_blk_sz, new_blk_no;
3553
3554 new_blk_sz =
3555 erts_atomic_add_read_nob(&orig_allctr->cpool.stat.blocks_size[ix],
3556 -((erts_aint_t)crr->cpool.blocks_size[ix]));
3557 new_blk_no =
3558 erts_atomic_add_read_nob(&orig_allctr->cpool.stat.no_blocks[ix],
3559 -((erts_aint_t)crr->cpool.blocks[ix]));
3560
3561 ERTS_ALC_CPOOL_ASSERT(new_blk_sz >= 0);
3562 ERTS_ALC_CPOOL_ASSERT(new_blk_no >= 0);
3563 #else
3564 erts_atomic_add_nob(&orig_allctr->cpool.stat.blocks_size[ix],
3565 -((erts_aint_t) crr->cpool.blocks_size[ix]));
3566 erts_atomic_add_nob(&orig_allctr->cpool.stat.no_blocks[ix],
3567 -((erts_aint_t) crr->cpool.blocks[ix]));
3568 #endif
3569 }
3570
3571 erts_atomic_add_nob(&orig_allctr->cpool.stat.carriers_size,
3572 -((erts_aint_t) CARRIER_SZ(crr)));
3573 erts_atomic_dec_wb(&orig_allctr->cpool.stat.no_carriers);
3574 }
3575
3576 static Carrier_t *
cpool_fetch(Allctr_t * allctr,UWord size)3577 cpool_fetch(Allctr_t *allctr, UWord size)
3578 {
3579 int i, seen_sentinel;
3580 Carrier_t *crr;
3581 Carrier_t *reinsert_crr = NULL;
3582 ErtsAlcCPoolData_t *cpdp;
3583 ErtsAlcCPoolData_t *cpool_entrance = NULL;
3584 ErtsAlcCPoolData_t *sentinel;
3585
3586 ERTS_ALC_CPOOL_ASSERT(allctr->alloc_no == ERTS_ALC_A_TEST /* testcase */
3587 || erts_thr_progress_is_managed_thread());
3588
3589 i = ERTS_ALC_CPOOL_MAX_FETCH_INSPECT;
3590
3591 LTTNG3(carrier_pool_get, ERTS_ALC_A2AD(allctr->alloc_no), allctr->ix, (unsigned long)size);
3592 /*
3593 * Search my own pooled_tree,
3594 * i.e my abandoned carriers that were in the pool last time I checked.
3595 */
3596 do {
3597 erts_aint_t exp, act;
3598
3599 crr = aoff_lookup_pooled_mbc(allctr, size);
3600 if (!crr)
3601 break;
3602
3603 ASSERT(crr->cpool.state == ERTS_MBC_WAS_POOLED);
3604 ASSERT(crr->cpool.orig_allctr == allctr);
3605
3606 aoff_remove_pooled_mbc(allctr, crr);
3607
3608 exp = erts_atomic_read_nob(&crr->allctr);
3609 if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
3610 ASSERT((exp & ~ERTS_CRR_ALCTR_FLG_MASK) == (erts_aint_t)allctr);
3611 if (erts_atomic_read_nob(&crr->cpool.max_size) < size) {
3612 /*
3613 * This carrier has been fetched and inserted back again
3614 * by a foreign allocator. That's why it has a stale search size.
3615 */
3616 ASSERT(exp & ERTS_CRR_ALCTR_FLG_HOMECOMING);
3617 crr->cpool.pooled.hdr.bhdr = erts_atomic_read_nob(&crr->cpool.max_size);
3618 aoff_add_pooled_mbc(allctr, crr);
3619 INC_CC(allctr->cpool.stat.skip_size);
3620 continue;
3621 }
3622 else if (exp & ERTS_CRR_ALCTR_FLG_BUSY) {
3623 /*
3624 * This must be our own carrier as part of a realloc call.
3625 * Skip it to make things simpler.
3626 * Must wait to re-insert to not be found again by lookup.
3627 */
3628 ASSERT(!reinsert_crr);
3629 reinsert_crr = crr;
3630 INC_CC(allctr->cpool.stat.skip_busy);
3631 continue;
3632 }
3633
3634 /* Try to fetch it... */
3635 act = erts_atomic_cmpxchg_mb(&crr->allctr,
3636 exp & ~ERTS_CRR_ALCTR_FLG_IN_POOL,
3637 exp);
3638 if (act == exp) {
3639 cpool_delete(allctr, allctr, crr);
3640 crr->cpool.state = ERTS_MBC_IS_HOME;
3641
3642 if (reinsert_crr)
3643 aoff_add_pooled_mbc(allctr, reinsert_crr);
3644 return crr;
3645 }
3646 exp = act;
3647 INC_CC(allctr->cpool.stat.skip_race);
3648 }
3649 else
3650 INC_CC(allctr->cpool.stat.skip_not_pooled);
3651
3652 /* Not in pool anymore */
3653 ASSERT(!(exp & ERTS_CRR_ALCTR_FLG_BUSY));
3654 crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
3655
3656 }while (--i > 0);
3657
3658 if (reinsert_crr)
3659 aoff_add_pooled_mbc(allctr, reinsert_crr);
3660
3661 /*
3662 * Try find a nice cpool_entrance
3663 */
3664 while (allctr->cpool.pooled_tree) {
3665 erts_aint_t iallctr;
3666
3667 crr = ErtsContainerStruct(allctr->cpool.pooled_tree, Carrier_t, cpool.pooled);
3668 iallctr = erts_atomic_read_nob(&crr->allctr);
3669 if (iallctr & ERTS_CRR_ALCTR_FLG_IN_POOL) {
3670 cpool_entrance = &crr->cpool;
3671 break;
3672 }
3673 /* Not in pool anymore */
3674 ASSERT(!(iallctr & ERTS_CRR_ALCTR_FLG_BUSY));
3675 aoff_remove_pooled_mbc(allctr, crr);
3676 crr->cpool.state = ERTS_MBC_WAS_TRAITOR;
3677
3678 if (--i <= 0) {
3679 INC_CC(allctr->cpool.stat.fail_pooled);
3680 return NULL;
3681 }
3682 }
3683
3684
3685 /*
3686 * Finally search the shared pool and try employ foreign carriers
3687 */
3688 sentinel = allctr->cpool.sentinel;
3689 if (cpool_entrance) {
3690 /*
3691 * We saw a pooled carried above, use it as entrance into the pool
3692 */
3693 }
3694 else {
3695 /*
3696 * No pooled carrier seen above. Start search at cpool sentinel,
3697 * but begin by passing one element before trying to fetch.
3698 * This in order to avoid contention with threads inserting elements.
3699 */
3700 cpool_entrance = cpool_aint2cpd(cpool_read(&sentinel->prev));
3701 if (cpool_entrance == sentinel)
3702 goto check_dc_list;
3703 }
3704
3705 cpdp = cpool_entrance;
3706 seen_sentinel = 0;
3707 do {
3708 erts_aint_t exp;
3709 cpdp = cpool_aint2cpd(cpool_read(&cpdp->prev));
3710 if (cpdp == sentinel) {
3711 if (seen_sentinel) {
3712 /* We been here before. cpool_entrance must have been removed */
3713 INC_CC(allctr->cpool.stat.entrance_removed);
3714 break;
3715 }
3716 seen_sentinel = 1;
3717 continue;
3718 }
3719 ASSERT(cpdp != cpool_entrance || seen_sentinel);
3720
3721 crr = ErtsContainerStruct(cpdp, Carrier_t, cpool);
3722 exp = erts_atomic_read_rb(&crr->allctr);
3723
3724 if (erts_atomic_read_nob(&cpdp->max_size) < size) {
3725 INC_CC(allctr->cpool.stat.skip_size);
3726 }
3727 else if ((exp & (ERTS_CRR_ALCTR_FLG_IN_POOL | ERTS_CRR_ALCTR_FLG_BUSY))
3728 == ERTS_CRR_ALCTR_FLG_IN_POOL) {
3729 erts_aint_t act;
3730 erts_aint_t want = (((erts_aint_t) allctr)
3731 | (exp & ERTS_CRR_ALCTR_FLG_HOMECOMING));
3732 /* Try to fetch it... */
3733 act = erts_atomic_cmpxchg_mb(&crr->allctr, want, exp);
3734 if (act == exp) {
3735 cpool_delete(allctr, ((Allctr_t *) (act & ~ERTS_CRR_ALCTR_FLG_MASK)), crr);
3736 if (crr->cpool.orig_allctr == allctr) {
3737 unlink_abandoned_carrier(crr);
3738 crr->cpool.state = ERTS_MBC_IS_HOME;
3739 }
3740 return crr;
3741 }
3742 }
3743
3744 if (exp & ERTS_CRR_ALCTR_FLG_BUSY)
3745 INC_CC(allctr->cpool.stat.skip_busy);
3746 else
3747 INC_CC(allctr->cpool.stat.skip_race);
3748
3749 if (--i <= 0) {
3750 INC_CC(allctr->cpool.stat.fail_shared);
3751 return NULL;
3752 }
3753 }while (cpdp != cpool_entrance);
3754
3755 check_dc_list:
3756 /* Last; check our own pending dealloc carrier list... */
3757 crr = allctr->cpool.dc_list.last;
3758 while (crr) {
3759 if (erts_atomic_read_nob(&crr->cpool.max_size) >= size) {
3760 Block_t* blk;
3761 unlink_carrier(&allctr->cpool.dc_list, crr);
3762 ERTS_ALC_CPOOL_ASSERT(erts_atomic_read_nob(&crr->allctr)
3763 == ((erts_aint_t) allctr));
3764 blk = MBC_TO_FIRST_BLK(allctr, crr);
3765 ASSERT(FBLK_TO_MBC(blk) == crr);
3766 allctr->link_free_block(allctr, blk);
3767 return crr;
3768 }
3769 crr = crr->prev;
3770 if (--i <= 0) {
3771 INC_CC(allctr->cpool.stat.fail_pend_dealloc);
3772 return NULL;
3773 }
3774 }
3775
3776 if (i != ERTS_ALC_CPOOL_MAX_FETCH_INSPECT)
3777 INC_CC(allctr->cpool.stat.fail);
3778
3779 return NULL;
3780 }
3781
3782 static void
check_pending_dealloc_carrier(Allctr_t * allctr,int * need_thr_progress,ErtsThrPrgrVal * thr_prgr_p,int * need_more_work)3783 check_pending_dealloc_carrier(Allctr_t *allctr,
3784 int *need_thr_progress,
3785 ErtsThrPrgrVal *thr_prgr_p,
3786 int *need_more_work)
3787 {
3788 Carrier_t *crr = allctr->cpool.dc_list.first;
3789
3790 if (crr) {
3791 ErtsThrPrgrVal current = erts_thr_progress_current();
3792 int i = 0;
3793
3794 do {
3795 Carrier_t *dcrr;
3796
3797 if (!erts_thr_progress_has_reached_this(current, crr->cpool.thr_prgr))
3798 break;
3799
3800 dcrr = crr;
3801 crr = crr->next;
3802 dealloc_mbc(allctr, dcrr);
3803 i++;
3804 } while (crr && i < ERTS_ALC_MAX_DEALLOC_CARRIER);
3805
3806 allctr->cpool.dc_list.first = crr;
3807 if (!crr)
3808 allctr->cpool.dc_list.last = NULL;
3809 else {
3810 crr->prev = NULL;
3811
3812 if (need_more_work) {
3813 ERTS_ALC_CPOOL_ASSERT(need_thr_progress && thr_prgr_p);
3814 if (erts_thr_progress_has_reached_this(current, crr->cpool.thr_prgr))
3815 *need_more_work = 1;
3816 else {
3817 *need_thr_progress = 1;
3818 if (*thr_prgr_p == ERTS_THR_PRGR_INVALID
3819 || erts_thr_progress_cmp(crr->cpool.thr_prgr,
3820 *thr_prgr_p) < 0) {
3821 *thr_prgr_p = crr->cpool.thr_prgr;
3822 }
3823 }
3824 }
3825 }
3826 }
3827 }
3828
3829 static void
schedule_dealloc_carrier(Allctr_t * allctr,Carrier_t * crr)3830 schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
3831 {
3832 Allctr_t *orig_allctr;
3833
3834 ASSERT(IS_MB_CARRIER(crr));
3835
3836 if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
3837 dealloc_mbc(allctr, crr);
3838 return;
3839 }
3840
3841 orig_allctr = crr->cpool.orig_allctr;
3842
3843 if (allctr == orig_allctr) {
3844 if (!(erts_atomic_read_nob(&crr->allctr) & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
3845 dealloc_my_carrier(allctr, crr);
3846 }
3847 /*else
3848 * Carrier was abandoned earlier by other thread and
3849 * is still waiting for us in dd-queue.
3850 * handle_delayed_dealloc() will handle it when crr is dequeued.
3851 */
3852 }
3853 else {
3854 /*
3855 * We send the carrier to its origin for deallocation.
3856 * This in order:
3857 * - not to complicate things for the thread specific
3858 * instances of mseg_alloc, and
3859 * - to ensure that we always only reuse empty carriers
3860 * originating from our own thread specific mseg_alloc
3861 * instance which is beneficial on NUMA systems.
3862 */
3863 erts_aint_t iallctr;
3864 #ifdef ERTS_ALC_CPOOL_DEBUG
3865 Block_t* first_blk = MBC_TO_FIRST_BLK(allctr, crr);
3866 ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(first_blk));
3867
3868 ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, first_blk));
3869 ERTS_ALC_CPOOL_ASSERT(crr == FBLK_TO_MBC(first_blk));
3870 ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, first_blk));
3871 ERTS_ALC_CPOOL_ASSERT((erts_atomic_read_nob(&crr->allctr)
3872 & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
3873 == (erts_aint_t) allctr);
3874 #endif
3875
3876 iallctr = (erts_aint_t)orig_allctr | ERTS_CRR_ALCTR_FLG_HOMECOMING;
3877 if (!(erts_atomic_xchg_nob(&crr->allctr, iallctr)
3878 & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
3879 enqueue_homecoming(allctr, crr);
3880 }
3881 }
3882 }
3883
dealloc_my_carrier(Allctr_t * allctr,Carrier_t * crr)3884 static void dealloc_my_carrier(Allctr_t *allctr, Carrier_t *crr)
3885 {
3886 Block_t *blk;
3887 int check_pending_dealloc;
3888 erts_aint_t max_size;
3889
3890 ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
3891 if (is_abandoned(crr)) {
3892 unlink_abandoned_carrier(crr);
3893 crr->cpool.state = ERTS_MBC_IS_HOME;
3894 }
3895
3896 if (crr->cpool.thr_prgr == ERTS_THR_PRGR_INVALID
3897 || erts_thr_progress_has_reached(crr->cpool.thr_prgr)) {
3898 dealloc_mbc(allctr, crr);
3899 return;
3900 }
3901
3902 blk = MBC_TO_FIRST_BLK(allctr, crr);
3903 ASSERT(IS_FREE_LAST_MBC_BLK(blk));
3904 max_size = (erts_aint_t) MBC_FBLK_SZ(blk);
3905 erts_atomic_set_nob(&crr->cpool.max_size, max_size);
3906
3907 crr->next = NULL;
3908 crr->prev = allctr->cpool.dc_list.last;
3909 if (allctr->cpool.dc_list.last) {
3910 check_pending_dealloc = 1;
3911 allctr->cpool.dc_list.last->next = crr;
3912 }
3913 else {
3914 check_pending_dealloc = 0;
3915 allctr->cpool.dc_list.first = crr;
3916 }
3917 allctr->cpool.dc_list.last = crr;
3918 if (check_pending_dealloc)
3919 check_pending_dealloc_carrier(allctr, NULL, NULL, NULL);
3920 erts_alloc_ensure_handle_delayed_dealloc_call(allctr->ix);
3921 }
3922
3923 static ERTS_INLINE void
cpool_init_carrier_data(Allctr_t * allctr,Carrier_t * crr)3924 cpool_init_carrier_data(Allctr_t *allctr, Carrier_t *crr)
3925 {
3926 crr->cpool.homecoming_dd.blk.bhdr = HOMECOMING_MBC_BLK_HDR;
3927 erts_atomic_init_nob(&crr->cpool.next, ERTS_AINT_NULL);
3928 erts_atomic_init_nob(&crr->cpool.prev, ERTS_AINT_NULL);
3929 crr->cpool.orig_allctr = allctr;
3930 crr->cpool.thr_prgr = ERTS_THR_PRGR_INVALID;
3931 erts_atomic_init_nob(&crr->cpool.max_size, 0);
3932 sys_memset(&crr->cpool.blocks_size, 0, sizeof(crr->cpool.blocks_size));
3933 sys_memset(&crr->cpool.blocks, 0, sizeof(crr->cpool.blocks));
3934 crr->cpool.total_blocks_size = 0;
3935 if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
3936 crr->cpool.abandon_limit = 0;
3937 else {
3938 UWord csz = CARRIER_SZ(crr);
3939 UWord limit = csz*allctr->cpool.util_limit;
3940 if (limit > csz)
3941 limit /= 100;
3942 else
3943 limit = (csz/100)*allctr->cpool.util_limit;
3944 crr->cpool.abandon_limit = limit;
3945 }
3946 crr->cpool.state = ERTS_MBC_IS_HOME;
3947 }
3948
3949
3950
3951 static UWord
allctr_abandon_limit(Allctr_t * allctr)3952 allctr_abandon_limit(Allctr_t *allctr)
3953 {
3954 UWord limit;
3955 UWord csz;
3956 int i;
3957
3958 csz = 0;
3959 for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
3960 csz += allctr->mbcs.carriers[i].size;
3961 }
3962
3963 limit = csz*allctr->cpool.util_limit;
3964 if (limit > csz)
3965 limit /= 100;
3966 else
3967 limit = (csz/100)*allctr->cpool.util_limit;
3968
3969 return limit;
3970 }
3971
3972 static void ERTS_INLINE
set_new_allctr_abandon_limit(Allctr_t * allctr)3973 set_new_allctr_abandon_limit(Allctr_t *allctr)
3974 {
3975 allctr->cpool.abandon_limit = allctr_abandon_limit(allctr);
3976 }
3977
3978 static void
abandon_carrier(Allctr_t * allctr,Carrier_t * crr)3979 abandon_carrier(Allctr_t *allctr, Carrier_t *crr)
3980 {
3981 erts_aint_t iallctr;
3982
3983 STAT_MBC_CPOOL_ABANDON(allctr, crr);
3984
3985 unlink_carrier(&allctr->mbc_list, crr);
3986 allctr->remove_mbc(allctr, crr);
3987
3988 /* Mark our free blocks as unused and reclaimable to the OS. */
3989 carrier_mem_discard_free_blocks(allctr, crr);
3990
3991 cpool_insert(allctr, crr);
3992
3993
3994 iallctr = erts_atomic_read_nob(&crr->allctr);
3995 if (allctr == crr->cpool.orig_allctr) {
3996 /* preserve HOMECOMING flag */
3997 ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING) == (erts_aint_t)allctr);
3998 erts_atomic_set_wb(&crr->allctr, iallctr | ERTS_CRR_ALCTR_FLG_IN_POOL);
3999 poolify_my_carrier(allctr, crr);
4000 }
4001 else {
4002 ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING) == (erts_aint_t)allctr);
4003 iallctr = ((erts_aint_t)crr->cpool.orig_allctr |
4004 ERTS_CRR_ALCTR_FLG_HOMECOMING |
4005 ERTS_CRR_ALCTR_FLG_IN_POOL);
4006 if (!(erts_atomic_xchg_wb(&crr->allctr, iallctr)
4007 & ERTS_CRR_ALCTR_FLG_HOMECOMING)) {
4008
4009 enqueue_homecoming(allctr, crr);
4010 }
4011 }
4012 }
4013
4014 static void
enqueue_homecoming(Allctr_t * allctr,Carrier_t * crr)4015 enqueue_homecoming(Allctr_t* allctr, Carrier_t* crr)
4016 {
4017 Allctr_t* orig_allctr = crr->cpool.orig_allctr;
4018 const int cinit = orig_allctr->dd.ix - allctr->dd.ix;
4019 Block_t* dd_blk = &crr->cpool.homecoming_dd.blk;
4020
4021 /*
4022 * The receiver will recognize this as a carrier
4023 * (and not a block which is the common case)
4024 * since the block header is HOMECOMING_MBC_BLK_HDR.
4025 */
4026 ASSERT(dd_blk->bhdr == HOMECOMING_MBC_BLK_HDR);
4027 if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(dd_blk), cinit))
4028 erts_alloc_notify_delayed_dealloc(orig_allctr->ix);
4029 }
4030
4031 static void
poolify_my_carrier(Allctr_t * allctr,Carrier_t * crr)4032 poolify_my_carrier(Allctr_t *allctr, Carrier_t *crr)
4033 {
4034 ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
4035
4036 crr->cpool.pooled.hdr.bhdr = erts_atomic_read_nob(&crr->cpool.max_size);
4037 aoff_add_pooled_mbc(allctr, crr);
4038 crr->cpool.state = ERTS_MBC_WAS_POOLED;
4039 }
4040
4041 static void
cpool_read_stat(Allctr_t * allctr,int alloc_no,UWord * nocp,UWord * cszp,UWord * nobp,UWord * bszp)4042 cpool_read_stat(Allctr_t *allctr, int alloc_no,
4043 UWord *nocp, UWord *cszp, UWord *nobp, UWord *bszp)
4044 {
4045 int block_ix;
4046 int i;
4047 UWord noc = 0, csz = 0, nob = 0, bsz = 0;
4048
4049 block_ix = alloc_no - ERTS_ALC_A_MIN;
4050
4051 /*
4052 * We try to get consistent values, but after
4053 * ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS failed
4054 * tries we give up and present what we got...
4055 */
4056 for (i = 0; i <= ERTS_ALC_CPOOL_MAX_FAILED_STAT_READS; i++) {
4057 UWord tnoc, tcsz, tnob, tbsz;
4058
4059 tnoc = (UWord) (nocp
4060 ? erts_atomic_read_nob(&allctr->cpool.stat.no_carriers)
4061 : 0);
4062 tcsz = (UWord) (cszp
4063 ? erts_atomic_read_nob(&allctr->cpool.stat.carriers_size)
4064 : 0);
4065 tnob = (UWord) (nobp
4066 ? erts_atomic_read_nob(&allctr->cpool.stat.no_blocks[block_ix])
4067 : 0);
4068 tbsz = (UWord) (bszp
4069 ? erts_atomic_read_nob(&allctr->cpool.stat.blocks_size[block_ix])
4070 : 0);
4071 if (tnoc == noc && tcsz == csz && tnob == nob && tbsz == bsz)
4072 break;
4073 noc = tnoc;
4074 csz = tcsz;
4075 nob = tnob;
4076 bsz = tbsz;
4077 ERTS_THR_READ_MEMORY_BARRIER;
4078 }
4079
4080 if (nocp)
4081 *nocp = noc;
4082 if (cszp)
4083 *cszp = csz;
4084 if (nobp)
4085 *nobp = nob;
4086 if (bszp)
4087 *bszp = bsz;
4088 }
4089
4090
4091
4092 #ifdef DEBUG
4093
4094 #if ERTS_SA_MB_CARRIERS
4095 #define ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE(CSZ) ASSERT((CSZ) % ERTS_SACRR_UNIT_SZ == 0)
4096 #else
4097 #define ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE(CSZ)
4098 #endif
4099
CHECK_1BLK_CARRIER(Allctr_t * A,int SBC,int MSEGED,Carrier_t * C,UWord CSZ,Block_t * B,UWord BSZ)4100 static void CHECK_1BLK_CARRIER(Allctr_t* A, int SBC, int MSEGED, Carrier_t* C,
4101 UWord CSZ, Block_t* B, UWord BSZ)
4102 {
4103 ASSERT(IS_LAST_BLK((B)));
4104 ASSERT((CSZ) == CARRIER_SZ((C)));
4105 ASSERT((BSZ) % sizeof(Unit_t) == 0);
4106 if ((SBC)) {
4107 ASSERT((BSZ) == SBC_BLK_SZ((B)));
4108 ASSERT((char*)B == (char*)C + SBC_HEADER_SIZE);
4109 ASSERT(IS_SBC_BLK((B)));
4110 ASSERT(IS_SB_CARRIER((C)));
4111 }
4112 else {
4113 ASSERT(IS_FREE_BLK(B));
4114 ASSERT((BSZ) == MBC_FBLK_SZ((B)));
4115 ASSERT(IS_MBC_FIRST_FBLK(A, (B)));
4116 ASSERT(IS_MBC_BLK((B)));
4117 ASSERT(IS_MB_CARRIER((C)));
4118 ASSERT(FBLK_TO_MBC(B) == (C));
4119 if ((MSEGED)) {
4120 ASSERT_ERTS_SACRR_UNIT_SIZE_MULTIPLE((CSZ));
4121 }
4122 }
4123 if ((MSEGED)) {
4124 ASSERT(IS_MSEG_CARRIER((C)));
4125 }
4126 else {
4127 ASSERT(IS_SYS_ALLOC_CARRIER((C)));
4128 ASSERT((CSZ) % sizeof(Unit_t) == 0);
4129 }
4130 }
4131
4132 #else
4133 #define CHECK_1BLK_CARRIER(A, SBC, MSEGED, C, CSZ, B, BSZ)
4134 #endif
4135
4136 static Block_t *
create_carrier(Allctr_t * allctr,Uint umem_sz,UWord flags)4137 create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
4138 {
4139 Block_t *blk;
4140 Carrier_t *crr;
4141 Uint blk_sz, bcrr_sz, crr_sz;
4142 #if HAVE_ERTS_MSEG
4143 int have_tried_sys_alloc = 0, have_tried_mseg = 0;
4144 Uint mseg_flags;
4145 #endif
4146 #ifdef DEBUG
4147 int is_mseg = 0;
4148 #endif
4149
4150 if ((ERTS_SUPER_ALIGNED_MSEG_ONLY && (flags & CFLG_MBC))
4151 || !allow_sys_alloc_carriers) {
4152 flags |= CFLG_FORCE_MSEG;
4153 flags &= ~CFLG_FORCE_SYS_ALLOC;
4154 #if !HAVE_ERTS_MSEG
4155 return NULL;
4156 #endif
4157 }
4158 flags |= allctr->crr_set_flgs;
4159 flags &= ~allctr->crr_clr_flgs;
4160
4161 ASSERT((flags & CFLG_SBC && !(flags & CFLG_MBC))
4162 || (flags & CFLG_MBC && !(flags & CFLG_SBC)));
4163
4164 ASSERT(!(flags & CFLG_FORCE_MSEG && flags & CFLG_FORCE_SYS_ALLOC));
4165
4166 if (umem_sz > (ERTS_UINT_MAX - ERTS_UINT_MAX/100)) {
4167 /* Do an overly conservative _overflow_ check here so we don't
4168 * have to deal with it from here on. I guess we could be more accurate
4169 * but I don't think the need to allocate over 99% of the address space
4170 * will ever arise on any machine, neither 32 nor 64 bit.
4171 */
4172 return NULL;
4173 }
4174
4175 if (flags & CFLG_MAIN_CARRIER) {
4176 ASSERT(flags & CFLG_MBC);
4177 ASSERT(flags & CFLG_NO_CPOOL);
4178 ASSERT(umem_sz == allctr->main_carrier_size);
4179 ERTS_UNDEF(blk_sz, 0);
4180
4181 if (allctr->main_carrier_size < allctr->min_mbc_size)
4182 allctr->main_carrier_size = allctr->min_mbc_size;
4183 crr_sz = bcrr_sz = allctr->main_carrier_size;
4184 }
4185 else {
4186 ERTS_UNDEF(bcrr_sz, 0);
4187 ERTS_UNDEF(crr_sz, 0);
4188 blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz);
4189 }
4190
4191 allctr->cpool.disable_abandon = ERTS_ALC_CPOOL_MAX_DISABLE_ABANDON;
4192
4193 if ((flags & (CFLG_MBC|CFLG_NO_CPOOL)) == CFLG_MBC
4194 && ERTS_ALC_IS_CPOOL_ENABLED(allctr)
4195 && erts_thr_progress_is_managed_thread()) {
4196 crr = cpool_fetch(allctr, blk_sz);
4197 if (crr) {
4198 STAT_MBC_CPOOL_FETCH(allctr, crr);
4199 INC_CC(allctr->cpool.stat.fetch);
4200 link_carrier(&allctr->mbc_list, crr);
4201 (*allctr->add_mbc)(allctr, crr);
4202 blk = (*allctr->get_free_block)(allctr, blk_sz, NULL, 0);
4203 ASSERT(blk);
4204 return blk;
4205 }
4206 }
4207
4208 #if HAVE_ERTS_MSEG
4209
4210 if (flags & CFLG_FORCE_SYS_ALLOC)
4211 goto try_sys_alloc;
4212 if (flags & CFLG_FORCE_MSEG)
4213 goto try_mseg;
4214 if (erts_mseg_no(&allctr->mseg_opt) >= max_mseg_carriers)
4215 goto try_sys_alloc;
4216 if (flags & CFLG_SBC) {
4217 if (allctr->sbcs.carriers[ERTS_CRR_ALLOC_MSEG].no >= allctr->max_mseg_sbcs)
4218 goto try_sys_alloc;
4219 }
4220 #if !ERTS_SUPER_ALIGNED_MSEG_ONLY
4221 else {
4222 if (allctr->mbcs.carriers[ERTS_CRR_ALLOC_MSEG].no >= allctr->max_mseg_mbcs)
4223 goto try_sys_alloc;
4224 }
4225 #endif
4226
4227 try_mseg:
4228
4229 if (flags & CFLG_SBC) {
4230 crr_sz = blk_sz + SBC_HEADER_SIZE;
4231 mseg_flags = ERTS_MSEG_FLG_NONE;
4232 }
4233 else {
4234 if (!(flags & CFLG_MAIN_CARRIER)) {
4235 crr_sz = (*allctr->get_next_mbc_size)(allctr);
4236 if (crr_sz < MBC_HEADER_SIZE(allctr) + blk_sz)
4237 crr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
4238 }
4239 mseg_flags = ERTS_MSEG_FLG_2POW;
4240 }
4241
4242 crr = (Carrier_t *) allctr->mseg_alloc(allctr, &crr_sz, mseg_flags);
4243 if (!crr) {
4244 have_tried_mseg = 1;
4245 if (!(have_tried_sys_alloc || flags & CFLG_FORCE_MSEG))
4246 goto try_sys_alloc;
4247 return NULL;
4248 }
4249
4250 #ifdef DEBUG
4251 is_mseg = 1;
4252 #endif
4253 if (flags & CFLG_SBC) {
4254 SET_CARRIER_HDR(crr, crr_sz, SCH_MSEG|SCH_SBC, allctr);
4255 STAT_MSEG_SBC_ALLOC(allctr, crr_sz, blk_sz);
4256 goto sbc_final_touch;
4257 }
4258 else {
4259 #ifndef ARCH_64
4260 ASSERT(crr_sz <= MBC_SZ_MAX_LIMIT);
4261 #endif
4262 SET_CARRIER_HDR(crr, crr_sz, SCH_MSEG|SCH_MBC, allctr);
4263 STAT_MSEG_MBC_ALLOC(allctr, crr_sz);
4264 goto mbc_final_touch;
4265 }
4266
4267 try_sys_alloc:
4268
4269 #endif /* #if HAVE_ERTS_MSEG */
4270
4271 if (flags & CFLG_SBC) {
4272 bcrr_sz = blk_sz + SBC_HEADER_SIZE;
4273 }
4274 else if (!(flags & CFLG_MAIN_CARRIER)) {
4275 bcrr_sz = MBC_HEADER_SIZE(allctr) + blk_sz;
4276 if (bcrr_sz < allctr->smallest_mbc_size)
4277 bcrr_sz = allctr->smallest_mbc_size;
4278 }
4279
4280 crr_sz = (flags & CFLG_FORCE_SIZE
4281 ? UNIT_CEILING(bcrr_sz)
4282 : SYS_ALLOC_CARRIER_CEILING(bcrr_sz));
4283
4284 crr = (Carrier_t *) allctr->sys_alloc(allctr, &crr_sz, flags & CFLG_MBC);
4285
4286 if (!crr) {
4287 if (crr_sz > UNIT_CEILING(bcrr_sz)) {
4288 crr_sz = UNIT_CEILING(bcrr_sz);
4289 crr = (Carrier_t *) allctr->sys_alloc(allctr, &crr_sz, flags & CFLG_MBC);
4290 }
4291 if (!crr) {
4292 #if HAVE_ERTS_MSEG
4293 have_tried_sys_alloc = 1;
4294 if (!(have_tried_mseg || flags & CFLG_FORCE_SYS_ALLOC))
4295 goto try_mseg;
4296 #endif
4297 return NULL;
4298 }
4299 }
4300 if (flags & CFLG_SBC) {
4301 SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_SBC, allctr);
4302 STAT_SYS_ALLOC_SBC_ALLOC(allctr, crr_sz, blk_sz);
4303
4304 #if HAVE_ERTS_MSEG
4305 sbc_final_touch:
4306 #endif
4307
4308 blk = SBC2BLK(allctr, crr);
4309
4310 SET_SBC_BLK_HDR(blk, blk_sz);
4311
4312 link_carrier(&allctr->sbc_list, crr);
4313
4314 CHECK_1BLK_CARRIER(allctr, 1, is_mseg, crr, crr_sz, blk, blk_sz);
4315
4316 }
4317 else {
4318 SET_CARRIER_HDR(crr, crr_sz, SCH_SYS_ALLOC|SCH_MBC, allctr);
4319 STAT_SYS_ALLOC_MBC_ALLOC(allctr, crr_sz);
4320
4321 #if HAVE_ERTS_MSEG
4322 mbc_final_touch:
4323 #endif
4324 set_new_allctr_abandon_limit(allctr);
4325
4326 blk = MBC_TO_FIRST_BLK(allctr, crr);
4327
4328 blk_sz = UNIT_FLOOR(crr_sz - MBC_HEADER_SIZE(allctr));
4329
4330 SET_MBC_FBLK_HDR(blk, blk_sz, SBH_THIS_FREE|SBH_LAST_BLK, crr);
4331
4332 if (flags & CFLG_MAIN_CARRIER) {
4333 ASSERT(!allctr->main_carrier);
4334 allctr->main_carrier = crr;
4335 }
4336
4337 cpool_init_carrier_data(allctr, crr);
4338
4339 link_carrier(&allctr->mbc_list, crr);
4340
4341 CHECK_1BLK_CARRIER(allctr, 0, is_mseg, crr, crr_sz, blk, blk_sz);
4342 if (allctr->creating_mbc)
4343 (*allctr->creating_mbc)(allctr, crr);
4344
4345 }
4346
4347 #ifdef USE_LTTNG_VM_TRACEPOINTS
4348 if (LTTNG_ENABLED(carrier_create)) {
4349 lttng_decl_carrier_stats(mbc_stats);
4350 lttng_decl_carrier_stats(sbc_stats);
4351 LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->mbcs), mbc_stats);
4352 LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->sbcs), sbc_stats);
4353 LTTNG5(carrier_create,
4354 ERTS_ALC_A2AD(allctr->alloc_no),
4355 allctr->ix,
4356 crr_sz,
4357 mbc_stats,
4358 sbc_stats);
4359 }
4360 #endif
4361
4362 DEBUG_SAVE_ALIGNMENT(crr);
4363 return blk;
4364 }
4365
4366 static Block_t *
resize_carrier(Allctr_t * allctr,Block_t * old_blk,Uint umem_sz,UWord flags)4367 resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
4368 {
4369 Block_t *new_blk;
4370 Carrier_t *new_crr, *old_crr;
4371 UWord create_flags;
4372 Uint old_crr_sz, old_blk_sz, new_blk_sz, new_crr_sz;
4373 Uint new_bcrr_sz;
4374
4375 if (flags & CFLG_MBC) {
4376 ASSERT(0);
4377 return NULL;
4378 }
4379
4380 ASSERT(flags & CFLG_SBC);
4381 create_flags = flags|CFLG_SBC;
4382
4383 HARD_CHECK_BLK_CARRIER(allctr, old_blk);
4384
4385 old_blk_sz = SBC_BLK_SZ(old_blk);
4386 old_crr = BLK_TO_SBC(old_blk);
4387 old_crr_sz = CARRIER_SZ(old_crr);
4388 ASSERT(IS_SB_CARRIER(old_crr));
4389 ASSERT(IS_SBC_BLK(old_blk));
4390
4391 new_blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz);
4392
4393 #if HAVE_ERTS_MSEG
4394
4395 if (IS_MSEG_CARRIER(old_crr)) {
4396 STAT_MSEG_SBC_FREE(allctr, old_crr_sz, old_blk_sz);
4397
4398 if (!(flags & CFLG_FORCE_SYS_ALLOC)) {
4399
4400 new_crr_sz = new_blk_sz + SBC_HEADER_SIZE;
4401 new_crr_sz = ERTS_SACRR_UNIT_CEILING(new_crr_sz);
4402 new_crr = (Carrier_t *) allctr->mseg_realloc(allctr,
4403 old_crr,
4404 old_crr_sz,
4405 &new_crr_sz);
4406 if (new_crr) {
4407 SET_CARRIER_SZ(new_crr, new_crr_sz);
4408 new_blk = SBC2BLK(allctr, new_crr);
4409 SET_SBC_BLK_SZ(new_blk, new_blk_sz);
4410 STAT_MSEG_SBC_ALLOC(allctr, new_crr_sz, new_blk_sz);
4411 relink_carrier(&allctr->sbc_list, new_crr);
4412 CHECK_1BLK_CARRIER(allctr, 1, 1, new_crr, new_crr_sz,
4413 new_blk, new_blk_sz);
4414 DEBUG_SAVE_ALIGNMENT(new_crr);
4415 return new_blk;
4416 }
4417 create_flags |= CFLG_FORCE_SYS_ALLOC; /* since mseg_realloc()
4418 failed */
4419 }
4420
4421 new_blk = create_carrier(allctr, umem_sz, create_flags);
4422 if (new_blk) {
4423 sys_memcpy((void *) BLK2UMEM(new_blk),
4424 (void *) BLK2UMEM(old_blk),
4425 MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ);
4426 unlink_carrier(&allctr->sbc_list, old_crr);
4427 allctr->mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE);
4428 }
4429 else {
4430 /* Old carrier unchanged; restore stat */
4431 STAT_MSEG_SBC_ALLOC(allctr, old_crr_sz, old_blk_sz);
4432 }
4433
4434 return new_blk;
4435 }
4436 else {
4437 if (!(flags & CFLG_FORCE_MSEG)) {
4438 #endif /* #if HAVE_ERTS_MSEG */
4439 new_bcrr_sz = new_blk_sz + SBC_HEADER_SIZE;
4440 new_crr_sz = (flags & CFLG_FORCE_SIZE
4441 ? UNIT_CEILING(new_bcrr_sz)
4442 : SYS_ALLOC_CARRIER_CEILING(new_bcrr_sz));
4443
4444 new_crr = (Carrier_t *) allctr->sys_realloc(allctr,
4445 (void *) old_crr,
4446 &new_crr_sz,
4447 old_crr_sz,
4448 0);
4449 if (new_crr) {
4450 sys_realloc_success:
4451 SET_CARRIER_SZ(new_crr, new_crr_sz);
4452 new_blk = SBC2BLK(allctr, new_crr);
4453 SET_SBC_BLK_SZ(new_blk, new_blk_sz);
4454 STAT_SYS_ALLOC_SBC_FREE(allctr, old_crr_sz, old_blk_sz);
4455 STAT_SYS_ALLOC_SBC_ALLOC(allctr, new_crr_sz, new_blk_sz);
4456 relink_carrier(&allctr->sbc_list, new_crr);
4457 CHECK_1BLK_CARRIER(allctr, 1, 0, new_crr, new_crr_sz,
4458 new_blk, new_blk_sz);
4459 DEBUG_SAVE_ALIGNMENT(new_crr);
4460 return new_blk;
4461 }
4462 else if (new_crr_sz > UNIT_CEILING(new_bcrr_sz)) {
4463 new_crr_sz = new_blk_sz + SBC_HEADER_SIZE;
4464 new_crr_sz = UNIT_CEILING(new_crr_sz);
4465 new_crr = (Carrier_t *) allctr->sys_realloc(allctr,
4466 (void *) old_crr,
4467 &new_crr_sz,
4468 old_crr_sz,
4469 0);
4470 if (new_crr)
4471 goto sys_realloc_success;
4472 }
4473
4474 #if !HAVE_ERTS_MSEG
4475 return NULL;
4476 #else
4477 create_flags |= CFLG_FORCE_MSEG; /* Since sys_realloc() failed */
4478 }
4479
4480 STAT_SYS_ALLOC_SBC_FREE(allctr, old_crr_sz, old_blk_sz);
4481
4482 new_blk = create_carrier(allctr, umem_sz, create_flags);
4483 if (new_blk) {
4484 sys_memcpy((void *) BLK2UMEM(new_blk),
4485 (void *) BLK2UMEM(old_blk),
4486 MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ);
4487 unlink_carrier(&allctr->sbc_list, old_crr);
4488 allctr->sys_dealloc(allctr, old_crr, CARRIER_SZ(old_crr), 0);
4489 }
4490 else {
4491 /* Old carrier unchanged; restore... */
4492 STAT_SYS_ALLOC_SBC_ALLOC(allctr, old_crr_sz, old_blk_sz);
4493 }
4494 return new_blk;
4495 }
4496 #endif
4497 }
4498
4499 static void
dealloc_carrier(Allctr_t * allctr,Carrier_t * crr,int superaligned)4500 dealloc_carrier(Allctr_t *allctr, Carrier_t *crr, int superaligned)
4501 {
4502 #if HAVE_ERTS_MSEG
4503 if (IS_MSEG_CARRIER(crr))
4504 allctr->mseg_dealloc(allctr, crr, CARRIER_SZ(crr),
4505 (superaligned
4506 ? ERTS_MSEG_FLG_2POW
4507 : ERTS_MSEG_FLG_NONE));
4508 else
4509 #endif
4510 allctr->sys_dealloc(allctr, crr, CARRIER_SZ(crr), superaligned);
4511 }
4512
4513 static void
destroy_carrier(Allctr_t * allctr,Block_t * blk,Carrier_t ** busy_pcrr_pp)4514 destroy_carrier(Allctr_t *allctr, Block_t *blk, Carrier_t **busy_pcrr_pp)
4515 {
4516 Uint crr_sz;
4517 Carrier_t *crr;
4518
4519 if (IS_SBC_BLK(blk)) {
4520 Uint blk_sz = SBC_BLK_SZ(blk);
4521 crr = BLK_TO_SBC(blk);
4522 crr_sz = CARRIER_SZ(crr);
4523
4524 ASSERT(IS_LAST_BLK(blk));
4525
4526 HARD_CHECK_BLK_CARRIER(allctr, blk);
4527
4528 #if HAVE_ERTS_MSEG
4529 if (IS_MSEG_CARRIER(crr)) {
4530 STAT_MSEG_SBC_FREE(allctr, crr_sz, blk_sz);
4531 }
4532 else
4533 #endif
4534 STAT_SYS_ALLOC_SBC_FREE(allctr, crr_sz, blk_sz);
4535
4536 unlink_carrier(&allctr->sbc_list, crr);
4537
4538 dealloc_carrier(allctr, crr, 0);
4539 }
4540 else {
4541 ASSERT(IS_MBC_FIRST_FBLK(allctr, blk));
4542 crr = FIRST_BLK_TO_MBC(allctr, blk);
4543
4544 #ifdef DEBUG
4545 if (!allctr->stopped) {
4546 ASSERT(IS_LAST_BLK(blk));
4547
4548 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
4549 (*allctr->link_free_block)(allctr, blk);
4550 HARD_CHECK_BLK_CARRIER(allctr, blk);
4551 (*allctr->unlink_free_block)(allctr, blk);
4552 #endif
4553 }
4554 #endif
4555
4556 if (busy_pcrr_pp && *busy_pcrr_pp) {
4557 erts_aint_t iallctr = erts_atomic_read_nob(&crr->allctr);
4558 ERTS_ALC_CPOOL_ASSERT(*busy_pcrr_pp == crr);
4559 ERTS_ALC_CPOOL_ASSERT((iallctr & ~ERTS_CRR_ALCTR_FLG_HOMECOMING)
4560 == (((erts_aint_t) allctr)
4561 | ERTS_CRR_ALCTR_FLG_IN_POOL
4562 | ERTS_CRR_ALCTR_FLG_BUSY));
4563 ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
4564
4565 *busy_pcrr_pp = NULL;
4566 erts_atomic_set_nob(&crr->allctr,
4567 (iallctr & ~(ERTS_CRR_ALCTR_FLG_IN_POOL |
4568 ERTS_CRR_ALCTR_FLG_BUSY)));
4569 cpool_delete(allctr, allctr, crr);
4570 }
4571 else
4572 {
4573 unlink_carrier(&allctr->mbc_list, crr);
4574 STAT_MBC_FREE(allctr, crr);
4575 if (allctr->remove_mbc)
4576 allctr->remove_mbc(allctr, crr);
4577 }
4578
4579 #ifdef USE_LTTNG_VM_TRACEPOINTS
4580 if (LTTNG_ENABLED(carrier_destroy)) {
4581 lttng_decl_carrier_stats(mbc_stats);
4582 lttng_decl_carrier_stats(sbc_stats);
4583 LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->mbcs), mbc_stats);
4584 LTTNG_CARRIER_STATS_TO_LTTNG_STATS(&(allctr->sbcs), sbc_stats);
4585 LTTNG5(carrier_destroy,
4586 ERTS_ALC_A2AD(allctr->alloc_no),
4587 allctr->ix,
4588 CARRIER_SZ(crr),
4589 mbc_stats,
4590 sbc_stats);
4591 }
4592 #endif
4593
4594 schedule_dealloc_carrier(allctr, crr);
4595 }
4596 }
4597
4598
4599 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
4600 * Info stuff *
4601 \* */
4602
4603 static struct {
4604 Eterm versions;
4605
4606 Eterm options;
4607 Eterm e;
4608 Eterm t;
4609 Eterm ramv;
4610 Eterm atags;
4611 #if HAVE_ERTS_MSEG
4612 Eterm asbcst;
4613 Eterm rsbcst;
4614 #endif
4615 Eterm rsbcmt;
4616 Eterm rmbcmt;
4617 Eterm mmbcs;
4618 Eterm msbclt;
4619 #if HAVE_ERTS_MSEG
4620 Eterm mmsbc;
4621 Eterm mmmbc;
4622 #endif
4623 Eterm lmbcs;
4624 Eterm smbcs;
4625 Eterm mbcgs;
4626 Eterm acul;
4627 Eterm acnl;
4628 Eterm acfml;
4629 Eterm cp;
4630
4631 #if HAVE_ERTS_MSEG
4632 Eterm mmc;
4633 #endif
4634 Eterm ycs;
4635 Eterm sac;
4636
4637 Eterm fix_types;
4638
4639 Eterm mbcs;
4640 Eterm mbcs_pool;
4641 Eterm fetch;
4642 Eterm fail_pooled;
4643 Eterm fail_shared;
4644 Eterm fail_pend_dealloc;
4645 Eterm fail;
4646 Eterm skip_size;
4647 Eterm skip_busy;
4648 Eterm skip_not_pooled;
4649 Eterm skip_homecoming;
4650 Eterm skip_race;
4651 Eterm entrance_removed;
4652 Eterm sbcs;
4653
4654 Eterm sys_alloc_carriers_size;
4655 #if HAVE_ERTS_MSEG
4656 Eterm mseg_alloc_carriers_size;
4657 #endif
4658 Eterm carriers_size;
4659 Eterm sys_alloc_carriers;
4660 #if HAVE_ERTS_MSEG
4661 Eterm mseg_alloc_carriers;
4662 #endif
4663 Eterm carriers;
4664
4665 Eterm blocks;
4666 Eterm count;
4667 Eterm size;
4668
4669 Eterm calls;
4670 Eterm sys_alloc;
4671 Eterm sys_free;
4672 Eterm sys_realloc;
4673 #if HAVE_ERTS_MSEG
4674 Eterm mseg_alloc;
4675 Eterm mseg_dealloc;
4676 Eterm mseg_realloc;
4677 #endif
4678
4679 Eterm At_sign;
4680
4681 #ifdef DEBUG
4682 Eterm end_of_atoms;
4683 #endif
4684 } am;
4685
4686 static char *allocator_char_str[ERTS_ALC_A_MAX + 1];
4687 static Eterm allocator_char_atom[ERTS_ALC_A_MAX + 1];
4688 static Eterm alloc_type_atoms[ERTS_ALC_N_MAX + 1];
4689 static Eterm alloc_num_atoms[ERTS_ALC_A_MAX + 1];
4690
atom_init(Eterm * atom,char * name)4691 static ERTS_INLINE void atom_init(Eterm *atom, char *name)
4692 {
4693 *atom = am_atom_put(name, sys_strlen(name));
4694 }
4695 #define AM_INIT(AM) atom_init(&am.AM, #AM)
4696
4697 static erts_mtx_t init_atoms_mtx;
4698
4699 static void
init_atoms(Allctr_t * allctr)4700 init_atoms(Allctr_t *allctr)
4701 {
4702 erts_mtx_lock(&init_atoms_mtx);
4703
4704 if (!atoms_initialized) {
4705 int ix;
4706 #ifdef DEBUG
4707 Eterm *atom;
4708
4709 for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
4710 *atom = THE_NON_VALUE;
4711 }
4712 #endif
4713
4714 AM_INIT(versions);
4715
4716 AM_INIT(options);
4717 AM_INIT(e);
4718 AM_INIT(t);
4719 AM_INIT(ramv);
4720 AM_INIT(atags);
4721 #if HAVE_ERTS_MSEG
4722 AM_INIT(asbcst);
4723 AM_INIT(rsbcst);
4724 #endif
4725 AM_INIT(rsbcmt);
4726 AM_INIT(rmbcmt);
4727 AM_INIT(mmbcs);
4728 AM_INIT(msbclt);
4729 #if HAVE_ERTS_MSEG
4730 AM_INIT(mmsbc);
4731 AM_INIT(mmmbc);
4732 #endif
4733 AM_INIT(lmbcs);
4734 AM_INIT(smbcs);
4735 AM_INIT(mbcgs);
4736 AM_INIT(acul);
4737 AM_INIT(acnl);
4738 AM_INIT(acfml);
4739 AM_INIT(cp);
4740
4741 #if HAVE_ERTS_MSEG
4742 AM_INIT(mmc);
4743 #endif
4744 AM_INIT(ycs);
4745 AM_INIT(sac);
4746
4747 AM_INIT(fix_types);
4748
4749 AM_INIT(mbcs);
4750 AM_INIT(mbcs_pool);
4751 AM_INIT(fetch);
4752 AM_INIT(fail_pooled);
4753 AM_INIT(fail_shared);
4754 AM_INIT(fail_pend_dealloc);
4755 AM_INIT(fail);
4756 AM_INIT(skip_size);
4757 AM_INIT(skip_busy);
4758 AM_INIT(skip_not_pooled);
4759 AM_INIT(skip_homecoming);
4760 AM_INIT(skip_race);
4761 AM_INIT(entrance_removed);
4762 AM_INIT(sbcs);
4763
4764 AM_INIT(sys_alloc_carriers_size);
4765 #if HAVE_ERTS_MSEG
4766 AM_INIT(mseg_alloc_carriers_size);
4767 #endif
4768 AM_INIT(carriers_size);
4769 AM_INIT(sys_alloc_carriers);
4770 #if HAVE_ERTS_MSEG
4771 AM_INIT(mseg_alloc_carriers);
4772 #endif
4773 AM_INIT(carriers);
4774 AM_INIT(blocks);
4775 AM_INIT(count);
4776 AM_INIT(size);
4777
4778 AM_INIT(calls);
4779 AM_INIT(sys_alloc);
4780 AM_INIT(sys_free);
4781 AM_INIT(sys_realloc);
4782 #if HAVE_ERTS_MSEG
4783 AM_INIT(mseg_alloc);
4784 AM_INIT(mseg_dealloc);
4785 AM_INIT(mseg_realloc);
4786 #endif
4787
4788 am.At_sign = am_atom_put("@", 1);
4789
4790 #ifdef DEBUG
4791 for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
4792 ASSERT(*atom != THE_NON_VALUE);
4793 }
4794 #endif
4795
4796 for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
4797 char *cp_str = allocator_char_str[ix];
4798 Eterm cp_atom = am_atom_put(cp_str, sys_strlen(cp_str));
4799 allocator_char_atom[ix] = cp_atom;
4800 }
4801
4802 for (ix = ERTS_ALC_N_MIN; ix <= ERTS_ALC_N_MAX; ix++) {
4803 const char *name = ERTS_ALC_N2TD(ix);
4804 size_t len = sys_strlen(name);
4805
4806 alloc_type_atoms[ix] = am_atom_put(name, len);
4807 }
4808
4809 for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
4810 const char *name = ERTS_ALC_A2AD(ix);
4811 size_t len = sys_strlen(name);
4812
4813 alloc_num_atoms[ix] = am_atom_put(name, len);
4814 }
4815 }
4816
4817 if (allctr && !allctr->atoms_initialized) {
4818
4819 make_name_atoms(allctr);
4820
4821 (*allctr->init_atoms)();
4822
4823 allctr->atoms_initialized = 1;
4824 }
4825
4826 atoms_initialized = 1;
4827 erts_mtx_unlock(&init_atoms_mtx);
4828
4829 }
4830
4831 static ERTS_INLINE void
ensure_atoms_initialized(Allctr_t * allctr)4832 ensure_atoms_initialized(Allctr_t *allctr)
4833 {
4834 if (!allctr || !allctr->atoms_initialized)
4835 init_atoms(allctr);
4836 }
4837
4838 #define bld_uint erts_bld_uint
4839 #define bld_cons erts_bld_cons
4840 #define bld_tuple erts_bld_tuple
4841 #define bld_string erts_bld_string
4842
4843 /*
4844 * bld_unstable_uint() (instead bld_uint()) is used when values may
4845 * change between size check and actual build. This because a value
4846 * that would fit a small when size check is done may need to be built
4847 * as a big when the actual build is performed. Caller is required to
4848 * HRelease after build.
4849 *
4850 * Note, bld_unstable_uint() should have been called bld_unstable_uword()
4851 * but we do not want to rename it...
4852 */
4853 static ERTS_INLINE Eterm
bld_unstable_uint(Uint ** hpp,Uint * szp,UWord ui)4854 bld_unstable_uint(Uint **hpp, Uint *szp, UWord ui)
4855 {
4856 Eterm res = THE_NON_VALUE;
4857 if (szp)
4858 *szp += BIG_UWORD_HEAP_SIZE(~((UWord) 0));
4859 if (hpp) {
4860 if (IS_USMALL(0, ui))
4861 res = make_small(ui);
4862 else {
4863 res = uword_to_big(ui, *hpp);
4864 *hpp += BIG_UWORD_HEAP_SIZE(ui);
4865 }
4866 }
4867 return res;
4868 }
4869
4870 static ERTS_INLINE void
add_2tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2)4871 add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
4872 {
4873 *lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 2, el1, el2), *lp);
4874 }
4875
4876 static ERTS_INLINE void
add_3tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2,Eterm el3)4877 add_3tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2, Eterm el3)
4878 {
4879 *lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 3, el1, el2, el3), *lp);
4880 }
4881
4882 static ERTS_INLINE void
add_4tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2,Eterm el3,Eterm el4)4883 add_4tup(Uint **hpp, Uint *szp, Eterm *lp,
4884 Eterm el1, Eterm el2, Eterm el3, Eterm el4)
4885 {
4886 *lp =
4887 bld_cons(hpp, szp, bld_tuple(hpp, szp, 4, el1, el2, el3, el4), *lp);
4888 }
4889
4890 static ERTS_INLINE void
add_fix_types(Allctr_t * allctr,int internal,Uint ** hpp,Uint * szp,Eterm * lp,Eterm fix)4891 add_fix_types(Allctr_t *allctr, int internal, Uint **hpp, Uint *szp,
4892 Eterm *lp, Eterm fix)
4893 {
4894 if (allctr->fix) {
4895 if (!ERTS_ALC_IS_CPOOL_ENABLED(allctr))
4896 add_2tup(hpp, szp, lp, am.fix_types, fix);
4897 else if (internal)
4898 add_3tup(hpp, szp, lp,
4899 am.fix_types,
4900 erts_bld_uword(hpp, szp, ~((UWord) 0)),
4901 fix);
4902 }
4903 }
4904
4905 static Eterm
sz_info_fix(Allctr_t * allctr,int internal,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)4906 sz_info_fix(Allctr_t *allctr,
4907 int internal,
4908 fmtfn_t *print_to_p,
4909 void *print_to_arg,
4910 Uint **hpp,
4911 Uint *szp)
4912 {
4913 Eterm res;
4914 int ix;
4915
4916 ASSERT(allctr->fix);
4917
4918 res = NIL;
4919
4920 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
4921
4922 if (internal) {
4923 for (ix = ERTS_ALC_NO_FIXED_SIZES-1; ix >= 0; ix--) {
4924 ErtsAlcFixList_t *fix = &allctr->fix[ix];
4925 UWord alloced = fix->type_size * fix->u.cpool.allocated;
4926 UWord used = fix->type_size * fix->u.cpool.used;
4927
4928 if (print_to_p) {
4929 fmtfn_t to = *print_to_p;
4930 void *arg = print_to_arg;
4931 erts_print(to,
4932 arg,
4933 "fix type internal: %s %bpu %bpu\n",
4934 (char *) ERTS_ALC_T2TD(fix->type),
4935 alloced,
4936 used);
4937 }
4938
4939 if (hpp || szp) {
4940 add_3tup(hpp, szp, &res,
4941 alloc_type_atoms[ERTS_ALC_T2N(fix->type)],
4942 bld_unstable_uint(hpp, szp, alloced),
4943 bld_unstable_uint(hpp, szp, used));
4944 }
4945 }
4946 }
4947 }
4948 else {
4949
4950 for (ix = ERTS_ALC_NO_FIXED_SIZES-1; ix >= 0; ix--) {
4951 ErtsAlcFixList_t *fix = &allctr->fix[ix];
4952 UWord alloced = fix->type_size * fix->u.nocpool.allocated;
4953 UWord used = fix->type_size*fix->u.nocpool.used;
4954
4955 if (print_to_p) {
4956 fmtfn_t to = *print_to_p;
4957 void *arg = print_to_arg;
4958 erts_print(to,
4959 arg,
4960 "fix type: %s %bpu %bpu\n",
4961 (char *) ERTS_ALC_T2TD(fix->type),
4962 alloced,
4963 used);
4964 }
4965
4966 if (hpp || szp) {
4967 add_3tup(hpp, szp, &res,
4968 alloc_type_atoms[ERTS_ALC_T2N(fix->type)],
4969 bld_unstable_uint(hpp, szp, alloced),
4970 bld_unstable_uint(hpp, szp, used));
4971 }
4972 }
4973 }
4974 return res;
4975 }
4976
4977 static Eterm
sz_info_carriers(Allctr_t * allctr,CarriersStats_t * cs,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)4978 sz_info_carriers(Allctr_t *allctr,
4979 CarriersStats_t *cs,
4980 char *prefix,
4981 fmtfn_t *print_to_p,
4982 void *print_to_arg,
4983 Uint **hpp,
4984 Uint *szp)
4985 {
4986 Eterm res = THE_NON_VALUE;
4987 UWord curr_size;
4988 int i;
4989
4990 curr_size = 0;
4991 for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
4992 curr_size += cs->carriers[i].size;
4993 }
4994
4995 if (print_to_p) {
4996 fmtfn_t to = *print_to_p;
4997 void *arg = print_to_arg;
4998 int alloc_no;
4999
5000 for (alloc_no = ERTS_ALC_A_MIN;
5001 alloc_no <= ERTS_ALC_A_MAX;
5002 alloc_no++) {
5003 int ix = alloc_no - ERTS_ALC_A_MIN;
5004
5005 erts_print(to,
5006 arg,
5007 "%sblocks[%s] size: %bpu %bpu %bpu\n",
5008 prefix,
5009 erts_alc_a2ad[alloc_no],
5010 cs->blocks[ix].curr.size,
5011 cs->blocks[ix].max.size,
5012 cs->blocks[ix].max_ever.size);
5013 }
5014
5015 erts_print(to,
5016 arg,
5017 "%scarriers size: %bpu %bpu %bpu\n",
5018 prefix,
5019 curr_size,
5020 cs->max.size,
5021 cs->max_ever.size);
5022 }
5023
5024 if (hpp || szp) {
5025 Eterm blocks;
5026 int alloc_no;
5027
5028 res = NIL;
5029
5030 add_4tup(hpp, szp, &res,
5031 am.carriers_size,
5032 bld_unstable_uint(hpp, szp, curr_size),
5033 bld_unstable_uint(hpp, szp, cs->max.size),
5034 bld_unstable_uint(hpp, szp, cs->max_ever.size));
5035
5036 blocks = NIL;
5037 for (alloc_no = ERTS_ALC_A_MIN;
5038 alloc_no <= ERTS_ALC_A_MAX;
5039 alloc_no++) {
5040 int ix = alloc_no - ERTS_ALC_A_MIN;
5041 UWord curr, max, max_ever;
5042 Eterm info = NIL;
5043
5044 curr = cs->blocks[ix].curr.size;
5045 max = cs->blocks[ix].max.size;
5046 max_ever = cs->blocks[ix].max_ever.size;
5047
5048 if (curr == 0 && max == 0 && max_ever == 0) {
5049 continue;
5050 }
5051
5052 add_4tup(hpp, szp, &info,
5053 am.size,
5054 bld_unstable_uint(hpp, szp, curr),
5055 bld_unstable_uint(hpp, szp, max),
5056 bld_unstable_uint(hpp, szp, max_ever));
5057
5058 add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
5059 }
5060
5061 add_2tup(hpp, szp, &res, am.blocks, blocks);
5062 }
5063
5064 return res;
5065 }
5066
5067
5068 static Eterm
info_cpool(Allctr_t * allctr,int sz_only,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5069 info_cpool(Allctr_t *allctr,
5070 int sz_only,
5071 char *prefix,
5072 fmtfn_t *print_to_p,
5073 void *print_to_arg,
5074 Uint **hpp,
5075 Uint *szp)
5076 {
5077 Eterm res = THE_NON_VALUE;
5078 UWord noc, csz;
5079
5080 noc = csz = ~0;
5081 if (print_to_p || hpp) {
5082 cpool_read_stat(allctr, allctr->alloc_no, &noc, &csz, NULL, NULL);
5083 }
5084
5085 if (print_to_p) {
5086 fmtfn_t to = *print_to_p;
5087 void *arg = print_to_arg;
5088 int alloc_no;
5089
5090 for (alloc_no = ERTS_ALC_A_MIN;
5091 alloc_no <= ERTS_ALC_A_MAX;
5092 alloc_no++) {
5093 UWord nob, bsz;
5094
5095 nob = bsz = ~0;
5096 cpool_read_stat(allctr, alloc_no, NULL, NULL, &nob, &bsz);
5097
5098 if (!sz_only)
5099 erts_print(to, arg, "%sblocks[%s] count: %bpu\n",
5100 prefix, erts_alc_a2ad[alloc_no], nob);
5101 erts_print(to, arg, "%sblocks[%s] size: %bpu\n",
5102 prefix, erts_alc_a2ad[alloc_no], bsz);
5103 }
5104
5105 if (!sz_only)
5106 erts_print(to, arg, "%scarriers: %bpu\n", prefix, noc);
5107 erts_print(to, arg, "%scarriers size: %bpu\n", prefix, csz);
5108 }
5109
5110 if (hpp || szp) {
5111 Eterm blocks;
5112 int alloc_no;
5113
5114 res = NIL;
5115
5116 if (!sz_only) {
5117 add_3tup(hpp, szp, &res, am.fail_pooled,
5118 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_pooled)),
5119 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_pooled)));
5120
5121 add_3tup(hpp, szp, &res, am.fail_shared,
5122 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_shared)),
5123 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_shared)));
5124
5125 add_3tup(hpp, szp, &res, am.fail_pend_dealloc,
5126 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail_pend_dealloc)),
5127 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail_pend_dealloc)));
5128
5129 add_3tup(hpp, szp, &res, am.fail,
5130 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fail)),
5131 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fail)));
5132
5133 add_3tup(hpp, szp, &res, am.fetch,
5134 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.fetch)),
5135 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.fetch)));
5136
5137 add_3tup(hpp, szp, &res, am.skip_size,
5138 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_size)),
5139 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_size)));
5140
5141 add_3tup(hpp, szp, &res, am.skip_busy,
5142 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_busy)),
5143 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_busy)));
5144
5145 add_3tup(hpp, szp, &res, am.skip_not_pooled,
5146 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_not_pooled)),
5147 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_not_pooled)));
5148
5149 add_3tup(hpp, szp, &res, am.skip_homecoming,
5150 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_homecoming)),
5151 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_homecoming)));
5152
5153 add_3tup(hpp, szp, &res, am.skip_race,
5154 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.skip_race)),
5155 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.skip_race)));
5156
5157 add_3tup(hpp, szp, &res, am.entrance_removed,
5158 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->cpool.stat.entrance_removed)),
5159 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->cpool.stat.entrance_removed)));
5160 }
5161
5162 add_2tup(hpp, szp, &res,
5163 am.carriers_size,
5164 bld_unstable_uint(hpp, szp, csz));
5165
5166 if (!sz_only) {
5167 add_2tup(hpp, szp, &res,
5168 am.carriers,
5169 bld_unstable_uint(hpp, szp, noc));
5170 }
5171
5172 blocks = NIL;
5173 for (alloc_no = ERTS_ALC_A_MIN;
5174 alloc_no <= ERTS_ALC_A_MAX;
5175 alloc_no++) {
5176 UWord nob, bsz;
5177 Eterm info;
5178
5179 nob = bsz = ~0;
5180 cpool_read_stat(allctr, alloc_no, NULL, NULL, &nob, &bsz);
5181
5182 if (bsz == 0 && (nob == 0 || sz_only)) {
5183 continue;
5184 }
5185
5186 info = NIL;
5187
5188 add_2tup(hpp, szp, &info,
5189 am.size,
5190 bld_unstable_uint(hpp, szp, bsz));
5191
5192 if (!sz_only) {
5193 add_2tup(hpp, szp, &info,
5194 am.count,
5195 bld_unstable_uint(hpp, szp, nob));
5196 }
5197
5198 add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
5199 }
5200
5201 add_2tup(hpp, szp, &res, am.blocks, blocks);
5202 }
5203
5204 return res;
5205 }
5206
5207
5208 static Eterm
info_carriers(Allctr_t * allctr,CarriersStats_t * cs,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5209 info_carriers(Allctr_t *allctr,
5210 CarriersStats_t *cs,
5211 char *prefix,
5212 fmtfn_t *print_to_p,
5213 void *print_to_arg,
5214 Uint **hpp,
5215 Uint *szp)
5216 {
5217 Eterm res = THE_NON_VALUE;
5218 UWord curr_no, curr_size;
5219 int i;
5220
5221 curr_no = curr_size = 0;
5222 for (i = ERTS_CRR_ALLOC_MIN; i <= ERTS_CRR_ALLOC_MAX; i++) {
5223 curr_no += cs->carriers[i].no;
5224 curr_size += cs->carriers[i].size;
5225 }
5226
5227 if (print_to_p) {
5228 fmtfn_t to = *print_to_p;
5229 void *arg = print_to_arg;
5230 int alloc_no;
5231
5232 for (alloc_no = ERTS_ALC_A_MIN;
5233 alloc_no <= ERTS_ALC_A_MAX;
5234 alloc_no++) {
5235 int ix = alloc_no - ERTS_ALC_A_MIN;
5236 erts_print(to,
5237 arg,
5238 "%sblocks[%s] count: %bpu %bpu %bpu\n",
5239 prefix,
5240 erts_alc_a2ad[alloc_no],
5241 cs->blocks[ix].curr.no,
5242 cs->blocks[ix].max.no,
5243 cs->blocks[ix].max_ever.no);
5244 erts_print(to,
5245 arg,
5246 "%sblocks[%s] size: %bpu %bpu %bpu\n",
5247 prefix,
5248 erts_alc_a2ad[alloc_no],
5249 cs->blocks[ix].curr.size,
5250 cs->blocks[ix].max.size,
5251 cs->blocks[ix].max_ever.size);
5252 }
5253
5254 erts_print(to,
5255 arg,
5256 "%scarriers: %bpu %bpu %bpu\n",
5257 prefix,
5258 curr_no,
5259 cs->max.no,
5260 cs->max_ever.no);
5261 #if HAVE_ERTS_MSEG
5262 erts_print(to,
5263 arg,
5264 "%smseg carriers: %bpu\n",
5265 prefix,
5266 cs->carriers[ERTS_CRR_ALLOC_MSEG].no);
5267 #endif
5268 erts_print(to,
5269 arg,
5270 "%ssys_alloc carriers: %bpu\n",
5271 prefix,
5272 cs->carriers[ERTS_CRR_ALLOC_SYS].no);
5273 erts_print(to,
5274 arg,
5275 "%scarriers size: %bpu %bpu %bpu\n",
5276 prefix,
5277 curr_size,
5278 cs->max.size,
5279 cs->max_ever.size);
5280 #if HAVE_ERTS_MSEG
5281 erts_print(to,
5282 arg,
5283 "%smseg carriers size: %bpu\n",
5284 prefix,
5285 cs->carriers[ERTS_CRR_ALLOC_MSEG].size);
5286 #endif
5287 erts_print(to,
5288 arg,
5289 "%ssys_alloc carriers size: %bpu\n",
5290 prefix,
5291 cs->carriers[ERTS_CRR_ALLOC_SYS].size);
5292 }
5293
5294 if (hpp || szp) {
5295 Eterm blocks;
5296 int alloc_no;
5297
5298 res = NIL;
5299 add_2tup(hpp, szp, &res,
5300 am.sys_alloc_carriers_size,
5301 bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_SYS].size));
5302 #if HAVE_ERTS_MSEG
5303 add_2tup(hpp, szp, &res,
5304 am.mseg_alloc_carriers_size,
5305 bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_MSEG].size));
5306 #endif
5307 add_4tup(hpp, szp, &res,
5308 am.carriers_size,
5309 bld_unstable_uint(hpp, szp, curr_size),
5310 bld_unstable_uint(hpp, szp, cs->max.size),
5311 bld_unstable_uint(hpp, szp, cs->max_ever.size));
5312 add_2tup(hpp, szp, &res,
5313 am.sys_alloc_carriers,
5314 bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_SYS].no));
5315 #if HAVE_ERTS_MSEG
5316 add_2tup(hpp, szp, &res,
5317 am.mseg_alloc_carriers,
5318 bld_unstable_uint(hpp, szp, cs->carriers[ERTS_CRR_ALLOC_MSEG].no));
5319 #endif
5320 add_4tup(hpp, szp, &res,
5321 am.carriers,
5322 bld_unstable_uint(hpp, szp, curr_no),
5323 bld_unstable_uint(hpp, szp, cs->max.no),
5324 bld_unstable_uint(hpp, szp, cs->max_ever.no));
5325
5326 blocks = NIL;
5327 for (alloc_no = ERTS_ALC_A_MIN;
5328 alloc_no <= ERTS_ALC_A_MAX;
5329 alloc_no++) {
5330 int ix = alloc_no - ERTS_ALC_A_MIN;
5331 UWord curr_size, max_size, max_ever_size;
5332 UWord curr_no, max_no, max_ever_no;
5333 Eterm info;
5334
5335 curr_size = cs->blocks[ix].curr.size;
5336 max_size = cs->blocks[ix].max.size;
5337 max_ever_size = cs->blocks[ix].max_ever.size;
5338
5339 curr_no = cs->blocks[ix].curr.no;
5340 max_no = cs->blocks[ix].max.no;
5341 max_ever_no = cs->blocks[ix].max_ever.no;
5342
5343 if (max_ever_no == 0) {
5344 continue;
5345 }
5346
5347 info = NIL;
5348
5349 add_4tup(hpp, szp, &info,
5350 am.size,
5351 bld_unstable_uint(hpp, szp, curr_size),
5352 bld_unstable_uint(hpp, szp, max_size),
5353 bld_unstable_uint(hpp, szp, max_ever_size));
5354
5355 add_4tup(hpp, szp, &info,
5356 am.count,
5357 bld_unstable_uint(hpp, szp, curr_no),
5358 bld_unstable_uint(hpp, szp, max_no),
5359 bld_unstable_uint(hpp, szp, max_ever_no));
5360
5361 add_2tup(hpp, szp, &blocks, alloc_num_atoms[alloc_no], info);
5362 }
5363
5364 add_2tup(hpp, szp, &res, am.blocks, blocks);
5365 }
5366
5367 return res;
5368 }
5369
5370 static void
make_name_atoms(Allctr_t * allctr)5371 make_name_atoms(Allctr_t *allctr)
5372 {
5373 char alloc[] = "alloc";
5374 char realloc[] = "realloc";
5375 char free[] = "free";
5376 char buf[MAX_ATOM_CHARACTERS];
5377 size_t prefix_len = sys_strlen(allctr->name_prefix);
5378
5379 if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1)
5380 erts_exit(ERTS_ERROR_EXIT,"Too long allocator name: %salloc\n",allctr->name_prefix);
5381
5382 sys_memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len);
5383
5384 sys_memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1);
5385 allctr->name.alloc = am_atom_put(buf, prefix_len + sizeof(alloc) - 1);
5386
5387 sys_memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1);
5388 allctr->name.realloc = am_atom_put(buf, prefix_len + sizeof(realloc) - 1);
5389
5390 sys_memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1);
5391 allctr->name.free = am_atom_put(buf, prefix_len + sizeof(free) - 1);
5392
5393 }
5394
5395 static Eterm
info_calls(Allctr_t * allctr,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5396 info_calls(Allctr_t *allctr,
5397 fmtfn_t *print_to_p,
5398 void *print_to_arg,
5399 Uint **hpp,
5400 Uint *szp)
5401 {
5402 Eterm res = THE_NON_VALUE;
5403
5404
5405 if (print_to_p) {
5406
5407 #define PRINT_CC_4(TO, TOA, NAME, CC) \
5408 erts_print(TO, TOA, "%s calls: %b64u\n", NAME, CC)
5409
5410 #define PRINT_CC_5(TO, TOA, PRFX, NAME, CC) \
5411 erts_print(TO, TOA, "%s%s calls: %b64u\n",PRFX,NAME,CC)
5412
5413 char *prefix = allctr->name_prefix;
5414 fmtfn_t to = *print_to_p;
5415 void *arg = print_to_arg;
5416
5417 PRINT_CC_5(to, arg, prefix, "alloc", allctr->calls.this_alloc);
5418 PRINT_CC_5(to, arg, prefix, "free", allctr->calls.this_free);
5419 PRINT_CC_5(to, arg, prefix, "realloc", allctr->calls.this_realloc);
5420
5421 #if HAVE_ERTS_MSEG
5422 PRINT_CC_4(to, arg, "mseg_alloc", allctr->calls.mseg_alloc);
5423 PRINT_CC_4(to, arg, "mseg_dealloc", allctr->calls.mseg_dealloc);
5424 PRINT_CC_4(to, arg, "mseg_realloc", allctr->calls.mseg_realloc);
5425 #endif
5426
5427 PRINT_CC_4(to, arg, "sys_alloc", allctr->calls.sys_alloc);
5428 PRINT_CC_4(to, arg, "sys_free", allctr->calls.sys_free);
5429 PRINT_CC_4(to, arg, "sys_realloc", allctr->calls.sys_realloc);
5430
5431 #undef PRINT_CC_4
5432 #undef PRINT_CC_5
5433
5434 }
5435
5436
5437 if (hpp || szp) {
5438
5439 ASSERT(allctr->name.alloc != THE_NON_VALUE);
5440 ASSERT(allctr->name.realloc != THE_NON_VALUE);
5441 ASSERT(allctr->name.free != THE_NON_VALUE);
5442
5443 res = NIL;
5444
5445 add_3tup(hpp, szp, &res,
5446 am.sys_realloc,
5447 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.sys_realloc)),
5448 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.sys_realloc)));
5449 add_3tup(hpp, szp, &res,
5450 am.sys_free,
5451 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.sys_free)),
5452 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.sys_free)));
5453 add_3tup(hpp, szp, &res,
5454 am.sys_alloc,
5455 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.sys_alloc)),
5456 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.sys_alloc)));
5457 #if HAVE_ERTS_MSEG
5458 add_3tup(hpp, szp, &res,
5459 am.mseg_realloc,
5460 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.mseg_realloc)),
5461 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.mseg_realloc)));
5462 add_3tup(hpp, szp, &res,
5463 am.mseg_dealloc,
5464 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.mseg_dealloc)),
5465 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.mseg_dealloc)));
5466 add_3tup(hpp, szp, &res,
5467 am.mseg_alloc,
5468 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.mseg_alloc)),
5469 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.mseg_alloc)));
5470 #endif
5471 add_3tup(hpp, szp, &res,
5472 allctr->name.realloc,
5473 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.this_realloc)),
5474 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.this_realloc)));
5475 add_3tup(hpp, szp, &res,
5476 allctr->name.free,
5477 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.this_free)),
5478 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.this_free)));
5479 add_3tup(hpp, szp, &res,
5480 allctr->name.alloc,
5481 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_GIGA_VAL(allctr->calls.this_alloc)),
5482 bld_unstable_uint(hpp, szp, ERTS_ALC_CC_VAL(allctr->calls.this_alloc)));
5483 }
5484
5485 return res;
5486 }
5487
5488 static Eterm
info_options(Allctr_t * allctr,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5489 info_options(Allctr_t *allctr,
5490 fmtfn_t *print_to_p,
5491 void *print_to_arg,
5492 Uint **hpp,
5493 Uint *szp)
5494 {
5495 Eterm res = THE_NON_VALUE;
5496 UWord acul, acnl, acfml;
5497 char *cp_str;
5498 Eterm cp_atom;
5499
5500 if (!allctr) {
5501 if (print_to_p)
5502 erts_print(*print_to_p, print_to_arg, "option e: false\n");
5503 if (hpp || szp) {
5504 res = NIL;
5505 add_2tup(hpp, szp, &res, am.e, am_false);
5506 }
5507 return res;
5508 }
5509
5510 acul = allctr->cpool.util_limit;
5511 acnl = allctr->cpool.in_pool_limit;
5512 acfml = allctr->cpool.fblk_min_limit;
5513 ASSERT(allctr->cpool.carrier_pool <= ERTS_ALC_A_MAX);
5514 if (allctr->cpool.carrier_pool < ERTS_ALC_A_MIN) {
5515 cp_str = "undefined";
5516 cp_atom = am_undefined;
5517 }
5518 else if (allctr->cpool.carrier_pool == ERTS_ALC_COMMON_CPOOL_IX) {
5519 cp_str = "@";
5520 cp_atom = am.At_sign;
5521 }
5522 else {
5523 cp_str = allocator_char_str[allctr->cpool.carrier_pool];
5524 cp_atom = allocator_char_atom[allctr->cpool.carrier_pool];
5525 }
5526
5527 if (print_to_p) {
5528 char topt[21]; /* Enough for any 64-bit integer */
5529 if (allctr->t)
5530 erts_snprintf(&topt[0], sizeof(topt), "%d", allctr->t);
5531 else
5532 erts_snprintf(&topt[0], sizeof(topt), "false");
5533 /*
5534 * Do not use '%T' in the format string here. You'll
5535 * likely get into lock order violations...
5536 */
5537 erts_print(*print_to_p,
5538 print_to_arg,
5539 "option e: true\n"
5540 "option t: %s\n"
5541 "option ramv: %s\n"
5542 "option atags: %s\n"
5543 "option sbct: %beu\n"
5544 #if HAVE_ERTS_MSEG
5545 "option asbcst: %bpu\n"
5546 "option rsbcst: %bpu\n"
5547 #endif
5548 "option rsbcmt: %beu\n"
5549 "option rmbcmt: %beu\n"
5550 "option mmbcs: %beu\n"
5551 #if HAVE_ERTS_MSEG
5552 "option mmsbc: %beu\n"
5553 "option mmmbc: %beu\n"
5554 #endif
5555 "option lmbcs: %beu\n"
5556 "option smbcs: %beu\n"
5557 "option mbcgs: %beu\n"
5558 "option acul: %bpu\n"
5559 "option acnl: %bpu\n"
5560 "option acfml: %bpu\n"
5561 "option cp: %s\n",
5562 topt,
5563 allctr->ramv ? "true" : "false",
5564 allctr->atags ? "true" : "false",
5565 allctr->sbc_threshold,
5566 #if HAVE_ERTS_MSEG
5567 allctr->mseg_opt.abs_shrink_th,
5568 allctr->mseg_opt.rel_shrink_th,
5569 #endif
5570 allctr->sbc_move_threshold,
5571 allctr->mbc_move_threshold,
5572 allctr->main_carrier_size,
5573 #if HAVE_ERTS_MSEG
5574 allctr->max_mseg_sbcs,
5575 allctr->max_mseg_mbcs,
5576 #endif
5577 allctr->largest_mbc_size,
5578 allctr->smallest_mbc_size,
5579 allctr->mbc_growth_stages,
5580 acul,
5581 acnl,
5582 acfml,
5583 cp_str);
5584 }
5585
5586 res = (*allctr->info_options)(allctr, "option ", print_to_p, print_to_arg,
5587 hpp, szp);
5588
5589 if (hpp || szp) {
5590 add_2tup(hpp, szp, &res, am.cp, cp_atom);
5591 add_2tup(hpp, szp, &res,
5592 am.acfml,
5593 bld_uint(hpp, szp, acfml));
5594 add_2tup(hpp, szp, &res,
5595 am.acnl,
5596 bld_uint(hpp, szp, acnl));
5597 add_2tup(hpp, szp, &res,
5598 am.acul,
5599 bld_uint(hpp, szp, acul));
5600 add_2tup(hpp, szp, &res,
5601 am.mbcgs,
5602 bld_uint(hpp, szp, allctr->mbc_growth_stages));
5603 add_2tup(hpp, szp, &res,
5604 am.smbcs,
5605 bld_uint(hpp, szp, allctr->smallest_mbc_size));
5606 add_2tup(hpp, szp, &res,
5607 am.lmbcs,
5608 bld_uint(hpp, szp, allctr->largest_mbc_size));
5609 #if HAVE_ERTS_MSEG
5610 add_2tup(hpp, szp, &res,
5611 am.mmsbc,
5612 bld_uint(hpp, szp, allctr->max_mseg_sbcs));
5613 add_2tup(hpp, szp, &res,
5614 am.mmmbc,
5615 bld_uint(hpp, szp, allctr->max_mseg_mbcs));
5616 #endif
5617 add_2tup(hpp, szp, &res,
5618 am.mmbcs,
5619 bld_uint(hpp, szp, allctr->main_carrier_size));
5620 add_2tup(hpp, szp, &res,
5621 am.rmbcmt,
5622 bld_uint(hpp, szp, allctr->mbc_move_threshold));
5623 add_2tup(hpp, szp, &res,
5624 am.rsbcmt,
5625 bld_uint(hpp, szp, allctr->sbc_move_threshold));
5626 #if HAVE_ERTS_MSEG
5627 add_2tup(hpp, szp, &res,
5628 am.rsbcst,
5629 bld_uint(hpp, szp, allctr->mseg_opt.rel_shrink_th));
5630 add_2tup(hpp, szp, &res,
5631 am.asbcst,
5632 bld_uint(hpp, szp, allctr->mseg_opt.abs_shrink_th));
5633 #endif
5634 add_2tup(hpp, szp, &res,
5635 am_sbct,
5636 bld_uint(hpp, szp, allctr->sbc_threshold));
5637 add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false);
5638 add_2tup(hpp, szp, &res, am.atags, allctr->atags ? am_true : am_false);
5639 add_2tup(hpp, szp, &res, am.t, (allctr->t ? am_true : am_false));
5640 add_2tup(hpp, szp, &res, am.e, am_true);
5641 }
5642
5643 return res;
5644 }
5645
5646
5647 static ERTS_INLINE void
update_max_ever_values(CarriersStats_t * cs)5648 update_max_ever_values(CarriersStats_t *cs)
5649 {
5650 int ix;
5651
5652 for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
5653 BlockStats_t *bstats = &cs->blocks[ix];
5654
5655 if (bstats->max_ever.no < bstats->max.no) {
5656 bstats->max_ever.no = bstats->max.no;
5657 }
5658
5659 if (bstats->max_ever.size < bstats->max.size) {
5660 bstats->max_ever.size = bstats->max.size;
5661 }
5662 }
5663
5664 if (cs->max_ever.no < cs->max.no) {
5665 cs->max_ever.no = cs->max.no;
5666 }
5667
5668 if (cs->max_ever.size < cs->max.size) {
5669 cs->max_ever.size = cs->max.size;
5670 }
5671 }
5672
5673 static ERTS_INLINE void
reset_max_values(CarriersStats_t * cs)5674 reset_max_values(CarriersStats_t *cs)
5675 {
5676 UWord curr_no, curr_size;
5677 int ix;
5678
5679 for (ix = 0; ix < ERTS_ALC_A_COUNT; ix++) {
5680 BlockStats_t *bstats = &cs->blocks[ix];
5681
5682 bstats->max.no = bstats->curr.no;
5683 bstats->max.size = bstats->curr.size;
5684 }
5685
5686 curr_no = curr_size = 0;
5687 for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
5688 curr_no += cs->carriers[ix].no;
5689 curr_size += cs->carriers[ix].size;
5690 }
5691
5692 cs->max.no = curr_no;
5693 cs->max.size = curr_size;
5694 }
5695
5696
5697 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
5698 * Exported functions *
5699 \* */
5700
5701 Eterm
erts_alcu_au_info_options(fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5702 erts_alcu_au_info_options(fmtfn_t *print_to_p, void *print_to_arg,
5703 Uint **hpp, Uint *szp)
5704 {
5705 Eterm res = THE_NON_VALUE;
5706
5707 if (print_to_p) {
5708
5709 erts_print(*print_to_p,
5710 print_to_arg,
5711 #if HAVE_ERTS_MSEG
5712 "option mmc: %beu\n"
5713 #endif
5714 "option ycs: %beu\n"
5715 "option sac: %s\n",
5716 #if HAVE_ERTS_MSEG
5717 max_mseg_carriers,
5718 #endif
5719 sys_alloc_carrier_size,
5720 allow_sys_alloc_carriers ? "true" : "false");
5721 }
5722
5723 if (hpp || szp) {
5724 res = NIL;
5725 ensure_atoms_initialized(NULL);
5726 add_2tup(hpp, szp, &res,
5727 am.sac,
5728 allow_sys_alloc_carriers ? am_true : am_false);
5729 add_2tup(hpp, szp, &res,
5730 am.ycs,
5731 bld_uint(hpp, szp, sys_alloc_carrier_size));
5732 #if HAVE_ERTS_MSEG
5733 add_2tup(hpp, szp, &res,
5734 am.mmc,
5735 bld_uint(hpp, szp, max_mseg_carriers));
5736 #endif
5737 }
5738
5739 return res;
5740 }
5741
5742
5743 Eterm
erts_alcu_info_options(Allctr_t * allctr,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5744 erts_alcu_info_options(Allctr_t *allctr,
5745 fmtfn_t *print_to_p,
5746 void *print_to_arg,
5747 Uint **hpp,
5748 Uint *szp)
5749 {
5750 Eterm res;
5751
5752 if (hpp || szp)
5753 ensure_atoms_initialized(allctr);
5754
5755 if (allctr->thread_safe) {
5756 erts_allctr_wrapper_pre_lock();
5757 erts_mtx_lock(&allctr->mutex);
5758 }
5759 res = info_options(allctr, print_to_p, print_to_arg, hpp, szp);
5760 if (allctr->thread_safe) {
5761 erts_mtx_unlock(&allctr->mutex);
5762 erts_allctr_wrapper_pre_unlock();
5763 }
5764 return res;
5765 }
5766
5767 /* ----------------------------------------------------------------------- */
5768
5769 Eterm
erts_alcu_sz_info(Allctr_t * allctr,int internal,int begin_max_period,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5770 erts_alcu_sz_info(Allctr_t *allctr,
5771 int internal,
5772 int begin_max_period,
5773 fmtfn_t *print_to_p,
5774 void *print_to_arg,
5775 Uint **hpp,
5776 Uint *szp)
5777 {
5778 Eterm res, mbcs, sbcs, fix = THE_NON_VALUE;
5779 Eterm mbcs_pool;
5780
5781 res = THE_NON_VALUE;
5782
5783 if (!allctr) {
5784 if (print_to_p)
5785 erts_print(*print_to_p, print_to_arg, "false\n");
5786 if (szp)
5787 *szp = 0;
5788 return am_false;
5789 }
5790
5791 if (hpp || szp)
5792 ensure_atoms_initialized(allctr);
5793
5794 if (allctr->thread_safe) {
5795 erts_allctr_wrapper_pre_lock();
5796 erts_mtx_lock(&allctr->mutex);
5797 }
5798
5799 ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
5800
5801 update_max_ever_values(&allctr->mbcs);
5802 update_max_ever_values(&allctr->sbcs);
5803
5804 if (allctr->fix)
5805 fix = sz_info_fix(allctr, internal, print_to_p, print_to_arg, hpp, szp);
5806 mbcs = sz_info_carriers(allctr, &allctr->mbcs, "mbcs ", print_to_p,
5807 print_to_arg, hpp, szp);
5808 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
5809 mbcs_pool = info_cpool(allctr, 1, "mbcs_pool ", print_to_p,
5810 print_to_arg, hpp, szp);
5811 else
5812 mbcs_pool = THE_NON_VALUE; /* shut up annoying warning... */
5813 sbcs = sz_info_carriers(allctr, &allctr->sbcs, "sbcs ", print_to_p,
5814 print_to_arg, hpp, szp);
5815
5816 if (hpp || szp) {
5817 res = NIL;
5818 add_2tup(hpp, szp, &res, am.sbcs, sbcs);
5819 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
5820 add_2tup(hpp, szp, &res, am.mbcs_pool, mbcs_pool);
5821 add_2tup(hpp, szp, &res, am.mbcs, mbcs);
5822 add_fix_types(allctr, internal, hpp, szp, &res, fix);
5823 }
5824
5825 if (begin_max_period) {
5826 reset_max_values(&allctr->mbcs);
5827 reset_max_values(&allctr->sbcs);
5828 }
5829
5830
5831 if (allctr->thread_safe) {
5832 erts_mtx_unlock(&allctr->mutex);
5833 erts_allctr_wrapper_pre_unlock();
5834 }
5835
5836 return res;
5837 }
5838
5839
5840 Eterm
erts_alcu_info(Allctr_t * allctr,int internal,int begin_max_period,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)5841 erts_alcu_info(Allctr_t *allctr,
5842 int internal,
5843 int begin_max_period,
5844 fmtfn_t *print_to_p,
5845 void *print_to_arg,
5846 Uint **hpp,
5847 Uint *szp)
5848 {
5849 Eterm res, sett, mbcs, sbcs, calls, fix = THE_NON_VALUE;
5850 Eterm mbcs_pool;
5851
5852 res = THE_NON_VALUE;
5853
5854 if (!allctr) {
5855 if (print_to_p)
5856 erts_print(*print_to_p, print_to_arg, "false\n");
5857 if (szp)
5858 *szp = 0;
5859 return am_false;
5860 }
5861
5862 if (hpp || szp)
5863 ensure_atoms_initialized(allctr);
5864
5865 if (allctr->thread_safe) {
5866 erts_allctr_wrapper_pre_lock();
5867 erts_mtx_lock(&allctr->mutex);
5868 }
5869
5870 ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
5871
5872 update_max_ever_values(&allctr->mbcs);
5873 update_max_ever_values(&allctr->sbcs);
5874
5875 if (print_to_p) {
5876 erts_print(*print_to_p,
5877 print_to_arg,
5878 "versions: %s %s\n",
5879 allctr->vsn_str,
5880 ERTS_ALCU_VSN_STR);
5881 }
5882
5883 sett = info_options(allctr, print_to_p, print_to_arg, hpp, szp);
5884 if (allctr->fix)
5885 fix = sz_info_fix(allctr, internal, print_to_p, print_to_arg, hpp, szp);
5886 mbcs = info_carriers(allctr, &allctr->mbcs, "mbcs ", print_to_p,
5887 print_to_arg, hpp, szp);
5888 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
5889 mbcs_pool = info_cpool(allctr, 0, "mbcs_pool ", print_to_p,
5890 print_to_arg, hpp, szp);
5891 else
5892 mbcs_pool = THE_NON_VALUE; /* shut up annoying warning... */
5893 sbcs = info_carriers(allctr, &allctr->sbcs, "sbcs ", print_to_p,
5894 print_to_arg, hpp, szp);
5895 calls = info_calls(allctr, print_to_p, print_to_arg, hpp, szp);
5896
5897 if (hpp || szp) {
5898 res = NIL;
5899
5900 add_2tup(hpp, szp, &res, am.calls, calls);
5901 add_2tup(hpp, szp, &res, am.sbcs, sbcs);
5902 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
5903 add_2tup(hpp, szp, &res, am.mbcs_pool, mbcs_pool);
5904 add_2tup(hpp, szp, &res, am.mbcs, mbcs);
5905 add_fix_types(allctr, internal, hpp, szp, &res, fix);
5906 add_2tup(hpp, szp, &res, am.options, sett);
5907 add_3tup(hpp, szp, &res,
5908 am.versions,
5909 bld_string(hpp, szp, allctr->vsn_str),
5910 bld_string(hpp, szp, ERTS_ALCU_VSN_STR));;
5911 }
5912
5913 if (begin_max_period) {
5914 reset_max_values(&allctr->mbcs);
5915 reset_max_values(&allctr->sbcs);
5916 }
5917
5918
5919 if (allctr->thread_safe) {
5920 erts_mtx_unlock(&allctr->mutex);
5921 erts_allctr_wrapper_pre_unlock();
5922 }
5923
5924 return res;
5925 }
5926
5927 void
erts_alcu_foreign_size(Allctr_t * allctr,ErtsAlcType_t alloc_no,AllctrSize_t * size)5928 erts_alcu_foreign_size(Allctr_t *allctr, ErtsAlcType_t alloc_no, AllctrSize_t *size)
5929 {
5930 int ix;
5931
5932 sys_memset(size, 0, sizeof(*size));
5933
5934 if (allctr->thread_safe)
5935 erts_mtx_lock(&allctr->mutex);
5936
5937 for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
5938 size->carriers += allctr->mbcs.carriers[ix].size;
5939 size->carriers += allctr->sbcs.carriers[ix].size;
5940 }
5941
5942 ix = alloc_no - ERTS_ALC_A_MIN;
5943 size->blocks += allctr->mbcs.blocks[ix].curr.size;
5944 size->blocks += allctr->sbcs.blocks[ix].curr.size;
5945
5946 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
5947 UWord csz, bsz;
5948
5949 csz = bsz = 0;
5950 cpool_read_stat(allctr, alloc_no, NULL, &csz, NULL, &bsz);
5951
5952 size->carriers += csz;
5953 size->blocks += bsz;
5954 }
5955
5956 if (allctr->thread_safe)
5957 erts_mtx_unlock(&allctr->mutex);
5958 }
5959
5960 void
erts_alcu_current_size(Allctr_t * allctr,AllctrSize_t * size,ErtsAlcUFixInfo_t * fi,int fisz)5961 erts_alcu_current_size(Allctr_t *allctr, AllctrSize_t *size, ErtsAlcUFixInfo_t *fi, int fisz)
5962 {
5963 erts_alcu_foreign_size(allctr, allctr->alloc_no, size);
5964
5965 if (fi) {
5966 int ix;
5967
5968 if (allctr->thread_safe)
5969 erts_mtx_lock(&allctr->mutex);
5970
5971 for (ix = 0; ix < fisz; ix++) {
5972 if (allctr->fix) {
5973 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
5974 fi[ix].allocated += (allctr->fix[ix].type_size
5975 * allctr->fix[ix].u.cpool.allocated);
5976 fi[ix].used += (allctr->fix[ix].type_size
5977 * allctr->fix[ix].u.cpool.used);
5978 } else {
5979 fi[ix].allocated += (allctr->fix[ix].type_size
5980 * allctr->fix[ix].u.nocpool.allocated);
5981 fi[ix].used += (allctr->fix[ix].type_size
5982 * allctr->fix[ix].u.nocpool.used);
5983 }
5984 }
5985 }
5986
5987 if (allctr->thread_safe)
5988 erts_mtx_unlock(&allctr->mutex);
5989 }
5990 }
5991
5992 /* ----------------------------------------------------------------------- */
5993
5994 static ERTS_INLINE void *
do_erts_alcu_alloc(ErtsAlcType_t type,Allctr_t * allctr,Uint size)5995 do_erts_alcu_alloc(ErtsAlcType_t type, Allctr_t *allctr, Uint size)
5996 {
5997 void *res;
5998
5999 ASSERT(initialized);
6000
6001 ASSERT(allctr);
6002
6003 ERTS_LC_ASSERT(!allctr->thread_safe
6004 || erts_lc_mtx_is_locked(&allctr->mutex));
6005
6006 ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
6007
6008 /* Reject sizes that can't fit into the header word. */
6009 if (size > ~BLK_FLG_MASK) {
6010 return NULL;
6011 }
6012
6013 #if ALLOC_ZERO_EQ_NULL
6014 if (!size)
6015 return NULL;
6016 #endif
6017
6018 INC_CC(allctr->calls.this_alloc);
6019
6020 if (allctr->fix) {
6021 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
6022 return fix_cpool_alloc(allctr, type, size);
6023 else
6024 return fix_nocpool_alloc(allctr, type, size);
6025 }
6026
6027 if (size >= allctr->sbc_threshold) {
6028 Block_t *blk;
6029 blk = create_carrier(allctr, size, CFLG_SBC);
6030 res = blk ? BLK2UMEM(blk) : NULL;
6031 }
6032 else
6033 res = mbc_alloc(allctr, size);
6034
6035 return res;
6036 }
6037
erts_alcu_alloc(ErtsAlcType_t type,void * extra,Uint size)6038 void *erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size)
6039 {
6040 Allctr_t *allctr = (Allctr_t *) extra;
6041 void *res;
6042
6043 ASSERT(!"This is not thread safe");
6044
6045 res = do_erts_alcu_alloc(type, allctr, size);
6046
6047 if (allctr->atags && res) {
6048 set_alloc_tag(allctr, res, determine_alloc_tag(allctr, type));
6049 }
6050
6051 DEBUG_CHECK_ALIGNMENT(res);
6052
6053 return res;
6054 }
6055
6056
6057
6058 void *
erts_alcu_alloc_ts(ErtsAlcType_t type,void * extra,Uint size)6059 erts_alcu_alloc_ts(ErtsAlcType_t type, void *extra, Uint size)
6060 {
6061 Allctr_t *allctr = (Allctr_t *) extra;
6062 alcu_atag_t tag = 0;
6063 void *res;
6064
6065 if (allctr->atags) {
6066 tag = determine_alloc_tag(allctr, type);
6067 }
6068
6069 erts_mtx_lock(&allctr->mutex);
6070
6071 res = do_erts_alcu_alloc(type, allctr, size);
6072
6073 if (allctr->atags && res) {
6074 set_alloc_tag(allctr, res, tag);
6075 }
6076
6077 erts_mtx_unlock(&allctr->mutex);
6078
6079 DEBUG_CHECK_ALIGNMENT(res);
6080
6081 return res;
6082 }
6083
6084
6085 void *
erts_alcu_alloc_thr_spec(ErtsAlcType_t type,void * extra,Uint size)6086 erts_alcu_alloc_thr_spec(ErtsAlcType_t type, void *extra, Uint size)
6087 {
6088 ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
6089 int ix;
6090 alcu_atag_t tag = 0;
6091 Allctr_t *allctr;
6092 void *res;
6093
6094 ix = ERTS_ALC_GET_THR_IX();
6095
6096 ASSERT(0 <= ix && ix < tspec->size);
6097
6098 allctr = tspec->allctr[ix];
6099
6100 if (allctr->atags) {
6101 tag = determine_alloc_tag(allctr, type);
6102 }
6103
6104 if (allctr->thread_safe)
6105 erts_mtx_lock(&allctr->mutex);
6106
6107 res = do_erts_alcu_alloc(type, allctr, size);
6108
6109 if (allctr->atags && res) {
6110 set_alloc_tag(allctr, res, tag);
6111 }
6112
6113 if (allctr->thread_safe)
6114 erts_mtx_unlock(&allctr->mutex);
6115
6116 DEBUG_CHECK_ALIGNMENT(res);
6117
6118 return res;
6119 }
6120
6121 void *
erts_alcu_alloc_thr_pref(ErtsAlcType_t type,void * extra,Uint size)6122 erts_alcu_alloc_thr_pref(ErtsAlcType_t type, void *extra, Uint size)
6123 {
6124 Allctr_t *pref_allctr;
6125 alcu_atag_t tag = 0;
6126 void *res;
6127
6128 pref_allctr = get_pref_allctr(extra);
6129
6130 if (pref_allctr->atags) {
6131 tag = determine_alloc_tag(pref_allctr, type);
6132 }
6133
6134 if (pref_allctr->thread_safe)
6135 erts_mtx_lock(&pref_allctr->mutex);
6136
6137 ASSERT(pref_allctr->dd.use);
6138 ERTS_ALCU_HANDLE_DD_IN_OP(pref_allctr, 1);
6139
6140 ERTS_ALCU_DBG_CHK_THR_ACCESS(pref_allctr);
6141
6142 res = do_erts_alcu_alloc(type, pref_allctr, size);
6143
6144 if (!res && ERTS_ALCU_HANDLE_DD_IN_OP(pref_allctr, 1)) {
6145 /* Cleaned up a bit more; try one more time... */
6146 res = do_erts_alcu_alloc(type, pref_allctr, size);
6147 }
6148
6149 if (pref_allctr->atags && res) {
6150 set_alloc_tag(pref_allctr, res, tag);
6151 }
6152
6153 if (pref_allctr->thread_safe)
6154 erts_mtx_unlock(&pref_allctr->mutex);
6155
6156 DEBUG_CHECK_ALIGNMENT(res);
6157
6158 return res;
6159 }
6160
6161
6162
6163 /* ------------------------------------------------------------------------- */
6164
6165 static ERTS_INLINE void
do_erts_alcu_free(ErtsAlcType_t type,Allctr_t * allctr,void * p,Carrier_t ** busy_pcrr_pp)6166 do_erts_alcu_free(ErtsAlcType_t type, Allctr_t *allctr, void *p,
6167 Carrier_t **busy_pcrr_pp)
6168 {
6169 ASSERT(initialized);
6170
6171 ASSERT(allctr);
6172
6173 ERTS_LC_ASSERT(!allctr->thread_safe
6174 || erts_lc_mtx_is_locked(&allctr->mutex));
6175
6176 ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
6177
6178 if (p) {
6179 INC_CC(allctr->calls.this_free);
6180
6181 if (ERTS_ALC_IS_FIX_TYPE(type)) {
6182 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr))
6183 fix_cpool_free(allctr, type, 0, p, busy_pcrr_pp);
6184 else
6185 fix_nocpool_free(allctr, type, p);
6186 }
6187 else {
6188 Block_t *blk = UMEM2BLK(p);
6189 if (IS_SBC_BLK(blk))
6190 destroy_carrier(allctr, blk, NULL);
6191 else
6192 mbc_free(allctr, type, p, busy_pcrr_pp);
6193 }
6194 }
6195 }
6196
erts_alcu_free(ErtsAlcType_t type,void * extra,void * p)6197 void erts_alcu_free(ErtsAlcType_t type, void *extra, void *p)
6198 {
6199 Allctr_t *allctr = (Allctr_t *) extra;
6200 do_erts_alcu_free(type, allctr, p, NULL);
6201 }
6202
6203
6204 void
erts_alcu_free_ts(ErtsAlcType_t type,void * extra,void * p)6205 erts_alcu_free_ts(ErtsAlcType_t type, void *extra, void *p)
6206 {
6207 Allctr_t *allctr = (Allctr_t *) extra;
6208 erts_mtx_lock(&allctr->mutex);
6209 do_erts_alcu_free(type, allctr, p, NULL);
6210 erts_mtx_unlock(&allctr->mutex);
6211 }
6212
6213
6214 void
erts_alcu_free_thr_spec(ErtsAlcType_t type,void * extra,void * p)6215 erts_alcu_free_thr_spec(ErtsAlcType_t type, void *extra, void *p)
6216 {
6217 ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
6218 int ix;
6219 Allctr_t *allctr;
6220
6221 ix = ERTS_ALC_GET_THR_IX();
6222
6223 ASSERT(0 <= ix && ix < tspec->size);
6224
6225 allctr = tspec->allctr[ix];
6226
6227 if (allctr->thread_safe)
6228 erts_mtx_lock(&allctr->mutex);
6229
6230 do_erts_alcu_free(type, allctr, p, NULL);
6231
6232 if (allctr->thread_safe)
6233 erts_mtx_unlock(&allctr->mutex);
6234 }
6235
6236 void
erts_alcu_free_thr_pref(ErtsAlcType_t type,void * extra,void * p)6237 erts_alcu_free_thr_pref(ErtsAlcType_t type, void *extra, void *p)
6238 {
6239 if (p) {
6240 Carrier_t *busy_pcrr_p;
6241 Allctr_t *pref_allctr, *used_allctr;
6242
6243 pref_allctr = get_pref_allctr(extra);
6244 used_allctr = get_used_allctr(pref_allctr, ERTS_ALC_TS_PREF_LOCK_IF_USED,
6245 p, NULL, &busy_pcrr_p);
6246 if (pref_allctr != used_allctr) {
6247 enqueue_dealloc_other_instance(type,
6248 used_allctr,
6249 p,
6250 (used_allctr->dd.ix
6251 - pref_allctr->dd.ix));
6252 }
6253 else {
6254 ERTS_ALCU_DBG_CHK_THR_ACCESS(used_allctr);
6255 do_erts_alcu_free(type, used_allctr, p, &busy_pcrr_p);
6256 clear_busy_pool_carrier(used_allctr, busy_pcrr_p);
6257 if (pref_allctr->thread_safe)
6258 erts_mtx_unlock(&pref_allctr->mutex);
6259 }
6260 }
6261 }
6262
6263
6264
6265 /* ------------------------------------------------------------------------- */
6266
6267 static ERTS_INLINE void *
do_erts_alcu_realloc(ErtsAlcType_t type,Allctr_t * allctr,void * p,Uint size,Uint32 alcu_flgs,Carrier_t ** busy_pcrr_pp)6268 do_erts_alcu_realloc(ErtsAlcType_t type,
6269 Allctr_t *allctr,
6270 void *p,
6271 Uint size,
6272 Uint32 alcu_flgs,
6273 Carrier_t **busy_pcrr_pp)
6274 {
6275 Block_t *blk;
6276 void *res;
6277
6278 ASSERT(initialized);
6279
6280 ASSERT(allctr);
6281
6282 ERTS_LC_ASSERT(!allctr->thread_safe
6283 || erts_lc_mtx_is_locked(&allctr->mutex));
6284
6285 ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr);
6286
6287 if (!p) {
6288 res = do_erts_alcu_alloc(type, allctr, size);
6289 INC_CC(allctr->calls.this_realloc);
6290 DEC_CC(allctr->calls.this_alloc);
6291 return res;
6292 }
6293
6294 /* Reject sizes that can't fit into the header word. */
6295 if (size > ~BLK_FLG_MASK) {
6296 return NULL;
6297 }
6298
6299 #if ALLOC_ZERO_EQ_NULL
6300 if (!size) {
6301 ASSERT(p);
6302 do_erts_alcu_free(type, allctr, p, busy_pcrr_pp);
6303 INC_CC(allctr->calls.this_realloc);
6304 DEC_CC(allctr->calls.this_free);
6305 return NULL;
6306 }
6307 #endif
6308
6309 INC_CC(allctr->calls.this_realloc);
6310
6311 blk = UMEM2BLK(p);
6312
6313 if (size < allctr->sbc_threshold) {
6314 if (IS_MBC_BLK(blk))
6315 res = mbc_realloc(allctr, type, p, size, alcu_flgs, busy_pcrr_pp);
6316 else {
6317 Uint used_sz = SBC_HEADER_SIZE + ABLK_HDR_SZ + size;
6318 Uint crr_sz;
6319 Uint diff_sz_val;
6320 Uint crr_sz_val;
6321
6322 #if HAVE_ERTS_MSEG
6323 if (IS_SYS_ALLOC_CARRIER(BLK_TO_SBC(blk)))
6324 #endif
6325 crr_sz = SYS_ALLOC_CARRIER_CEILING(used_sz);
6326 #if HAVE_ERTS_MSEG
6327 else
6328 crr_sz = ERTS_SACRR_UNIT_CEILING(used_sz);
6329 #endif
6330 diff_sz_val = crr_sz - used_sz;
6331 if (diff_sz_val < (~((Uint) 0) / 100))
6332 crr_sz_val = crr_sz;
6333 else {
6334 /* div both by 128 */
6335 crr_sz_val = crr_sz >> 7;
6336 /* A sys_alloc carrier could potentially be
6337 smaller than 128 bytes (but not likely) */
6338 if (crr_sz_val == 0)
6339 goto do_carrier_resize;
6340 diff_sz_val >>= 7;
6341 }
6342
6343 if (100*diff_sz_val < allctr->sbc_move_threshold*crr_sz_val)
6344 /* Data won't be copied into a new carrier... */
6345 goto do_carrier_resize;
6346 else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
6347 return NULL;
6348
6349 res = mbc_alloc(allctr, size);
6350 if (res) {
6351 sys_memcpy((void*) res,
6352 (void*) p,
6353 MIN(SBC_BLK_SZ(blk) - ABLK_HDR_SZ, size));
6354 destroy_carrier(allctr, blk, NULL);
6355 }
6356 }
6357 }
6358 else {
6359 Block_t *new_blk;
6360 if(IS_SBC_BLK(blk)) {
6361 do_carrier_resize:
6362 new_blk = resize_carrier(allctr, blk, size, CFLG_SBC);
6363 res = new_blk ? BLK2UMEM(new_blk) : NULL;
6364 }
6365 else if (alcu_flgs & ERTS_ALCU_FLG_FAIL_REALLOC_MOVE)
6366 return NULL;
6367 else {
6368 new_blk = create_carrier(allctr, size, CFLG_SBC);
6369 if (new_blk) {
6370 res = BLK2UMEM(new_blk);
6371 sys_memcpy((void *) res,
6372 (void *) p,
6373 MIN(MBC_ABLK_SZ(blk) - ABLK_HDR_SZ, size));
6374 mbc_free(allctr, type, p, busy_pcrr_pp);
6375 }
6376 else
6377 res = NULL;
6378 }
6379 }
6380
6381 return res;
6382 }
6383
6384 void *
erts_alcu_realloc(ErtsAlcType_t type,void * extra,void * p,Uint size)6385 erts_alcu_realloc(ErtsAlcType_t type, void *extra, void *p, Uint size)
6386 {
6387 Allctr_t *allctr = (Allctr_t *)extra;
6388 void *res;
6389
6390 res = do_erts_alcu_realloc(type, allctr, p, size, 0, NULL);
6391
6392 DEBUG_CHECK_ALIGNMENT(res);
6393
6394 if (allctr->atags && res) {
6395 set_alloc_tag(allctr, res, determine_alloc_tag(allctr, type));
6396 }
6397
6398 return res;
6399 }
6400
6401 void *
erts_alcu_realloc_mv(ErtsAlcType_t type,void * extra,void * p,Uint size)6402 erts_alcu_realloc_mv(ErtsAlcType_t type, void *extra, void *p, Uint size)
6403 {
6404 Allctr_t *allctr = (Allctr_t *)extra;
6405 void *res;
6406
6407 res = do_erts_alcu_alloc(type, allctr, size);
6408 if (!res)
6409 res = do_erts_alcu_realloc(type, allctr, p, size, 0, NULL);
6410 else {
6411 Block_t *blk;
6412 size_t cpy_size;
6413
6414 blk = UMEM2BLK(p);
6415 cpy_size = BLK_SZ(blk) - ABLK_HDR_SZ;
6416 if (cpy_size > size)
6417 cpy_size = size;
6418 sys_memcpy(res, p, cpy_size);
6419 do_erts_alcu_free(type, allctr, p, NULL);
6420 }
6421
6422 DEBUG_CHECK_ALIGNMENT(res);
6423
6424 if (allctr->atags && res) {
6425 set_alloc_tag(allctr, res, determine_alloc_tag(allctr, type));
6426 }
6427
6428 return res;
6429 }
6430
6431
6432 void *
erts_alcu_realloc_ts(ErtsAlcType_t type,void * extra,void * ptr,Uint size)6433 erts_alcu_realloc_ts(ErtsAlcType_t type, void *extra, void *ptr, Uint size)
6434 {
6435 Allctr_t *allctr = (Allctr_t *) extra;
6436 alcu_atag_t tag = 0;
6437 void *res;
6438
6439 if (allctr->atags) {
6440 tag = determine_alloc_tag(allctr, type);
6441 }
6442
6443 erts_mtx_lock(&allctr->mutex);
6444
6445 res = do_erts_alcu_realloc(type, allctr, ptr, size, 0, NULL);
6446
6447 if (allctr->atags && res) {
6448 set_alloc_tag(allctr, res, tag);
6449 }
6450
6451 erts_mtx_unlock(&allctr->mutex);
6452
6453 DEBUG_CHECK_ALIGNMENT(res);
6454
6455 return res;
6456 }
6457
6458 void *
erts_alcu_realloc_mv_ts(ErtsAlcType_t type,void * extra,void * p,Uint size)6459 erts_alcu_realloc_mv_ts(ErtsAlcType_t type, void *extra, void *p, Uint size)
6460 {
6461 Allctr_t *allctr = (Allctr_t *) extra;
6462 alcu_atag_t tag = 0;
6463 void *res;
6464
6465 if (allctr->atags) {
6466 tag = determine_alloc_tag(allctr, type);
6467 }
6468
6469 erts_mtx_lock(&allctr->mutex);
6470 res = do_erts_alcu_alloc(type, allctr, size);
6471 if (!res)
6472 res = do_erts_alcu_realloc(type, allctr, p, size, 0, NULL);
6473 else {
6474 Block_t *blk;
6475 size_t cpy_size;
6476
6477 blk = UMEM2BLK(p);
6478 cpy_size = BLK_SZ(blk) - ABLK_HDR_SZ;
6479 if (cpy_size > size)
6480 cpy_size = size;
6481 sys_memcpy(res, p, cpy_size);
6482 do_erts_alcu_free(type, allctr, p, NULL);
6483 }
6484
6485 if (allctr->atags && res) {
6486 set_alloc_tag(allctr, res, tag);
6487 }
6488
6489 erts_mtx_unlock(&allctr->mutex);
6490
6491 DEBUG_CHECK_ALIGNMENT(res);
6492
6493 return res;
6494 }
6495
6496
6497 void *
erts_alcu_realloc_thr_spec(ErtsAlcType_t type,void * extra,void * ptr,Uint size)6498 erts_alcu_realloc_thr_spec(ErtsAlcType_t type, void *extra,
6499 void *ptr, Uint size)
6500 {
6501 ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
6502 int ix;
6503 alcu_atag_t tag = 0;
6504 Allctr_t *allctr;
6505 void *res;
6506
6507 ix = ERTS_ALC_GET_THR_IX();
6508
6509 ASSERT(0 <= ix && ix < tspec->size);
6510
6511 allctr = tspec->allctr[ix];
6512
6513 if (allctr->atags) {
6514 tag = determine_alloc_tag(allctr, type);
6515 }
6516
6517 if (allctr->thread_safe)
6518 erts_mtx_lock(&allctr->mutex);
6519
6520 res = do_erts_alcu_realloc(type, allctr, ptr, size, 0, NULL);
6521
6522 if (allctr->atags && res) {
6523 set_alloc_tag(allctr, res, tag);
6524 }
6525
6526 if (allctr->thread_safe)
6527 erts_mtx_unlock(&allctr->mutex);
6528
6529 DEBUG_CHECK_ALIGNMENT(res);
6530
6531 return res;
6532 }
6533
6534 void *
erts_alcu_realloc_mv_thr_spec(ErtsAlcType_t type,void * extra,void * ptr,Uint size)6535 erts_alcu_realloc_mv_thr_spec(ErtsAlcType_t type, void *extra,
6536 void *ptr, Uint size)
6537 {
6538 ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t *) extra;
6539 int ix;
6540 alcu_atag_t tag = 0;
6541 Allctr_t *allctr;
6542 void *res;
6543
6544 ix = ERTS_ALC_GET_THR_IX();
6545
6546 ASSERT(0 <= ix && ix < tspec->size);
6547
6548 allctr = tspec->allctr[ix];
6549
6550 if (allctr->atags) {
6551 tag = determine_alloc_tag(allctr, type);
6552 }
6553
6554 if (allctr->thread_safe)
6555 erts_mtx_lock(&allctr->mutex);
6556
6557 res = do_erts_alcu_alloc(type, allctr, size);
6558 if (!res) {
6559 res = do_erts_alcu_realloc(type, allctr, ptr, size, 0, NULL);
6560 }
6561 else {
6562 Block_t *blk;
6563 size_t cpy_size;
6564
6565 blk = UMEM2BLK(ptr);
6566 cpy_size = BLK_SZ(blk) - ABLK_HDR_SZ;
6567 if (cpy_size > size)
6568 cpy_size = size;
6569 sys_memcpy(res, ptr, cpy_size);
6570 do_erts_alcu_free(type, allctr, ptr, NULL);
6571 }
6572
6573 if (allctr->atags && res) {
6574 set_alloc_tag(allctr, res, tag);
6575 }
6576
6577 if (allctr->thread_safe)
6578 erts_mtx_unlock(&allctr->mutex);
6579
6580 DEBUG_CHECK_ALIGNMENT(res);
6581
6582 return res;
6583 }
6584
6585 static ERTS_INLINE void *
realloc_thr_pref(ErtsAlcType_t type,Allctr_t * pref_allctr,void * p,Uint size,int force_move)6586 realloc_thr_pref(ErtsAlcType_t type, Allctr_t *pref_allctr, void *p, Uint size,
6587 int force_move)
6588 {
6589 void *res;
6590 Allctr_t *used_allctr;
6591 UWord old_user_size;
6592 Carrier_t *busy_pcrr_p;
6593 alcu_atag_t tag = 0;
6594 int retried;
6595
6596 if (pref_allctr->atags) {
6597 tag = determine_alloc_tag(pref_allctr, type);
6598 }
6599
6600 if (pref_allctr->thread_safe)
6601 erts_mtx_lock(&pref_allctr->mutex);
6602
6603 ASSERT(pref_allctr->dd.use);
6604 ERTS_ALCU_HANDLE_DD_IN_OP(pref_allctr, 1);
6605 retried = 0;
6606 restart:
6607
6608 used_allctr = get_used_allctr(pref_allctr, ERTS_ALC_TS_PREF_LOCK_NO,
6609 p, &old_user_size, &busy_pcrr_p);
6610
6611 ASSERT(used_allctr && pref_allctr);
6612
6613 if (!force_move && used_allctr == pref_allctr) {
6614 ERTS_ALCU_DBG_CHK_THR_ACCESS(used_allctr);
6615 res = do_erts_alcu_realloc(type,
6616 used_allctr,
6617 p,
6618 size,
6619 0,
6620 &busy_pcrr_p);
6621 clear_busy_pool_carrier(used_allctr, busy_pcrr_p);
6622 if (!res && !retried && ERTS_ALCU_HANDLE_DD_IN_OP(pref_allctr, 1)) {
6623 /* Cleaned up a bit more; try one more time... */
6624 retried = 1;
6625 goto restart;
6626 }
6627
6628 if (pref_allctr->atags && res) {
6629 set_alloc_tag(pref_allctr, res, tag);
6630 }
6631
6632 if (pref_allctr->thread_safe)
6633 erts_mtx_unlock(&pref_allctr->mutex);
6634 }
6635 else {
6636 res = do_erts_alcu_alloc(type, pref_allctr, size);
6637 if (!res)
6638 goto unlock_ts_return;
6639 else {
6640 if (pref_allctr->atags) {
6641 set_alloc_tag(pref_allctr, res, tag);
6642 }
6643
6644 DEBUG_CHECK_ALIGNMENT(res);
6645
6646 if (used_allctr != pref_allctr) {
6647 if (pref_allctr->thread_safe)
6648 erts_mtx_unlock(&pref_allctr->mutex);
6649
6650 sys_memcpy(res, p, MIN(size, old_user_size));
6651
6652 enqueue_dealloc_other_instance(type,
6653 used_allctr,
6654 p,
6655 (used_allctr->dd.ix
6656 - pref_allctr->dd.ix));
6657 }
6658 else {
6659
6660 sys_memcpy(res, p, MIN(size, old_user_size));
6661
6662 do_erts_alcu_free(type, used_allctr, p, &busy_pcrr_p);
6663 ASSERT(pref_allctr == used_allctr);
6664 clear_busy_pool_carrier(used_allctr, busy_pcrr_p);
6665
6666 unlock_ts_return:
6667 if (pref_allctr->thread_safe)
6668 erts_mtx_unlock(&pref_allctr->mutex);
6669 }
6670 }
6671 }
6672
6673 DEBUG_CHECK_ALIGNMENT(res);
6674
6675 return res;
6676 }
6677
6678 void *
erts_alcu_realloc_thr_pref(ErtsAlcType_t type,void * extra,void * p,Uint size)6679 erts_alcu_realloc_thr_pref(ErtsAlcType_t type, void *extra, void *p, Uint size)
6680 {
6681 if (p) {
6682 Allctr_t *pref_allctr = get_pref_allctr(extra);
6683
6684 return realloc_thr_pref(type, pref_allctr, p, size, 0);
6685 }
6686
6687 return erts_alcu_alloc_thr_pref(type, extra, size);
6688 }
6689
6690 void *
erts_alcu_realloc_mv_thr_pref(ErtsAlcType_t type,void * extra,void * p,Uint size)6691 erts_alcu_realloc_mv_thr_pref(ErtsAlcType_t type, void *extra,
6692 void *p, Uint size)
6693 {
6694 if (p) {
6695 Allctr_t *pref_allctr = get_pref_allctr(extra);
6696
6697 return realloc_thr_pref(type, pref_allctr, p, size, 1);
6698 }
6699
6700 return erts_alcu_alloc_thr_pref(type, extra, size);
6701 }
6702
6703
6704
adjust_sbct(Allctr_t * allctr,Uint sbct)6705 static Uint adjust_sbct(Allctr_t* allctr, Uint sbct)
6706 {
6707 #ifndef ARCH_64
6708 if (sbct > 0) {
6709 Uint max_mbc_block_sz = UNIT_CEILING(sbct - 1 + ABLK_HDR_SZ);
6710 if (max_mbc_block_sz + UNIT_FLOOR(allctr->min_block_size - 1) > MBC_ABLK_SZ_MASK
6711 || max_mbc_block_sz < sbct) { /* wrap around */
6712 /*
6713 * By limiting sbc_threshold to (hard limit - min_block_size)
6714 * we avoid having to split off free "residue blocks"
6715 * smaller than min_block_size.
6716 */
6717 max_mbc_block_sz = MBC_ABLK_SZ_MASK - UNIT_FLOOR(allctr->min_block_size - 1);
6718 sbct = max_mbc_block_sz - ABLK_HDR_SZ + 1;
6719 }
6720 }
6721 #endif
6722 return sbct;
6723 }
6724
erts_alcu_try_set_dyn_param(Allctr_t * allctr,Eterm param,Uint value)6725 int erts_alcu_try_set_dyn_param(Allctr_t* allctr, Eterm param, Uint value)
6726 {
6727 const Uint MIN_DYN_SBCT = 4000; /* a lame catastrophe prevention */
6728
6729 if (param == am_sbct && value >= MIN_DYN_SBCT) {
6730 allctr->sbc_threshold = adjust_sbct(allctr, value);
6731 return 1;
6732 }
6733 return 0;
6734 }
6735
6736 /* ------------------------------------------------------------------------- */
6737
6738 int
erts_alcu_start(Allctr_t * allctr,AllctrInit_t * init)6739 erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
6740 {
6741 /* erts_alcu_start assumes that allctr has been zeroed */
6742 int i;
6743
6744 if (((UWord)allctr & ERTS_CRR_ALCTR_FLG_MASK) != 0) {
6745 erts_exit(ERTS_ABORT_EXIT, "%s:%d:erts_alcu_start: Alignment error\n",
6746 __FILE__, __LINE__);
6747 }
6748
6749 /* The various fields packed into the header word must not overlap */
6750 ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & MBC_ABLK_SZ_MASK));
6751 ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & BLK_FLG_MASK));
6752 ERTS_CT_ASSERT(!(MBC_ABLK_SZ_MASK & BLK_FLG_MASK));
6753 ERTS_CT_ASSERT(!(MBC_FBLK_SZ_MASK & BLK_FLG_MASK));
6754 ERTS_CT_ASSERT(!(SBC_BLK_SZ_MASK & BLK_FLG_MASK));
6755 ERTS_CT_ASSERT(!(CRR_SZ_MASK & CRR_FLG_MASK));
6756
6757 if (!initialized)
6758 goto error;
6759
6760 #if HAVE_ERTS_MSEG
6761 sys_memcpy((void *) &allctr->mseg_opt,
6762 (void *) &erts_mseg_default_opt,
6763 sizeof(ErtsMsegOpt_t));
6764 if (init->tspec || init->tpref)
6765 allctr->mseg_opt.sched_spec = 1;
6766 #endif /* HAVE_ERTS_MSEG */
6767
6768 allctr->name_prefix = init->name_prefix;
6769 if (!allctr->name_prefix)
6770 goto error;
6771
6772 allctr->ix = init->ix;
6773 allctr->alloc_no = init->alloc_no;
6774 allctr->alloc_strat = init->alloc_strat;
6775
6776 ASSERT(allctr->alloc_no >= ERTS_ALC_A_MIN &&
6777 allctr->alloc_no <= ERTS_ALC_A_MAX);
6778
6779 if (allctr->alloc_no < ERTS_ALC_A_MIN
6780 || ERTS_ALC_A_MAX < allctr->alloc_no)
6781 allctr->alloc_no = ERTS_ALC_A_INVALID;
6782
6783 if (!allctr->vsn_str)
6784 goto error;
6785
6786 allctr->name.alloc = THE_NON_VALUE;
6787 allctr->name.realloc = THE_NON_VALUE;
6788 allctr->name.free = THE_NON_VALUE;
6789
6790 if (init->tspec)
6791 allctr->t = init->tspec;
6792 else if (init->tpref)
6793 allctr->t = init->tpref;
6794 else
6795 allctr->t = 0;
6796
6797 allctr->ramv = init->ramv;
6798 allctr->atags = init->atags;
6799 allctr->main_carrier_size = init->mmbcs;
6800
6801 #if HAVE_ERTS_MSEG
6802 allctr->mseg_opt.abs_shrink_th = init->asbcst;
6803 allctr->mseg_opt.rel_shrink_th = init->rsbcst;
6804 #endif
6805 allctr->sbc_move_threshold = init->rsbcmt;
6806 allctr->mbc_move_threshold = init->rmbcmt;
6807 #if HAVE_ERTS_MSEG
6808 allctr->max_mseg_sbcs = init->mmsbc;
6809 # if ERTS_SUPER_ALIGNED_MSEG_ONLY
6810 allctr->max_mseg_mbcs = ~(Uint)0;
6811 # else
6812 allctr->max_mseg_mbcs = init->mmmbc;
6813 # endif
6814 #endif
6815
6816 allctr->largest_mbc_size = MAX(init->lmbcs, init->smbcs);
6817 #ifndef ARCH_64
6818 if (allctr->largest_mbc_size > MBC_SZ_MAX_LIMIT) {
6819 allctr->largest_mbc_size = MBC_SZ_MAX_LIMIT;
6820 }
6821 #endif
6822 allctr->smallest_mbc_size = init->smbcs;
6823 allctr->mbc_growth_stages = MAX(1, init->mbcgs);
6824
6825 if (allctr->min_block_size < ABLK_HDR_SZ)
6826 goto error;
6827 allctr->min_block_size = UNIT_CEILING(allctr->min_block_size
6828 + sizeof(FreeBlkFtr_t));
6829 if (init->tpref) {
6830 Uint sz = ABLK_HDR_SZ;
6831 sz += sizeof(ErtsAllctrDDBlock_t);
6832 sz = UNIT_CEILING(sz);
6833 if (sz > allctr->min_block_size)
6834 allctr->min_block_size = sz;
6835 }
6836
6837 allctr->cpool.pooled_tree = NULL;
6838 allctr->cpool.dc_list.first = NULL;
6839 allctr->cpool.dc_list.last = NULL;
6840 allctr->cpool.abandon_limit = 0;
6841 allctr->cpool.disable_abandon = 0;
6842
6843 for (i = 0; i < ERTS_ALC_A_COUNT; i++) {
6844 erts_atomic_init_nob(&allctr->cpool.stat.blocks_size[i], 0);
6845 erts_atomic_init_nob(&allctr->cpool.stat.no_blocks[i], 0);
6846 }
6847
6848 erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0);
6849 erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0);
6850 if (!init->ts && init->acul && init->acnl && init->cp >= 0) {
6851 ASSERT(allctr->add_mbc);
6852 ASSERT(allctr->remove_mbc);
6853 ASSERT(allctr->largest_fblk_in_mbc);
6854 ASSERT(allctr->first_fblk_in_mbc);
6855 ASSERT(allctr->next_fblk_in_mbc);
6856
6857 allctr->cpool.util_limit = init->acul;
6858 allctr->cpool.in_pool_limit = init->acnl;
6859 allctr->cpool.fblk_min_limit = init->acfml;
6860 allctr->cpool.carrier_pool = init->cp;
6861
6862 if (allctr->alloc_strat == ERTS_ALC_S_FIRSTFIT) {
6863 allctr->cpool.sentinel = &firstfit_carrier_pools[init->cp].sentinel;
6864 }
6865 else if (allctr->alloc_no != ERTS_ALC_A_TEST) {
6866 ERTS_INTERNAL_ERROR("Impossible carrier migration config.");
6867 }
6868 }
6869 else {
6870 allctr->cpool.util_limit = 0;
6871 allctr->cpool.in_pool_limit = 0;
6872 allctr->cpool.fblk_min_limit = 0;
6873 allctr->cpool.carrier_pool = -1;
6874 }
6875
6876 /* The invasive tests don't really care whether the pool is enabled or not,
6877 * so we need to set this unconditionally for this allocator type. */
6878 if (allctr->alloc_no == ERTS_ALC_A_TEST) {
6879 allctr->cpool.sentinel = &firstfit_carrier_pools[ERTS_ALC_TEST_CPOOL_IX].sentinel;
6880 }
6881
6882 allctr->sbc_threshold = adjust_sbct(allctr, init->sbct);
6883
6884 #if HAVE_ERTS_MSEG
6885 if (allctr->mseg_opt.abs_shrink_th > ~((UWord) 0) / 100)
6886 allctr->mseg_opt.abs_shrink_th = ~((UWord) 0) / 100;
6887 #endif
6888
6889 if (init->ts) {
6890 allctr->thread_safe = 1;
6891
6892 erts_mtx_init(&allctr->mutex, "alcu_allocator", make_small(allctr->alloc_no),
6893 ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
6894
6895 #ifdef DEBUG
6896 allctr->debug.saved_tid = 0;
6897 #endif
6898 }
6899
6900 if(!allctr->get_free_block
6901 || !allctr->link_free_block
6902 || !allctr->unlink_free_block
6903 || !allctr->info_options)
6904 goto error;
6905
6906 if (!allctr->get_next_mbc_size)
6907 allctr->get_next_mbc_size = get_next_mbc_size;
6908
6909 if (allctr->mbc_header_size < sizeof(Carrier_t))
6910 goto error;
6911 allctr->dd.use = 0;
6912 if (init->tpref) {
6913 allctr->dd.use = 1;
6914 init_dd_queue(&allctr->dd.q);
6915 allctr->dd.ix = init->ix;
6916 }
6917 allctr->mbc_header_size = (UNIT_CEILING(allctr->mbc_header_size
6918 + ABLK_HDR_SZ)
6919 - ABLK_HDR_SZ);
6920
6921 if (init->sys_alloc) {
6922 ASSERT(init->sys_realloc && init->sys_dealloc);
6923 allctr->sys_alloc = init->sys_alloc;
6924 allctr->sys_realloc = init->sys_realloc;
6925 allctr->sys_dealloc = init->sys_dealloc;
6926 }
6927 else {
6928 ASSERT(!init->sys_realloc && !init->sys_dealloc);
6929 allctr->sys_alloc = &erts_alcu_sys_alloc;
6930 allctr->sys_realloc = &erts_alcu_sys_realloc;
6931 allctr->sys_dealloc = &erts_alcu_sys_dealloc;
6932 }
6933
6934 allctr->try_set_dyn_param = &erts_alcu_try_set_dyn_param;
6935
6936 #if HAVE_ERTS_MSEG
6937 if (init->mseg_alloc) {
6938 ASSERT(init->mseg_realloc && init->mseg_dealloc);
6939 allctr->mseg_alloc = init->mseg_alloc;
6940 allctr->mseg_realloc = init->mseg_realloc;
6941 allctr->mseg_dealloc = init->mseg_dealloc;
6942 allctr->mseg_mmapper = init->mseg_mmapper;
6943 }
6944 else {
6945 ASSERT(!init->mseg_realloc && !init->mseg_dealloc);
6946 allctr->mseg_alloc = &erts_alcu_mseg_alloc;
6947 allctr->mseg_realloc = &erts_alcu_mseg_realloc;
6948 allctr->mseg_dealloc = &erts_alcu_mseg_dealloc;
6949 }
6950
6951 /* If a custom carrier alloc function is specified, make sure it's used */
6952 if (init->mseg_alloc && !init->sys_alloc) {
6953 allctr->crr_set_flgs = CFLG_FORCE_MSEG;
6954 allctr->crr_clr_flgs = CFLG_FORCE_SYS_ALLOC;
6955 }
6956 else if (!init->mseg_alloc && init->sys_alloc) {
6957 allctr->crr_set_flgs = CFLG_FORCE_SYS_ALLOC;
6958 allctr->crr_clr_flgs = CFLG_FORCE_MSEG;
6959 }
6960 #endif
6961
6962 if (allctr->main_carrier_size) {
6963 Block_t *blk;
6964
6965 blk = create_carrier(allctr,
6966 allctr->main_carrier_size,
6967 (ERTS_SUPER_ALIGNED_MSEG_ONLY
6968 ? CFLG_FORCE_MSEG : CFLG_FORCE_SYS_ALLOC)
6969 | CFLG_MBC
6970 | CFLG_FORCE_SIZE
6971 | CFLG_NO_CPOOL
6972 | CFLG_MAIN_CARRIER);
6973 if (!blk) {
6974 if (allctr->thread_safe)
6975 erts_mtx_destroy(&allctr->mutex);
6976 erts_exit(ERTS_ABORT_EXIT,
6977 "Failed to create main carrier for %salloc\n",
6978 init->name_prefix);
6979 }
6980
6981 (*allctr->link_free_block)(allctr, blk);
6982
6983 HARD_CHECK_BLK_CARRIER(allctr, blk);
6984
6985 }
6986
6987 if (init->fix) {
6988 int i;
6989 allctr->fix = init->fix;
6990 allctr->fix_shrink_scheduled = 0;
6991 for (i = 0; i < ERTS_ALC_NO_FIXED_SIZES; i++) {
6992 allctr->fix[i].type_size = init->fix_type_size[i];
6993 allctr->fix[i].type = ERTS_ALC_N2T(i + ERTS_ALC_N_MIN_A_FIXED_SIZE);
6994 allctr->fix[i].list_size = 0;
6995 allctr->fix[i].list = NULL;
6996 if (ERTS_ALC_IS_CPOOL_ENABLED(allctr)) {
6997 allctr->fix[i].u.cpool.min_list_size = 0;
6998 allctr->fix[i].u.cpool.shrink_list = 0;
6999 allctr->fix[i].u.cpool.allocated = 0;
7000 allctr->fix[i].u.cpool.used = 0;
7001 }
7002 else {
7003 allctr->fix[i].u.nocpool.max_used = 0;
7004 allctr->fix[i].u.nocpool.limit = 0;
7005 allctr->fix[i].u.nocpool.allocated = 0;
7006 allctr->fix[i].u.nocpool.used = 0;
7007 }
7008 }
7009 }
7010
7011 return 1;
7012
7013 error:
7014
7015 if (allctr->thread_safe)
7016 erts_mtx_destroy(&allctr->mutex);
7017
7018 return 0;
7019
7020 }
7021
7022 /* ------------------------------------------------------------------------- */
7023
7024 void
erts_alcu_stop(Allctr_t * allctr)7025 erts_alcu_stop(Allctr_t *allctr)
7026 {
7027 allctr->stopped = 1;
7028
7029 while (allctr->sbc_list.first)
7030 destroy_carrier(allctr, SBC2BLK(allctr, allctr->sbc_list.first), NULL);
7031 while (allctr->mbc_list.first)
7032 destroy_carrier(allctr, MBC_TO_FIRST_BLK(allctr, allctr->mbc_list.first), NULL);
7033
7034 if (allctr->thread_safe)
7035 erts_mtx_destroy(&allctr->mutex);
7036
7037 }
7038
7039 /* ------------------------------------------------------------------------- */
7040
7041 void
erts_alcu_init(AlcUInit_t * init)7042 erts_alcu_init(AlcUInit_t *init)
7043 {
7044 int i;
7045 ErtsAlcCPoolData_t *sentinel;
7046
7047 for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
7048 sentinel = &firstfit_carrier_pools[i].sentinel;
7049 erts_atomic_init_nob(&sentinel->next, (erts_aint_t) sentinel);
7050 erts_atomic_init_nob(&sentinel->prev, (erts_aint_t) sentinel);
7051 }
7052 sentinel = &firstfit_carrier_pools[ERTS_ALC_A_INVALID].sentinel;
7053 erts_atomic_init_nob(&sentinel->next, (erts_aint_t) sentinel);
7054 erts_atomic_init_nob(&sentinel->prev, (erts_aint_t) sentinel);
7055
7056 ERTS_CT_ASSERT(SBC_BLK_SZ_MASK == MBC_FBLK_SZ_MASK); /* see BLK_SZ */
7057 #if HAVE_ERTS_MSEG
7058 ASSERT(erts_mseg_unit_size() == ERTS_SACRR_UNIT_SZ);
7059 max_mseg_carriers = init->mmc;
7060 sys_alloc_carrier_size = ERTS_SACRR_UNIT_CEILING(init->ycs);
7061 #else /* #if HAVE_ERTS_MSEG */
7062 sys_alloc_carrier_size = ((init->ycs + 4095) / 4096) * 4096;
7063 #endif
7064 allow_sys_alloc_carriers = init->sac;
7065
7066 sys_page_size = erts_sys_get_page_size();
7067
7068 #ifdef DEBUG
7069 carrier_alignment = sizeof(Unit_t);
7070 #endif
7071
7072 #ifdef DEBUG
7073 for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++)
7074 allocator_char_str[i] = NULL;
7075 #endif
7076 allocator_char_str[ERTS_ALC_A_SYSTEM] = "Y";
7077 allocator_char_str[ERTS_ALC_A_TEMPORARY] = "T";
7078 allocator_char_str[ERTS_ALC_A_SHORT_LIVED] = "S";
7079 allocator_char_str[ERTS_ALC_A_STANDARD] = "D";
7080 allocator_char_str[ERTS_ALC_A_LONG_LIVED] = "L";
7081 allocator_char_str[ERTS_ALC_A_EHEAP] = "H";
7082 allocator_char_str[ERTS_ALC_A_ETS] = "E";
7083 allocator_char_str[ERTS_ALC_A_FIXED_SIZE] = "F";
7084 allocator_char_str[ERTS_ALC_A_LITERAL] = "I";
7085 #ifdef ERTS_ALC_A_EXEC
7086 allocator_char_str[ERTS_ALC_A_EXEC] = "X";
7087 #endif
7088 allocator_char_str[ERTS_ALC_A_BINARY] = "B";
7089 allocator_char_str[ERTS_ALC_A_DRIVER] = "R";
7090 allocator_char_str[ERTS_ALC_A_TEST] = "Z";
7091 #ifdef DEBUG
7092 for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++)
7093 ASSERT(allocator_char_str[i]);
7094 #endif
7095
7096 erts_mtx_init(&init_atoms_mtx, "alcu_init_atoms", NIL,
7097 ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
7098
7099 atoms_initialized = 0;
7100 initialized = 1;
7101 }
7102
7103 /* ------------------------------------------------------------------------- */
7104
7105 /* Allocation histograms and carrier information is gathered by walking through
7106 * all carriers associated with each allocator instance. This is done as
7107 * aux_yield_work on the scheduler that owns each instance.
7108 *
7109 * Yielding is implemented by temporarily inserting a "dummy carrier" at the
7110 * last position. It's permanently "busy" so it won't get picked up by someone
7111 * else when in the carrier pool, and we never make the employer aware of it
7112 * through callbacks so we can't accidentally allocate on it.
7113 *
7114 * Plain malloc/free is used to guarantee we won't allocate with the allocator
7115 * we're scanning. */
7116
7117 /* Yield between carriers once this many blocks have been processed. Note that
7118 * a single carrier scan may exceed this figure. */
7119 #ifndef DEBUG
7120 #define BLOCKSCAN_REDUCTIONS (8000)
7121 #else
7122 #define BLOCKSCAN_REDUCTIONS (400)
7123 #endif
7124
7125 /* Abort a single carrier scan after this many blocks to prevent really large
7126 * MBCs from blocking forever. */
7127 #define BLOCKSCAN_BAILOUT_THRESHOLD (16000)
7128
7129 typedef struct alcu_blockscan {
7130 /* A per-scheduler list used when multiple scans have been queued. The
7131 * current scanner will always run until completion/abort before moving on
7132 * to the next. */
7133 struct alcu_blockscan *scanner_queue;
7134
7135 Allctr_t *allocator;
7136 Process *process;
7137
7138 int (*current_op)(struct alcu_blockscan *scanner);
7139 int (*next_op)(struct alcu_blockscan *scanner);
7140 int reductions;
7141
7142 ErtsAlcCPoolData_t *cpool_cursor;
7143 CarrierList_t *current_clist;
7144 Carrier_t *clist_cursor;
7145 Carrier_t dummy_carrier;
7146
7147 /* Called if the process that started this job dies before we're done. */
7148 void (*abort)(void *user_data);
7149
7150 /* Called on each carrier. The callback must return the number of blocks
7151 * scanned to yield properly between carriers.
7152 *
7153 * Note that it's not possible to "yield back" into a carrier. */
7154 int (*scan)(Allctr_t *, void *user_data, Carrier_t *);
7155
7156 /* Called when all carriers have been scanned. The callback may return
7157 * non-zero to yield. */
7158 int (*finish)(void *user_data);
7159
7160 void *user_data;
7161 } blockscan_t;
7162
blockscan_restore_clist_cursor(blockscan_t * state)7163 static Carrier_t *blockscan_restore_clist_cursor(blockscan_t *state)
7164 {
7165 Carrier_t *cursor = state->clist_cursor;
7166
7167 ASSERT(state->clist_cursor == (state->current_clist)->first ||
7168 state->clist_cursor == &state->dummy_carrier);
7169
7170 if (cursor == &state->dummy_carrier) {
7171 cursor = cursor->next;
7172
7173 unlink_carrier(state->current_clist, state->clist_cursor);
7174 }
7175
7176 return cursor;
7177 }
7178
blockscan_save_clist_cursor(blockscan_t * state,Carrier_t * after)7179 static void blockscan_save_clist_cursor(blockscan_t *state, Carrier_t *after)
7180 {
7181 ASSERT(state->clist_cursor == (state->current_clist)->first ||
7182 state->clist_cursor == &state->dummy_carrier);
7183
7184 state->clist_cursor = &state->dummy_carrier;
7185
7186 (state->clist_cursor)->next = after->next;
7187 (state->clist_cursor)->prev = after;
7188
7189 relink_carrier(state->current_clist, state->clist_cursor);
7190 }
7191
blockscan_clist_yielding(blockscan_t * state)7192 static int blockscan_clist_yielding(blockscan_t *state)
7193 {
7194 Carrier_t *cursor = blockscan_restore_clist_cursor(state);
7195
7196 if (ERTS_PROC_IS_EXITING(state->process)) {
7197 return 0;
7198 }
7199
7200 while (cursor) {
7201 /* Skip dummy carriers inserted by another (concurrent) block scan.
7202 * This can happen when scanning thread-safe allocators from multiple
7203 * schedulers. */
7204 if (CARRIER_SZ(cursor) > 0) {
7205 int blocks_scanned = state->scan(state->allocator,
7206 state->user_data,
7207 cursor);
7208
7209 state->reductions -= blocks_scanned;
7210
7211 if (state->reductions <= 0) {
7212 blockscan_save_clist_cursor(state, cursor);
7213 return 1;
7214 }
7215 }
7216
7217 cursor = cursor->next;
7218 }
7219
7220 return 0;
7221 }
7222
blockscan_restore_cpool_cursor(blockscan_t * state)7223 static ErtsAlcCPoolData_t *blockscan_restore_cpool_cursor(blockscan_t *state)
7224 {
7225 ErtsAlcCPoolData_t *cursor;
7226
7227 cursor = cpool_aint2cpd(cpool_read(&(state->cpool_cursor)->next));
7228
7229 if (state->cpool_cursor == &state->dummy_carrier.cpool) {
7230 cpool_delete(state->allocator, state->allocator, &state->dummy_carrier);
7231 }
7232
7233 return cursor;
7234 }
7235
blockscan_save_cpool_cursor(blockscan_t * state,ErtsAlcCPoolData_t * after)7236 static void blockscan_save_cpool_cursor(blockscan_t *state,
7237 ErtsAlcCPoolData_t *after)
7238 {
7239 ErtsAlcCPoolData_t *dummy_carrier, *prev_carrier, *next_carrier;
7240
7241 dummy_carrier = &state->dummy_carrier.cpool;
7242
7243 next_carrier = cpool_aint2cpd(cpool_mod_mark(&after->next));
7244 prev_carrier = cpool_aint2cpd(cpool_mod_mark(&next_carrier->prev));
7245
7246 cpool_init(&dummy_carrier->next, (erts_aint_t)next_carrier);
7247 cpool_init(&dummy_carrier->prev, (erts_aint_t)prev_carrier);
7248
7249 cpool_set_mod_marked(&prev_carrier->next,
7250 (erts_aint_t)dummy_carrier,
7251 (erts_aint_t)next_carrier);
7252 cpool_set_mod_marked(&next_carrier->prev,
7253 (erts_aint_t)dummy_carrier,
7254 (erts_aint_t)prev_carrier);
7255
7256 state->cpool_cursor = dummy_carrier;
7257 }
7258
blockscan_cpool_yielding(blockscan_t * state)7259 static int blockscan_cpool_yielding(blockscan_t *state)
7260 {
7261 ErtsAlcCPoolData_t *sentinel, *cursor;
7262
7263 sentinel = (state->allocator)->cpool.sentinel;
7264 cursor = blockscan_restore_cpool_cursor(state);
7265
7266 if (ERTS_PROC_IS_EXITING(state->process)) {
7267 return 0;
7268 }
7269
7270 while (cursor != sentinel) {
7271 Carrier_t *carrier;
7272 erts_aint_t exp;
7273
7274 /* When a deallocation happens on a pooled carrier it will be routed to
7275 * its owner, so the only way to be sure that it isn't modified while
7276 * scanning is to skip all carriers that aren't ours. The deallocations
7277 * deferred to us will get handled when we're done. */
7278 while (cursor->orig_allctr != state->allocator) {
7279 cursor = cpool_aint2cpd(cpool_read(&cursor->next));
7280
7281 if (cursor == sentinel) {
7282 return 0;
7283 }
7284 }
7285
7286 carrier = ErtsContainerStruct(cursor, Carrier_t, cpool);
7287 exp = erts_atomic_read_rb(&carrier->allctr);
7288
7289 if (exp & ERTS_CRR_ALCTR_FLG_IN_POOL) {
7290 ASSERT(state->allocator == (Allctr_t*)(exp & ~ERTS_CRR_ALCTR_FLG_MASK));
7291 ASSERT(!(exp & ERTS_CRR_ALCTR_FLG_BUSY));
7292
7293 if (erts_atomic_cmpxchg_acqb(&carrier->allctr,
7294 exp | ERTS_CRR_ALCTR_FLG_BUSY,
7295 exp) == exp) {
7296 /* Skip dummy carriers inserted by another (concurrent) block
7297 * scan. This can happen when scanning thread-safe allocators
7298 * from multiple schedulers. */
7299 if (CARRIER_SZ(carrier) > 0) {
7300 int blocks_scanned = state->scan(state->allocator,
7301 state->user_data,
7302 carrier);
7303
7304 state->reductions -= blocks_scanned;
7305
7306 if (state->reductions <= 0) {
7307 blockscan_save_cpool_cursor(state, cursor);
7308 erts_atomic_set_relb(&carrier->allctr, exp);
7309
7310 return 1;
7311 }
7312 }
7313
7314 erts_atomic_set_relb(&carrier->allctr, exp);
7315 }
7316 }
7317
7318 cursor = cpool_aint2cpd(cpool_read(&cursor->next));
7319 }
7320
7321 return 0;
7322 }
7323
7324 /* */
7325
blockscan_finish(blockscan_t * state)7326 static int blockscan_finish(blockscan_t *state)
7327 {
7328 if (ERTS_PROC_IS_EXITING(state->process)) {
7329 state->abort(state->user_data);
7330 return 0;
7331 }
7332
7333 state->current_op = blockscan_finish;
7334
7335 return state->finish(state->user_data);
7336 }
7337
blockscan_lock_helper(blockscan_t * state)7338 static void blockscan_lock_helper(blockscan_t *state) {
7339 if ((state->allocator)->thread_safe) {
7340 /* Locked scans have to be as short as possible. */
7341 state->reductions = 1;
7342
7343 erts_mtx_lock(&(state->allocator)->mutex);
7344 } else {
7345 state->reductions = BLOCKSCAN_REDUCTIONS;
7346 }
7347 }
7348
blockscan_unlock_helper(blockscan_t * state)7349 static void blockscan_unlock_helper(blockscan_t *state) {
7350 if ((state->allocator)->thread_safe) {
7351 erts_mtx_unlock(&(state->allocator)->mutex);
7352 }
7353 }
7354
blockscan_sweep_sbcs(blockscan_t * state)7355 static int blockscan_sweep_sbcs(blockscan_t *state)
7356 {
7357 blockscan_lock_helper(state);
7358
7359 if (state->current_op != blockscan_sweep_sbcs) {
7360 SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_SBC, state->allocator);
7361 state->current_clist = &(state->allocator)->sbc_list;
7362 state->clist_cursor = (state->current_clist)->first;
7363 }
7364
7365 state->current_op = blockscan_sweep_sbcs;
7366 state->next_op = blockscan_finish;
7367
7368 if (blockscan_clist_yielding(state)) {
7369 state->next_op = state->current_op;
7370 }
7371
7372 blockscan_unlock_helper(state);
7373
7374 return 1;
7375 }
7376
blockscan_sweep_mbcs(blockscan_t * state)7377 static int blockscan_sweep_mbcs(blockscan_t *state)
7378 {
7379 blockscan_lock_helper(state);
7380
7381 if (state->current_op != blockscan_sweep_mbcs) {
7382 SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_MBC, state->allocator);
7383 state->current_clist = &(state->allocator)->mbc_list;
7384 state->clist_cursor = (state->current_clist)->first;
7385 }
7386
7387 state->current_op = blockscan_sweep_mbcs;
7388 state->next_op = blockscan_sweep_sbcs;
7389
7390 if (blockscan_clist_yielding(state)) {
7391 state->next_op = state->current_op;
7392 }
7393
7394 blockscan_unlock_helper(state);
7395
7396 return 1;
7397 }
7398
blockscan_sweep_cpool(blockscan_t * state)7399 static int blockscan_sweep_cpool(blockscan_t *state)
7400 {
7401 blockscan_lock_helper(state);
7402
7403 if (state->current_op != blockscan_sweep_cpool) {
7404 SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_MBC, state->allocator);
7405 state->cpool_cursor = (state->allocator)->cpool.sentinel;
7406 }
7407
7408 state->current_op = blockscan_sweep_cpool;
7409 state->next_op = blockscan_sweep_mbcs;
7410
7411 if (blockscan_cpool_yielding(state)) {
7412 state->next_op = state->current_op;
7413 }
7414
7415 blockscan_unlock_helper(state);
7416
7417 return 1;
7418 }
7419
blockscan_get_specific_allocator(int allocator_num,int sched_id,Allctr_t ** out)7420 static int blockscan_get_specific_allocator(int allocator_num,
7421 int sched_id,
7422 Allctr_t **out)
7423 {
7424 ErtsAllocatorInfo_t *ai;
7425 Allctr_t *allocator;
7426
7427 ASSERT(allocator_num >= ERTS_ALC_A_MIN &&
7428 allocator_num <= ERTS_ALC_A_MAX);
7429 ASSERT(sched_id >= 0 && sched_id <= erts_no_schedulers);
7430
7431 ai = &erts_allctrs_info[allocator_num];
7432
7433 if (!ai->enabled || !ai->alloc_util) {
7434 return 0;
7435 }
7436
7437 if (!ai->thr_spec) {
7438 if (sched_id != 0) {
7439 /* Only thread-specific allocators can be scanned on a specific
7440 * scheduler. */
7441 return 0;
7442 }
7443
7444 allocator = (Allctr_t*)ai->extra;
7445 ASSERT(allocator->thread_safe);
7446 } else {
7447 ErtsAllocatorThrSpec_t *tspec = (ErtsAllocatorThrSpec_t*)ai->extra;
7448
7449 ASSERT(sched_id < tspec->size);
7450
7451 allocator = tspec->allctr[sched_id];
7452 }
7453
7454 *out = allocator;
7455
7456 return 1;
7457 }
7458
blockscan_sched_trampoline(void * arg)7459 static void blockscan_sched_trampoline(void *arg)
7460 {
7461 ErtsAlcuBlockscanYieldData *yield;
7462 ErtsSchedulerData *esdp;
7463 blockscan_t *scanner;
7464
7465 esdp = erts_get_scheduler_data();
7466 scanner = (blockscan_t*)arg;
7467
7468 yield = ERTS_SCHED_AUX_YIELD_DATA(esdp, alcu_blockscan);
7469
7470 ASSERT((yield->last == NULL) == (yield->current == NULL));
7471
7472 if (yield->last != NULL) {
7473 blockscan_t *prev_scanner = yield->last;
7474
7475 ASSERT(prev_scanner->scanner_queue == NULL);
7476
7477 prev_scanner->scanner_queue = scanner;
7478 } else {
7479 yield->current = scanner;
7480 }
7481
7482 scanner->scanner_queue = NULL;
7483 yield->last = scanner;
7484
7485 erts_notify_new_aux_yield_work(esdp);
7486 }
7487
blockscan_dispatch(blockscan_t * scanner,Process * owner,Allctr_t * allocator,int sched_id)7488 static void blockscan_dispatch(blockscan_t *scanner, Process *owner,
7489 Allctr_t *allocator, int sched_id)
7490 {
7491 ASSERT(erts_get_scheduler_id() != 0);
7492
7493 if (sched_id == 0) {
7494 /* Global instances are always handled on the current scheduler. */
7495 sched_id = ERTS_ALC_GET_THR_IX();
7496 ASSERT(allocator->thread_safe);
7497 }
7498
7499 scanner->allocator = allocator;
7500 scanner->process = owner;
7501
7502 erts_proc_inc_refc(scanner->process);
7503
7504 cpool_init_carrier_data(scanner->allocator, &scanner->dummy_carrier);
7505 erts_atomic_init_nob(&(scanner->dummy_carrier).allctr,
7506 (erts_aint_t)allocator | ERTS_CRR_ALCTR_FLG_BUSY);
7507
7508 if (ERTS_ALC_IS_CPOOL_ENABLED(scanner->allocator)) {
7509 scanner->next_op = blockscan_sweep_cpool;
7510 } else {
7511 scanner->next_op = blockscan_sweep_mbcs;
7512 }
7513
7514 /* Aux yield jobs can only be set up while running on the scheduler that
7515 * services them, so we move there before continuing.
7516 *
7517 * We can't drive the scan itself through this since the scheduler will
7518 * always finish *all* misc aux work in one go which makes it impossible to
7519 * yield. */
7520 erts_schedule_misc_aux_work(sched_id, blockscan_sched_trampoline, scanner);
7521 }
7522
erts_handle_yielded_alcu_blockscan(ErtsSchedulerData * esdp,ErtsAlcuBlockscanYieldData * yield)7523 int erts_handle_yielded_alcu_blockscan(ErtsSchedulerData *esdp,
7524 ErtsAlcuBlockscanYieldData *yield)
7525 {
7526 blockscan_t *scanner = yield->current;
7527
7528 (void)esdp;
7529
7530 ASSERT((yield->last == NULL) == (yield->current == NULL));
7531
7532 if (scanner) {
7533 if (scanner->next_op(scanner)) {
7534 return 1;
7535 }
7536
7537 ASSERT(ERTS_PROC_IS_EXITING(scanner->process) ||
7538 scanner->current_op == blockscan_finish);
7539
7540 yield->current = scanner->scanner_queue;
7541
7542 if (yield->current == NULL) {
7543 ASSERT(scanner == yield->last);
7544 yield->last = NULL;
7545 }
7546
7547 erts_proc_dec_refc(scanner->process);
7548
7549 /* Plain free is intentional. */
7550 free(scanner);
7551
7552 return yield->current != NULL;
7553 }
7554
7555 return 0;
7556 }
7557
erts_alcu_sched_spec_data_init(ErtsSchedulerData * esdp)7558 void erts_alcu_sched_spec_data_init(ErtsSchedulerData *esdp)
7559 {
7560 ErtsAlcuBlockscanYieldData *yield;
7561
7562 yield = ERTS_SCHED_AUX_YIELD_DATA(esdp, alcu_blockscan);
7563
7564 yield->current = NULL;
7565 yield->last = NULL;
7566 }
7567
7568 /* ------------------------------------------------------------------------- */
7569
u64_log2(Uint64 v)7570 static ERTS_INLINE int u64_log2(Uint64 v)
7571 {
7572 static const int log2_tab64[64] = {
7573 63, 0, 58, 1, 59, 47, 53, 2,
7574 60, 39, 48, 27, 54, 33, 42, 3,
7575 61, 51, 37, 40, 49, 18, 28, 20,
7576 55, 30, 34, 11, 43, 14, 22, 4,
7577 62, 57, 46, 52, 38, 26, 32, 41,
7578 50, 36, 17, 19, 29, 10, 13, 21,
7579 56, 45, 25, 31, 35, 16, 9, 12,
7580 44, 24, 15, 8, 23, 7, 6, 5};
7581
7582 v |= v >> 1;
7583 v |= v >> 2;
7584 v |= v >> 4;
7585 v |= v >> 8;
7586 v |= v >> 16;
7587 v |= v >> 32;
7588
7589 return log2_tab64[((Uint64)((v - (v >> 1))*0x07EDD5E59A4E28C2)) >> 58];
7590 }
7591
7592 /* ------------------------------------------------------------------------- */
7593
7594 typedef struct hist_tree__ {
7595 struct hist_tree__ *parent;
7596 struct hist_tree__ *left;
7597 struct hist_tree__ *right;
7598
7599 int is_red;
7600
7601 alcu_atag_t tag;
7602 UWord histogram[1];
7603 } hist_tree_t;
7604
7605 #define ERTS_RBT_PREFIX hist_tree
7606 #define ERTS_RBT_T hist_tree_t
7607 #define ERTS_RBT_KEY_T UWord
7608 #define ERTS_RBT_FLAGS_T int
7609 #define ERTS_RBT_INIT_EMPTY_TNODE(T) ((void)0)
7610 #define ERTS_RBT_IS_RED(T) ((T)->is_red)
7611 #define ERTS_RBT_SET_RED(T) ((T)->is_red = 1)
7612 #define ERTS_RBT_IS_BLACK(T) (!ERTS_RBT_IS_RED(T))
7613 #define ERTS_RBT_SET_BLACK(T) ((T)->is_red = 0)
7614 #define ERTS_RBT_GET_FLAGS(T) ((T)->is_red)
7615 #define ERTS_RBT_SET_FLAGS(T, F) ((T)->is_red = F)
7616 #define ERTS_RBT_GET_PARENT(T) ((T)->parent)
7617 #define ERTS_RBT_SET_PARENT(T, P) ((T)->parent = P)
7618 #define ERTS_RBT_GET_RIGHT(T) ((T)->right)
7619 #define ERTS_RBT_SET_RIGHT(T, R) ((T)->right = (R))
7620 #define ERTS_RBT_GET_LEFT(T) ((T)->left)
7621 #define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L))
7622 #define ERTS_RBT_GET_KEY(T) ((T)->tag)
7623 #define ERTS_RBT_IS_LT(KX, KY) (KX < KY)
7624 #define ERTS_RBT_IS_EQ(KX, KY) (KX == KY)
7625 #define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING
7626 #define ERTS_RBT_WANT_FOREACH_DESTROY
7627 #define ERTS_RBT_WANT_INSERT
7628 #define ERTS_RBT_WANT_LOOKUP
7629 #define ERTS_RBT_UNDEF
7630
7631 #include "erl_rbtree.h"
7632
7633 typedef struct {
7634 blockscan_t common;
7635
7636 ErtsIRefStorage iref;
7637 Process *process;
7638
7639 hist_tree_rbt_yield_state_t hist_tree_yield;
7640 hist_tree_t *hist_tree;
7641 UWord hist_count;
7642
7643 UWord hist_slot_start;
7644 int hist_slot_count;
7645
7646 UWord unscanned_size;
7647
7648 ErtsHeapFactory msg_factory;
7649 int building_result;
7650 Eterm result_list;
7651 } gather_ahist_t;
7652
gather_ahist_update(gather_ahist_t * state,UWord tag,UWord size)7653 static void gather_ahist_update(gather_ahist_t *state, UWord tag, UWord size)
7654 {
7655 hist_tree_t *hist_node;
7656 UWord size_interval;
7657 int hist_slot;
7658
7659 hist_node = hist_tree_rbt_lookup(state->hist_tree, tag);
7660
7661 if (hist_node == NULL) {
7662 /* Plain calloc is intentional. */
7663 hist_node = (hist_tree_t*)calloc(1, sizeof(hist_tree_t) +
7664 (state->hist_slot_count - 1) *
7665 sizeof(hist_node->histogram[0]));
7666 hist_node->tag = tag;
7667
7668 hist_tree_rbt_insert(&state->hist_tree, hist_node);
7669 state->hist_count++;
7670 }
7671
7672 size_interval = (size / state->hist_slot_start);
7673 size_interval = u64_log2(size_interval + 1);
7674
7675 hist_slot = MIN(size_interval, state->hist_slot_count - 1);
7676
7677 hist_node->histogram[hist_slot]++;
7678 }
7679
gather_ahist_scan(Allctr_t * allocator,void * user_data,Carrier_t * carrier)7680 static int gather_ahist_scan(Allctr_t *allocator,
7681 void *user_data,
7682 Carrier_t *carrier)
7683 {
7684 gather_ahist_t *state;
7685 int blocks_scanned;
7686 Block_t *block;
7687
7688 state = (gather_ahist_t*)user_data;
7689 blocks_scanned = 1;
7690
7691 if (IS_SB_CARRIER(carrier)) {
7692 alcu_atag_t tag;
7693
7694 block = SBC2BLK(allocator, carrier);
7695
7696 if (BLK_HAS_ATAG(block)) {
7697 tag = GET_BLK_ATAG(block);
7698
7699 ASSERT(DBG_IS_VALID_ATAG(tag));
7700
7701 gather_ahist_update(state, tag, SBC_BLK_SZ(block));
7702 }
7703 } else {
7704 UWord scanned_bytes = MBC_HEADER_SIZE(allocator);
7705
7706 ASSERT(IS_MB_CARRIER(carrier));
7707
7708 block = MBC_TO_FIRST_BLK(allocator, carrier);
7709
7710 while (1) {
7711 UWord block_size = MBC_BLK_SZ(block);
7712
7713 if (IS_ALLOCED_BLK(block) && BLK_HAS_ATAG(block)) {
7714 alcu_atag_t tag = GET_BLK_ATAG(block);
7715
7716 ASSERT(DBG_IS_VALID_ATAG(tag));
7717
7718 gather_ahist_update(state, tag, block_size);
7719 }
7720
7721 scanned_bytes += block_size;
7722
7723 if (blocks_scanned >= BLOCKSCAN_BAILOUT_THRESHOLD) {
7724 state->unscanned_size += CARRIER_SZ(carrier) - scanned_bytes;
7725 break;
7726 } else if (IS_LAST_BLK(block)) {
7727 break;
7728 }
7729
7730 block = NXT_BLK(block);
7731 blocks_scanned++;
7732 }
7733 }
7734
7735 return blocks_scanned;
7736 }
7737
gather_ahist_append_result(hist_tree_t * node,void * arg,Sint reds)7738 static int gather_ahist_append_result(hist_tree_t *node, void *arg, Sint reds)
7739 {
7740 gather_ahist_t *state = (gather_ahist_t*)arg;
7741
7742 Eterm histogram_tuple, tag_tuple;
7743
7744 Eterm *hp;
7745 int ix;
7746
7747 ASSERT(state->building_result);
7748
7749 hp = erts_produce_heap(&state->msg_factory, 7 + state->hist_slot_count, 0);
7750
7751 hp[0] = make_arityval(state->hist_slot_count);
7752
7753 for (ix = 0; ix < state->hist_slot_count; ix++) {
7754 hp[1 + ix] = make_small(node->histogram[ix]);
7755 }
7756
7757 histogram_tuple = make_tuple(hp);
7758 hp += 1 + state->hist_slot_count;
7759
7760 hp[0] = make_arityval(3);
7761 hp[1] = ATAG_ID(node->tag);
7762 hp[2] = alloc_type_atoms[ATAG_TYPE(node->tag)];
7763 hp[3] = histogram_tuple;
7764
7765 tag_tuple = make_tuple(hp);
7766 hp += 4;
7767
7768 state->result_list = CONS(hp, tag_tuple, state->result_list);
7769
7770 /* Plain free is intentional. */
7771 free(node);
7772 return 1;
7773 }
7774
gather_ahist_send(gather_ahist_t * state)7775 static void gather_ahist_send(gather_ahist_t *state)
7776 {
7777 Eterm result_tuple, unscanned_size, task_ref;
7778
7779 Uint term_size;
7780 Eterm *hp;
7781
7782 ASSERT((state->result_list == NIL) ^ (state->hist_count > 0));
7783 ASSERT(state->building_result);
7784
7785 term_size = 4 + erts_iref_storage_heap_size(&state->iref);
7786 term_size += IS_USMALL(0, state->unscanned_size) ? 0 : BIG_UINT_HEAP_SIZE;
7787
7788 hp = erts_produce_heap(&state->msg_factory, term_size, 0);
7789
7790 task_ref = erts_iref_storage_make_ref(&state->iref, &hp,
7791 &(state->msg_factory.message)->hfrag.off_heap, 0);
7792
7793 unscanned_size = bld_unstable_uint(&hp, NULL, state->unscanned_size);
7794
7795 hp[0] = make_arityval(3);
7796 hp[1] = task_ref;
7797 hp[2] = unscanned_size;
7798 hp[3] = state->result_list;
7799
7800 result_tuple = make_tuple(hp);
7801
7802 erts_factory_trim_and_close(&state->msg_factory, &result_tuple, 1);
7803
7804 erts_queue_message(state->process, 0, state->msg_factory.message,
7805 result_tuple, am_system);
7806 }
7807
gather_ahist_finish(void * arg)7808 static int gather_ahist_finish(void *arg)
7809 {
7810 gather_ahist_t *state = (gather_ahist_t*)arg;
7811
7812 if (!state->building_result) {
7813 ErtsMessage *message;
7814 Uint minimum_size;
7815 Eterm *hp;
7816
7817 /* {Ref, unscanned size, [{Tag, {Histogram}} | Rest]} */
7818 minimum_size = 4 + erts_iref_storage_heap_size(&state->iref) +
7819 state->hist_count * (7 + state->hist_slot_count);
7820
7821 message = erts_alloc_message(minimum_size, &hp);
7822 erts_factory_selfcontained_message_init(&state->msg_factory,
7823 message, hp);
7824
7825 ERTS_RBT_YIELD_STAT_INIT(&state->hist_tree_yield);
7826
7827 state->result_list = NIL;
7828 state->building_result = 1;
7829 }
7830
7831 if (!hist_tree_rbt_foreach_destroy_yielding(&state->hist_tree,
7832 &gather_ahist_append_result,
7833 state,
7834 &state->hist_tree_yield,
7835 BLOCKSCAN_REDUCTIONS)) {
7836 return 1;
7837 }
7838
7839 gather_ahist_send(state);
7840
7841 return 0;
7842 }
7843
gather_ahist_destroy_result(hist_tree_t * node,void * arg,Sint reds)7844 static int gather_ahist_destroy_result(hist_tree_t *node, void *arg, Sint reds)
7845 {
7846 (void)arg;
7847 free(node);
7848 return 1;
7849 }
7850
gather_ahist_abort(void * arg)7851 static void gather_ahist_abort(void *arg)
7852 {
7853 gather_ahist_t *state = (gather_ahist_t*)arg;
7854
7855 if (state->building_result) {
7856 erts_factory_undo(&state->msg_factory);
7857 }
7858
7859 hist_tree_rbt_foreach_destroy(&state->hist_tree,
7860 &gather_ahist_destroy_result,
7861 NULL);
7862 }
7863
erts_alcu_gather_alloc_histograms(Process * p,int allocator_num,int sched_id,int hist_width,UWord hist_start,Eterm ref)7864 int erts_alcu_gather_alloc_histograms(Process *p, int allocator_num,
7865 int sched_id, int hist_width,
7866 UWord hist_start, Eterm ref)
7867 {
7868 gather_ahist_t *gather_state;
7869 blockscan_t *scanner;
7870 Allctr_t *allocator;
7871
7872 ASSERT(is_internal_ref(ref));
7873
7874 if (!blockscan_get_specific_allocator(allocator_num,
7875 sched_id,
7876 &allocator)) {
7877 return 0;
7878 }
7879
7880 ensure_atoms_initialized(allocator);
7881
7882 /* Plain calloc is intentional. */
7883 gather_state = (gather_ahist_t*)calloc(1, sizeof(gather_ahist_t));
7884 scanner = &gather_state->common;
7885
7886 scanner->abort = gather_ahist_abort;
7887 scanner->scan = gather_ahist_scan;
7888 scanner->finish = gather_ahist_finish;
7889 scanner->user_data = gather_state;
7890
7891 erts_iref_storage_save(&gather_state->iref, ref);
7892 gather_state->hist_slot_start = hist_start;
7893 gather_state->hist_slot_count = hist_width;
7894 gather_state->process = p;
7895
7896 blockscan_dispatch(scanner, p, allocator, sched_id);
7897
7898 return 1;
7899 }
7900
7901 /* ------------------------------------------------------------------------- */
7902
7903 typedef struct chist_node__ {
7904 struct chist_node__ *next;
7905
7906 UWord carrier_size;
7907 UWord unscanned_size;
7908
7909 int flags;
7910
7911 /* A mirror of the block counters in the carrier's ErtsAlcCPoolData_t. */
7912 UWord alloc_count[ERTS_ALC_A_COUNT];
7913 UWord alloc_size[ERTS_ALC_A_COUNT];
7914
7915 /* BLOCKSCAN_BAILOUT_THRESHOLD guarantees we won't overflow. */
7916 int free_histogram[1];
7917 } chist_node_t;
7918
7919 typedef struct {
7920 blockscan_t common;
7921
7922 ErtsIRefStorage iref;
7923 Process *process;
7924
7925 int allocator_number;
7926
7927 chist_node_t *info_list;
7928 UWord info_count;
7929
7930 UWord hist_slot_start;
7931 int hist_slot_count;
7932
7933 ErtsHeapFactory msg_factory;
7934 int building_result;
7935 Eterm result_list;
7936 } gather_cinfo_t;
7937
gather_cinfo_scan(Allctr_t * allocator,void * user_data,Carrier_t * carrier)7938 static int gather_cinfo_scan(Allctr_t *allocator,
7939 void *user_data,
7940 Carrier_t *carrier)
7941 {
7942 gather_cinfo_t *state;
7943 chist_node_t *node;
7944 int blocks_scanned;
7945 Block_t *block;
7946
7947 state = (gather_cinfo_t*)user_data;
7948 node = calloc(1, sizeof(chist_node_t) +
7949 (state->hist_slot_count - 1) *
7950 sizeof(node->free_histogram[0]));
7951 blocks_scanned = 1;
7952
7953 /* ERTS_CRR_ALCTR_FLG_BUSY is ignored since we've set it ourselves and it
7954 * would be misleading to include it. */
7955 node->flags = erts_atomic_read_rb(&carrier->allctr) &
7956 (ERTS_CRR_ALCTR_FLG_MASK & ~ERTS_CRR_ALCTR_FLG_BUSY);
7957 node->carrier_size = CARRIER_SZ(carrier);
7958
7959 if (IS_SB_CARRIER(carrier)) {
7960 int ix = allocator->alloc_no - ERTS_ALC_A_MIN;
7961
7962 node->alloc_count[ix] = 1;
7963 node->alloc_size[ix] = node->carrier_size;
7964 } else {
7965 UWord scanned_bytes = MBC_HEADER_SIZE(allocator);
7966
7967 sys_memcpy(&node->alloc_count[0],
7968 &carrier->cpool.blocks[0],
7969 sizeof(UWord) * ERTS_ALC_A_COUNT);
7970 sys_memcpy(&node->alloc_size[0],
7971 &carrier->cpool.blocks_size[0],
7972 sizeof(UWord) * ERTS_ALC_A_COUNT);
7973
7974 block = MBC_TO_FIRST_BLK(allocator, carrier);
7975
7976 while (1) {
7977 UWord block_size = MBC_BLK_SZ(block);
7978
7979 scanned_bytes += block_size;
7980
7981 if (IS_FREE_BLK(block)) {
7982 UWord size_interval;
7983 int hist_slot;
7984
7985 size_interval = (block_size / state->hist_slot_start);
7986 size_interval = u64_log2(size_interval + 1);
7987
7988 hist_slot = MIN(size_interval, state->hist_slot_count - 1);
7989
7990 node->free_histogram[hist_slot]++;
7991 }
7992
7993 if (blocks_scanned >= BLOCKSCAN_BAILOUT_THRESHOLD) {
7994 node->unscanned_size += CARRIER_SZ(carrier) - scanned_bytes;
7995 break;
7996 } else if (IS_LAST_BLK(block)) {
7997 break;
7998 }
7999
8000 block = NXT_BLK(block);
8001 blocks_scanned++;
8002 }
8003 }
8004
8005 node->next = state->info_list;
8006 state->info_list = node;
8007 state->info_count++;
8008
8009 return blocks_scanned;
8010 }
8011
gather_cinfo_append_result(gather_cinfo_t * state,chist_node_t * info)8012 static void gather_cinfo_append_result(gather_cinfo_t *state,
8013 chist_node_t *info)
8014 {
8015 Eterm carrier_tuple, block_list, histogram_tuple;
8016 Eterm carrier_size, unscanned_size;
8017
8018 Uint term_size;
8019 Eterm *hp;
8020 int ix;
8021
8022 ASSERT(state->building_result);
8023 term_size = 0;
8024
8025 /* Free block histogram. */
8026 term_size += 1 + state->hist_slot_count;
8027
8028 /* Per-type block list. */
8029 for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
8030 UWord count = info->alloc_count[ix - ERTS_ALC_A_MIN];
8031 UWord size = info->alloc_size[ix - ERTS_ALC_A_MIN];
8032
8033 if (count > 0) {
8034 /* We have at least one block of this type, so we'll need a cons
8035 * cell and a 3-tuple; {Type, Count, Size}. */
8036 term_size += 2 + 4;
8037 term_size += IS_USMALL(0, count) ? 0 : BIG_UINT_HEAP_SIZE;
8038 term_size += IS_USMALL(0, size) ? 0 : BIG_UINT_HEAP_SIZE;
8039 }
8040 }
8041
8042 /* Carrier tuple and its fields. */
8043 term_size += 7;
8044 term_size += IS_USMALL(0, info->carrier_size) ? 0 : BIG_UINT_HEAP_SIZE;
8045 term_size += IS_USMALL(0, info->unscanned_size) ? 0 : BIG_UINT_HEAP_SIZE;
8046
8047 /* ... and a finally a cons cell to keep the result in. */
8048 term_size += 2;
8049
8050 /* * * */
8051 hp = erts_produce_heap(&state->msg_factory, term_size, 0);
8052
8053 block_list = NIL;
8054 for (ix = ERTS_ALC_A_MIN; ix <= ERTS_ALC_A_MAX; ix++) {
8055 UWord count = info->alloc_count[ix - ERTS_ALC_A_MIN];
8056 UWord size = info->alloc_size[ix - ERTS_ALC_A_MIN];
8057
8058 if (count > 0) {
8059 Eterm block_count, block_size;
8060 Eterm alloc_tuple;
8061
8062 block_count = bld_unstable_uint(&hp, NULL, count);
8063 block_size = bld_unstable_uint(&hp, NULL, size);
8064
8065 hp[0] = make_arityval(3);
8066 hp[1] = alloc_num_atoms[ix];
8067 hp[2] = block_count;
8068 hp[3] = block_size;
8069
8070 alloc_tuple = make_tuple(hp);
8071 hp += 4;
8072
8073 block_list = CONS(hp, alloc_tuple, block_list);
8074 hp += 2;
8075 }
8076 }
8077
8078 hp[0] = make_arityval(state->hist_slot_count);
8079 for (ix = 0; ix < state->hist_slot_count; ix++) {
8080 hp[1 + ix] = make_small(info->free_histogram[ix]);
8081 }
8082 histogram_tuple = make_tuple(hp);
8083 hp += 1 + state->hist_slot_count;
8084
8085 carrier_size = bld_unstable_uint(&hp, NULL, info->carrier_size);
8086 unscanned_size = bld_unstable_uint(&hp, NULL, info->unscanned_size);
8087
8088 hp[0] = make_arityval(6);
8089 hp[1] = alloc_num_atoms[state->allocator_number];
8090 hp[2] = (info->flags & ERTS_CRR_ALCTR_FLG_IN_POOL) ? am_true : am_false;
8091 hp[3] = carrier_size;
8092 hp[4] = unscanned_size;
8093 hp[5] = block_list;
8094 hp[6] = histogram_tuple;
8095
8096 carrier_tuple = make_tuple(hp);
8097 hp += 7;
8098
8099 state->result_list = CONS(hp, carrier_tuple, state->result_list);
8100
8101 free(info);
8102 }
8103
gather_cinfo_send(gather_cinfo_t * state)8104 static void gather_cinfo_send(gather_cinfo_t *state)
8105 {
8106 Eterm result_tuple, task_ref;
8107
8108 int term_size;
8109 Eterm *hp;
8110
8111 ASSERT((state->result_list == NIL) ^ (state->info_count > 0));
8112 ASSERT(state->building_result);
8113
8114 term_size = 3 + erts_iref_storage_heap_size(&state->iref);
8115 hp = erts_produce_heap(&state->msg_factory, term_size, 0);
8116
8117 task_ref = erts_iref_storage_make_ref(&state->iref, &hp,
8118 &(state->msg_factory.message)->hfrag.off_heap, 0);
8119
8120 hp[0] = make_arityval(2);
8121 hp[1] = task_ref;
8122 hp[2] = state->result_list;
8123
8124 result_tuple = make_tuple(hp);
8125
8126 erts_factory_trim_and_close(&state->msg_factory, &result_tuple, 1);
8127
8128 erts_queue_message(state->process, 0, state->msg_factory.message,
8129 result_tuple, am_system);
8130 }
8131
gather_cinfo_finish(void * arg)8132 static int gather_cinfo_finish(void *arg)
8133 {
8134 gather_cinfo_t *state = (gather_cinfo_t*)arg;
8135 int reductions = BLOCKSCAN_REDUCTIONS;
8136
8137 if (!state->building_result) {
8138 ErtsMessage *message;
8139 Uint minimum_size;
8140 Eterm *hp;
8141
8142 /* {Ref, [{Carrier size, unscanned size, allocated size,
8143 * allocated block count, {Free block histogram}} | Rest]} */
8144 minimum_size = 3 + erts_iref_storage_heap_size(&state->iref) +
8145 state->info_count * (11 + state->hist_slot_count);
8146
8147 message = erts_alloc_message(minimum_size, &hp);
8148 erts_factory_selfcontained_message_init(&state->msg_factory,
8149 message, hp);
8150
8151 state->result_list = NIL;
8152 state->building_result = 1;
8153 }
8154
8155 while (state->info_list) {
8156 chist_node_t *current = state->info_list;
8157 state->info_list = current->next;
8158
8159 gather_cinfo_append_result(state, current);
8160
8161 if (reductions-- <= 0) {
8162 return 1;
8163 }
8164 }
8165
8166 gather_cinfo_send(state);
8167
8168 return 0;
8169 }
8170
gather_cinfo_abort(void * arg)8171 static void gather_cinfo_abort(void *arg)
8172 {
8173 gather_cinfo_t *state = (gather_cinfo_t*)arg;
8174
8175 if (state->building_result) {
8176 erts_factory_undo(&state->msg_factory);
8177 }
8178
8179 while (state->info_list) {
8180 chist_node_t *current = state->info_list;
8181 state->info_list = current->next;
8182
8183 free(current);
8184 }
8185 }
8186
erts_alcu_gather_carrier_info(struct process * p,int allocator_num,int sched_id,int hist_width,UWord hist_start,Eterm ref)8187 int erts_alcu_gather_carrier_info(struct process *p, int allocator_num,
8188 int sched_id, int hist_width,
8189 UWord hist_start, Eterm ref)
8190 {
8191 gather_cinfo_t *gather_state;
8192 blockscan_t *scanner;
8193 Allctr_t *allocator;
8194
8195 ASSERT(is_internal_ref(ref));
8196
8197 if (!blockscan_get_specific_allocator(allocator_num,
8198 sched_id,
8199 &allocator)) {
8200 return 0;
8201 }
8202
8203 ensure_atoms_initialized(allocator);
8204
8205 /* Plain calloc is intentional. */
8206 gather_state = (gather_cinfo_t*)calloc(1, sizeof(gather_cinfo_t));
8207 scanner = &gather_state->common;
8208
8209 scanner->abort = gather_cinfo_abort;
8210 scanner->scan = gather_cinfo_scan;
8211 scanner->finish = gather_cinfo_finish;
8212 scanner->user_data = gather_state;
8213
8214 gather_state->allocator_number = allocator_num;
8215 erts_iref_storage_save(&gather_state->iref, ref);
8216 gather_state->hist_slot_start = hist_start * 2;
8217 gather_state->hist_slot_count = hist_width;
8218 gather_state->process = p;
8219
8220 blockscan_dispatch(scanner, p, allocator, sched_id);
8221
8222 return 1;
8223 }
8224
8225
8226 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
8227 * NOTE: erts_alcu_test() is only supposed to be used for testing. *
8228 * *
8229 * Keep alloc_SUITE_data/allocator_test.h updated if changes are made *
8230 * to erts_alcu_test() *
8231 \* */
8232
8233 UWord
erts_alcu_test(UWord op,UWord a1,UWord a2)8234 erts_alcu_test(UWord op, UWord a1, UWord a2)
8235 {
8236 switch (op) {
8237 case 0x000: return (UWord) BLK_SZ((Block_t *) a1);
8238 case 0x001: return (UWord) BLK_UMEM_SZ((Block_t *) a1);
8239 case 0x002: return (UWord) IS_PREV_BLK_FREE((Block_t *) a1);
8240 case 0x003: return (UWord) IS_FREE_BLK((Block_t *) a1);
8241 case 0x004: return (UWord) IS_LAST_BLK((Block_t *) a1);
8242 case 0x005: return (UWord) UMEM2BLK((void *) a1);
8243 case 0x006: return (UWord) BLK2UMEM((Block_t *) a1);
8244 case 0x007: return (UWord) IS_SB_CARRIER((Carrier_t *) a1);
8245 case 0x008: return (UWord) IS_SBC_BLK((Block_t *) a1);
8246 case 0x009: return (UWord) IS_MB_CARRIER((Carrier_t *) a1);
8247 case 0x00a: return (UWord) IS_MSEG_CARRIER((Carrier_t *) a1);
8248 case 0x00b: return (UWord) CARRIER_SZ((Carrier_t *) a1);
8249 case 0x00c: return (UWord) SBC2BLK((Allctr_t *) a1,
8250 (Carrier_t *) a2);
8251 case 0x00d: return (UWord) BLK_TO_SBC((Block_t *) a2);
8252 case 0x00e: return (UWord) MBC_TO_FIRST_BLK((Allctr_t *) a1,
8253 (Carrier_t *) a2);
8254 case 0x00f: return (UWord) FIRST_BLK_TO_MBC((Allctr_t *) a1,
8255 (Block_t *) a2);
8256 case 0x010: return (UWord) ((Allctr_t *) a1)->mbc_list.first;
8257 case 0x011: return (UWord) ((Allctr_t *) a1)->mbc_list.last;
8258 case 0x012: return (UWord) ((Allctr_t *) a1)->sbc_list.first;
8259 case 0x013: return (UWord) ((Allctr_t *) a1)->sbc_list.last;
8260 case 0x014: return (UWord) ((Carrier_t *) a1)->next;
8261 case 0x015: return (UWord) ((Carrier_t *) a1)->prev;
8262 case 0x016: return (UWord) ABLK_HDR_SZ;
8263 case 0x017: return (UWord) ((Allctr_t *) a1)->min_block_size;
8264 case 0x018: return (UWord) NXT_BLK((Block_t *) a1);
8265 case 0x019: return (UWord) PREV_BLK((Block_t *) a1);
8266 case 0x01a: return (UWord) IS_MBC_FIRST_BLK((Allctr_t*)a1, (Block_t *) a2);
8267 case 0x01b: return (UWord) sizeof(Unit_t);
8268 case 0x01c: return (UWord) BLK_TO_MBC((Block_t*) a1);
8269 case 0x01d: ((Allctr_t*) a1)->add_mbc((Allctr_t*)a1, (Carrier_t*)a2); break;
8270 case 0x01e: ((Allctr_t*) a1)->remove_mbc((Allctr_t*)a1, (Carrier_t*)a2); break;
8271 case 0x01f: return (UWord) sizeof(ErtsAlcCrrPool_t);
8272 case 0x020:
8273 SET_CARRIER_HDR((Carrier_t *) a2, 0, SCH_SYS_ALLOC|SCH_MBC, (Allctr_t *) a1);
8274 cpool_init_carrier_data((Allctr_t *) a1, (Carrier_t *) a2);
8275 return (UWord) a2;
8276 case 0x021:
8277 cpool_insert((Allctr_t *) a1, (Carrier_t *) a2);
8278 return (UWord) a2;
8279 case 0x022:
8280 cpool_delete((Allctr_t *) a1, (Allctr_t *) a1, (Carrier_t *) a2);
8281 return (UWord) a2;
8282 case 0x023: return (UWord) cpool_is_empty((Allctr_t *) a1);
8283 case 0x024: return (UWord) cpool_dbg_is_in_pool((Allctr_t *) a1, (Carrier_t *) a2);
8284 case 0x025: /* UMEM2BLK_TEST*/
8285 #ifdef DEBUG
8286 # ifdef HARD_DEBUG
8287 return (UWord)UMEM2BLK(a1-3*sizeof(UWord));
8288 # else
8289 return (UWord)UMEM2BLK(a1-2*sizeof(UWord));
8290 # endif
8291 #else
8292 return (UWord)UMEM2BLK(a1);
8293 #endif
8294
8295 default: ASSERT(0); return ~((UWord) 0);
8296 }
8297 return 0;
8298 }
8299
8300 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
8301 * Debug functions *
8302 \* */
8303
8304 void
erts_alcu_assert_failed(char * expr,char * file,int line,char * func)8305 erts_alcu_assert_failed(char* expr, char* file, int line, char *func)
8306 {
8307 fflush(stdout);
8308 fprintf(stderr, "%s:%d:%s(): Assertion failed: %s\n",
8309 file, line, func, expr);
8310 fflush(stderr);
8311 #if defined(__WIN__) || defined(__WIN32__)
8312 DebugBreak();
8313 #else
8314 abort();
8315 #endif
8316 }
8317
8318 void
erts_alcu_verify_unused(Allctr_t * allctr)8319 erts_alcu_verify_unused(Allctr_t *allctr)
8320 {
8321 UWord no;
8322 int ix;
8323
8324 no = 0;
8325 for (ix = ERTS_CRR_ALLOC_MIN; ix <= ERTS_CRR_ALLOC_MAX; ix++) {
8326 no += allctr->sbcs.carriers[ix].no;
8327 }
8328
8329 ix = allctr->alloc_no - ERTS_ALC_A_MIN;
8330 no += allctr->mbcs.blocks[ix].curr.no;
8331
8332 if (no) {
8333 UWord sz = 0;
8334
8335 sz += allctr->sbcs.blocks[ix].curr.size;
8336 sz += allctr->mbcs.blocks[ix].curr.size;
8337
8338 erts_exit(ERTS_ABORT_EXIT,
8339 "%salloc() used when expected to be unused!\n"
8340 "Total amount of blocks allocated: %bpu\n"
8341 "Total amount of bytes allocated: %bpu\n",
8342 allctr->name_prefix, no, sz);
8343 }
8344 }
8345
8346 void
erts_alcu_verify_unused_ts(Allctr_t * allctr)8347 erts_alcu_verify_unused_ts(Allctr_t *allctr)
8348 {
8349 erts_mtx_lock(&allctr->mutex);
8350 erts_alcu_verify_unused(allctr);
8351 erts_mtx_unlock(&allctr->mutex);
8352 }
8353
8354
8355 #ifdef DEBUG
is_sbc_blk(Block_t * blk)8356 int is_sbc_blk(Block_t* blk)
8357 {
8358 return IS_SBC_BLK(blk);
8359 }
8360 #endif
8361
8362 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
8363
8364 static void
check_blk_carrier(Allctr_t * allctr,Block_t * iblk)8365 check_blk_carrier(Allctr_t *allctr, Block_t *iblk)
8366 {
8367 Carrier_t *crr;
8368 CarrierList_t *cl;
8369
8370 if (IS_SBC_BLK(iblk)) {
8371 Carrier_t *sbc = BLK_TO_SBC(iblk);
8372
8373 ASSERT(SBC2BLK(allctr, sbc) == iblk);
8374 ASSERT(CARRIER_SZ(sbc) - SBC_HEADER_SIZE >= SBC_BLK_SZ(iblk));
8375 crr = sbc;
8376 cl = &allctr->sbc_list;
8377 }
8378 else {
8379 Block_t *prev_blk = NULL;
8380 Block_t *blk;
8381 char *carrier_end;
8382 Uint is_free_blk;
8383 Uint tot_blk_sz;
8384 Uint blk_sz;
8385 int has_wrapped_around = 0;
8386
8387 blk = iblk;
8388 tot_blk_sz = 0;
8389 crr = BLK_TO_MBC(blk);
8390 ASSERT(IS_MB_CARRIER(crr));
8391
8392 /* Step around the carrier one whole lap starting at 'iblk'
8393 */
8394 while (1) {
8395 ASSERT(IS_MBC_BLK(blk));
8396 ASSERT(BLK_TO_MBC(blk) == crr);
8397
8398 if (prev_blk) {
8399 ASSERT(NXT_BLK(prev_blk) == blk);
8400 if (IS_FREE_BLK(prev_blk)) {
8401 ASSERT(IS_PREV_BLK_FREE(blk));
8402 ASSERT(prev_blk == PREV_BLK(blk));
8403 }
8404 else {
8405 ASSERT(IS_PREV_BLK_ALLOCED(blk));
8406 }
8407 }
8408
8409 if (has_wrapped_around) {
8410 ASSERT(((Block_t *) crr) < blk);
8411 if (blk == iblk)
8412 break;
8413 ASSERT(blk < iblk);
8414 }
8415 else
8416 ASSERT(blk >= iblk);
8417
8418 blk_sz = MBC_BLK_SZ(blk);
8419
8420 ASSERT(blk_sz % sizeof(Unit_t) == 0);
8421 ASSERT(blk_sz >= allctr->min_block_size);
8422
8423 tot_blk_sz += blk_sz;
8424
8425 is_free_blk = (int) IS_FREE_BLK(blk);
8426 ASSERT(!is_free_blk
8427 || IS_LAST_BLK(blk)
8428 || PREV_BLK_SZ(((char *) blk)+blk_sz) == blk_sz);
8429
8430 if (allctr->check_block)
8431 (*allctr->check_block)(allctr, blk, (int) is_free_blk);
8432
8433 if (IS_LAST_BLK(blk)) {
8434 carrier_end = ((char *) NXT_BLK(blk));
8435 has_wrapped_around = 1;
8436 prev_blk = NULL;
8437 blk = MBC_TO_FIRST_BLK(allctr, crr);
8438 ASSERT(IS_MBC_FIRST_BLK(allctr,blk));
8439 }
8440 else {
8441 prev_blk = blk;
8442 blk = NXT_BLK(blk);
8443 }
8444 }
8445
8446 ASSERT((((char *) crr)
8447 + MBC_HEADER_SIZE(allctr)
8448 + tot_blk_sz) == carrier_end);
8449 ASSERT(((char *) crr) + CARRIER_SZ(crr) - sizeof(Unit_t) <= carrier_end
8450 && carrier_end <= ((char *) crr) + CARRIER_SZ(crr));
8451
8452 if (allctr->check_mbc)
8453 (*allctr->check_mbc)(allctr, crr);
8454
8455 #if HAVE_ERTS_MSEG
8456 if (IS_MSEG_CARRIER(crr)) {
8457 ASSERT(CARRIER_SZ(crr) % ERTS_SACRR_UNIT_SZ == 0);
8458 }
8459 #endif
8460 cl = &allctr->mbc_list;
8461 }
8462
8463 #ifdef DEBUG
8464 if (cl->first == crr) {
8465 ASSERT(!crr->prev);
8466 }
8467 else {
8468 ASSERT(crr->prev);
8469 ASSERT(crr->prev->next == crr);
8470 }
8471 if (cl->last == crr) {
8472 ASSERT(!crr->next);
8473 }
8474 else {
8475 ASSERT(crr->next);
8476 ASSERT(crr->next->prev == crr);
8477 }
8478 #endif
8479 }
8480
8481 #endif /* ERTS_ALLOC_UTIL_HARD_DEBUG */
8482
8483 #ifdef ERTS_ENABLE_LOCK_COUNT
8484
lcnt_enable_allocator_lock_count(Allctr_t * allocator,int enable)8485 static void lcnt_enable_allocator_lock_count(Allctr_t *allocator, int enable) {
8486 if(!allocator->thread_safe) {
8487 return;
8488 }
8489
8490 if(enable) {
8491 erts_lcnt_install_new_lock_info(&allocator->mutex.lcnt,
8492 "alcu_allocator", make_small(allocator->alloc_no),
8493 ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
8494 } else {
8495 erts_lcnt_uninstall(&allocator->mutex.lcnt);
8496 }
8497 }
8498
lcnt_update_thread_spec_locks(ErtsAllocatorThrSpec_t * tspec,int enable)8499 static void lcnt_update_thread_spec_locks(ErtsAllocatorThrSpec_t *tspec, int enable) {
8500 if(tspec->enabled) {
8501 int i;
8502
8503 for(i = 0; i < tspec->size; i++) {
8504 lcnt_enable_allocator_lock_count(tspec->allctr[i], enable);
8505 }
8506 }
8507 }
8508
erts_lcnt_update_allocator_locks(int enable)8509 void erts_lcnt_update_allocator_locks(int enable) {
8510 int i;
8511
8512 for(i = ERTS_ALC_A_MIN; i < ERTS_ALC_A_MAX; i++) {
8513 ErtsAllocatorInfo_t *ai = &erts_allctrs_info[i];
8514
8515 if(ai->enabled && ai->alloc_util) {
8516 if(ai->thr_spec) {
8517 lcnt_update_thread_spec_locks((ErtsAllocatorThrSpec_t*)ai->extra, enable);
8518 } else {
8519 lcnt_enable_allocator_lock_count((Allctr_t*)ai->extra, enable);
8520 }
8521 }
8522 }
8523 }
8524 #endif /* ERTS_ENABLE_LOCK_COUNT */
8525