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, &region);
2721         mem_discard_finish(allocator, block, &region);
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