1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2003-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 combined "address order best fit"/"best fit" allocator
24  *              based on a Red-Black (binary search) Tree. The search,
25  *              insert, and delete operations are all O(log n) operations
26  *              on a Red-Black Tree. In the "address order best fit" case
27  *              n equals number of free blocks, and in the "best fit" case
28  *              n equals number of distinct sizes of free blocks. Red-Black
29  *              Trees are described in "Introduction to Algorithms", by
30  *              Thomas H. Cormen, Charles E. Leiserson, and
31  *              Ronald L. Riverest.
32  *
33  *              This module is a callback-module for erl_alloc_util.c
34  *
35  * Author: 	Rickard Green
36  */
37 
38 
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42 #include "global.h"
43 #define GET_ERL_BF_ALLOC_IMPL
44 #include "erl_bestfit_alloc.h"
45 
46 #ifdef DEBUG
47 #if 0
48 #define HARD_DEBUG
49 #endif
50 #else
51 #undef HARD_DEBUG
52 #endif
53 
54 #define MIN_MBC_SZ		(16*1024)
55 
56 #define TREE_NODE_FLG		(((Uint) 1) << 0)
57 #define RED_FLG			(((Uint) 1) << 1)
58 #ifdef HARD_DEBUG
59 #  define LEFT_VISITED_FLG	(((Uint) 1) << 2)
60 #  define RIGHT_VISITED_FLG	(((Uint) 1) << 3)
61 #endif
62 
63 #define IS_TREE_NODE(N)		(((RBTree_t *) (N))->flags & TREE_NODE_FLG)
64 #define IS_LIST_ELEM(N)		(!IS_TREE_NODE(((RBTree_t *) (N))))
65 
66 #define SET_TREE_NODE(N)	(((RBTree_t *) (N))->flags |= TREE_NODE_FLG)
67 #define SET_LIST_ELEM(N)	(((RBTree_t *) (N))->flags &= ~TREE_NODE_FLG)
68 
69 #define IS_RED(N)		(((RBTree_t *) (N)) \
70 				 && ((RBTree_t *) (N))->flags & RED_FLG)
71 #define IS_BLACK(N)		(!IS_RED(((RBTree_t *) (N))))
72 
73 #define SET_RED(N)		(((RBTree_t *) (N))->flags |= RED_FLG)
74 #define SET_BLACK(N)		(((RBTree_t *) (N))->flags &= ~RED_FLG)
75 
76 #define BF_BLK_SZ(B)            MBC_FBLK_SZ(&(B)->hdr)
77 
78 #if 1
79 #define RBT_ASSERT	ASSERT
80 #else
81 #define RBT_ASSERT(x)
82 #endif
83 
84 
85 #ifdef HARD_DEBUG
86 static RBTree_t * check_tree(RBTree_t, int, Uint);
87 #endif
88 
89 static void tree_delete(Allctr_t *allctr, Block_t *del);
90 
91 /* Prototypes of callback functions */
92 
93 /* "address order best fit" specific callback functions */
94 static Block_t *	aobf_get_free_block	(Allctr_t *, Uint,
95 						 Block_t *, Uint);
96 static void		aobf_link_free_block	(Allctr_t *, Block_t *);
97 #define			aobf_unlink_free_block	tree_delete
98 
99 /* "best fit" specific callback functions */
100 static Block_t *	bf_get_free_block	(Allctr_t *, Uint,
101 						 Block_t *, Uint);
102 static void		bf_link_free_block	(Allctr_t *, Block_t *);
103 static ERTS_INLINE void	bf_unlink_free_block	(Allctr_t *, Block_t *);
104 
105 
106 static Eterm		info_options		(Allctr_t *, char *, fmtfn_t *,
107 						 void *, Uint **, Uint *);
108 static void		init_atoms		(void);
109 
110 /* Types... */
111 struct RBTree_t_ {
112     Block_t hdr;
113     Uint flags;
114     RBTree_t *parent;
115     RBTree_t *left;
116     RBTree_t *right;
117 };
118 
119 typedef struct {
120     RBTree_t t;
121     RBTree_t *next;
122 } RBTreeList_t;
123 
124 #define BF_LIST_NEXT(N) (((RBTreeList_t *) (N))->next)
125 #define BF_LIST_PREV(N) (((RBTreeList_t *) (N))->t.parent)
126 
127 
128 #ifdef DEBUG
129 
130 /* Destroy all tree fields */
131 #define DESTROY_TREE_NODE(N)						\
132   sys_memset((void *) (((Block_t *) (N)) + 1),				\
133 	     0xff,							\
134 	     (sizeof(RBTree_t) - sizeof(Block_t)))
135 
136 /* Destroy all tree and list fields */
137 #define DESTROY_LIST_ELEM(N)						\
138   sys_memset((void *) (((Block_t *) (N)) + 1),				\
139 	     0xff,							\
140 	     (sizeof(RBTreeList_t) - sizeof(Block_t)))
141 
142 #else
143 
144 #define DESTROY_TREE_NODE(N)
145 #define DESTROY_LIST_ELEM(N)
146 
147 #endif
148 
149 
150 static int atoms_initialized = 0;
151 
152 void
erts_bfalc_init(void)153 erts_bfalc_init(void)
154 {
155     atoms_initialized = 0;
156 }
157 
158 Allctr_t *
erts_bfalc_start(BFAllctr_t * bfallctr,BFAllctrInit_t * bfinit,AllctrInit_t * init)159 erts_bfalc_start(BFAllctr_t *bfallctr,
160 		 BFAllctrInit_t *bfinit,
161 		 AllctrInit_t *init)
162 {
163     struct {
164 	int dummy;
165 	BFAllctr_t allctr;
166     } zero = {0};
167     /* The struct with a dummy element first is used in order to avoid (an
168        incorrect) gcc warning. gcc warns if {0} is used as initializer of
169        a struct when the first member is a struct (not if, for example,
170        the third member is a struct). */
171 
172     Allctr_t *allctr = (Allctr_t *) bfallctr;
173 
174     sys_memcpy((void *) bfallctr, (void *) &zero.allctr, sizeof(BFAllctr_t));
175 
176     bfallctr->address_order		= bfinit->ao;
177 
178 
179     allctr->mbc_header_size		= sizeof(Carrier_t);
180     allctr->min_mbc_size		= MIN_MBC_SZ;
181     allctr->min_block_size		= (bfinit->ao
182 					   ? sizeof(RBTree_t)
183 					   : sizeof(RBTreeList_t));
184 
185     allctr->vsn_str			= (bfinit->ao
186 					   ? ERTS_ALC_AOBF_ALLOC_VSN_STR
187 					   : ERTS_ALC_BF_ALLOC_VSN_STR);
188 
189 
190     /* Callback functions */
191 
192     if (bfinit->ao) {
193 	allctr->get_free_block		= aobf_get_free_block;
194 	allctr->link_free_block		= aobf_link_free_block;
195 	allctr->unlink_free_block	= aobf_unlink_free_block;
196     }
197     else {
198 	allctr->get_free_block		= bf_get_free_block;
199 	allctr->link_free_block		= bf_link_free_block;
200 	allctr->unlink_free_block	= bf_unlink_free_block;
201     }
202     allctr->info_options		= info_options;
203 
204     allctr->get_next_mbc_size		= NULL;
205     allctr->creating_mbc		= NULL;
206     allctr->destroying_mbc		= NULL;
207     allctr->add_mbc                     = NULL;
208     allctr->remove_mbc		        = NULL;
209     allctr->largest_fblk_in_mbc         = NULL;
210     allctr->first_fblk_in_mbc           = NULL;
211     allctr->next_fblk_in_mbc            = NULL;
212     allctr->init_atoms			= init_atoms;
213 
214 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
215     allctr->check_block			= NULL;
216     allctr->check_mbc			= NULL;
217 #endif
218 
219     allctr->atoms_initialized		= 0;
220 
221     if (!erts_alcu_start(allctr, init))
222 	return NULL;
223 
224     return allctr;
225 }
226 
227 /*
228  * Red-Black Tree operations needed
229  */
230 
231 static ERTS_INLINE void
left_rotate(RBTree_t ** root,RBTree_t * x)232 left_rotate(RBTree_t **root, RBTree_t *x)
233 {
234     RBTree_t *y = x->right;
235     x->right = y->left;
236     if (y->left)
237 	y->left->parent = x;
238     y->parent = x->parent;
239     if (!y->parent) {
240 	RBT_ASSERT(*root == x);
241 	*root = y;
242     }
243     else if (x == x->parent->left)
244 	x->parent->left = y;
245     else {
246 	RBT_ASSERT(x == x->parent->right);
247 	x->parent->right = y;
248     }
249     y->left = x;
250     x->parent = y;
251 }
252 
253 static ERTS_INLINE void
right_rotate(RBTree_t ** root,RBTree_t * x)254 right_rotate(RBTree_t **root, RBTree_t *x)
255 {
256     RBTree_t *y = x->left;
257     x->left = y->right;
258     if (y->right)
259 	y->right->parent = x;
260     y->parent = x->parent;
261     if (!y->parent) {
262 	RBT_ASSERT(*root == x);
263 	*root = y;
264     }
265     else if (x == x->parent->right)
266 	x->parent->right = y;
267     else {
268 	RBT_ASSERT(x == x->parent->left);
269 	x->parent->left = y;
270     }
271     y->right = x;
272     x->parent = y;
273 }
274 
275 
276 /*
277  * Replace node x with node y
278  * NOTE: block header of y is not changed
279  */
280 static ERTS_INLINE void
replace(RBTree_t ** root,RBTree_t * x,RBTree_t * y)281 replace(RBTree_t **root, RBTree_t *x, RBTree_t *y)
282 {
283 
284     if (!x->parent) {
285 	RBT_ASSERT(*root == x);
286 	*root = y;
287     }
288     else if (x == x->parent->left)
289 	x->parent->left = y;
290     else {
291 	RBT_ASSERT(x == x->parent->right);
292 	x->parent->right = y;
293     }
294     if (x->left) {
295 	RBT_ASSERT(x->left->parent == x);
296 	x->left->parent = y;
297     }
298     if (x->right) {
299 	RBT_ASSERT(x->right->parent == x);
300 	x->right->parent = y;
301     }
302 
303     y->flags	= x->flags;
304     y->parent	= x->parent;
305     y->right	= x->right;
306     y->left	= x->left;
307 
308     DESTROY_TREE_NODE(x);
309 
310 }
311 
312 static void
tree_insert_fixup(RBTree_t ** root,RBTree_t * blk)313 tree_insert_fixup(RBTree_t **root, RBTree_t *blk)
314 {
315     RBTree_t *x = blk, *y;
316 
317     /*
318      * Rearrange the tree so that it satisfies the Red-Black Tree properties
319      */
320 
321     RBT_ASSERT(x != *root && IS_RED(x->parent));
322     do {
323 
324 	/*
325 	 * x and its parent are both red. Move the red pair up the tree
326 	 * until we get to the root or until we can separate them.
327 	 */
328 
329 	RBT_ASSERT(IS_RED(x));
330 	RBT_ASSERT(IS_BLACK(x->parent->parent));
331 	RBT_ASSERT(x->parent->parent);
332 
333 	if (x->parent == x->parent->parent->left) {
334 	    y = x->parent->parent->right;
335 	    if (IS_RED(y)) {
336 		SET_BLACK(y);
337 		x = x->parent;
338 		SET_BLACK(x);
339 		x = x->parent;
340 		SET_RED(x);
341 	    }
342 	    else {
343 
344 		if (x == x->parent->right) {
345 		    x = x->parent;
346 		    left_rotate(root, x);
347 		}
348 
349 		RBT_ASSERT(x == x->parent->parent->left->left);
350 		RBT_ASSERT(IS_RED(x));
351 		RBT_ASSERT(IS_RED(x->parent));
352 		RBT_ASSERT(IS_BLACK(x->parent->parent));
353 		RBT_ASSERT(IS_BLACK(y));
354 
355 		SET_BLACK(x->parent);
356 		SET_RED(x->parent->parent);
357 		right_rotate(root, x->parent->parent);
358 
359 		RBT_ASSERT(x == x->parent->left);
360 		RBT_ASSERT(IS_RED(x));
361 		RBT_ASSERT(IS_RED(x->parent->right));
362 		RBT_ASSERT(IS_BLACK(x->parent));
363 		break;
364 	    }
365 	}
366 	else {
367 	    RBT_ASSERT(x->parent == x->parent->parent->right);
368 	    y = x->parent->parent->left;
369 	    if (IS_RED(y)) {
370 		SET_BLACK(y);
371 		x = x->parent;
372 		SET_BLACK(x);
373 		x = x->parent;
374 		SET_RED(x);
375 	    }
376 	    else {
377 
378 		if (x == x->parent->left) {
379 		    x = x->parent;
380 		    right_rotate(root, x);
381 		}
382 
383 		RBT_ASSERT(x == x->parent->parent->right->right);
384 		RBT_ASSERT(IS_RED(x));
385 		RBT_ASSERT(IS_RED(x->parent));
386 		RBT_ASSERT(IS_BLACK(x->parent->parent));
387 		RBT_ASSERT(IS_BLACK(y));
388 
389 		SET_BLACK(x->parent);
390 		SET_RED(x->parent->parent);
391 		left_rotate(root, x->parent->parent);
392 
393 		RBT_ASSERT(x == x->parent->right);
394 		RBT_ASSERT(IS_RED(x));
395 		RBT_ASSERT(IS_RED(x->parent->left));
396 		RBT_ASSERT(IS_BLACK(x->parent));
397 		break;
398 	    }
399 	}
400     } while (x != *root && IS_RED(x->parent));
401 
402     SET_BLACK(*root);
403 
404 }
405 
406 /*
407  * The argument types of "Allctr_t *" and "Block_t *" have been
408  * chosen since we then can use tree_delete() as unlink_free_block
409  * callback function in the address order case.
410  */
411 static void
tree_delete(Allctr_t * allctr,Block_t * del)412 tree_delete(Allctr_t *allctr, Block_t *del)
413 {
414     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
415     Uint spliced_is_black;
416     RBTree_t **root = &bfallctr->mbc_root;
417     RBTree_t *x, *y, *z = (RBTree_t *) del;
418     RBTree_t null_x; /* null_x is used to get the fixup started when we
419 			splice out a node without children. */
420 
421     null_x.parent = NULL;
422 
423 
424 #ifdef HARD_DEBUG
425     check_tree(*root, bfallctr->address_order, 0);
426 #endif
427 
428     /* Remove node from tree... */
429 
430     /* Find node to splice out */
431     if (!z->left || !z->right)
432 	y = z;
433     else
434 	/* Set y to z:s successor */
435 	for (y = z->right; y->left; y = y->left)
436             ;
437     /* splice out y */
438     x = y->left ? y->left : y->right;
439     spliced_is_black = IS_BLACK(y);
440     if (x) {
441 	x->parent = y->parent;
442     }
443     else if (!x && spliced_is_black) {
444 	x = &null_x;
445 	x->flags = 0;
446 	SET_BLACK(x);
447 	x->right = x->left = NULL;
448 	x->parent = y->parent;
449 	y->left = x;
450     }
451 
452     if (!y->parent) {
453 	RBT_ASSERT(*root == y);
454 	*root = x;
455     }
456     else if (y == y->parent->left)
457 	y->parent->left = x;
458     else {
459 	RBT_ASSERT(y == y->parent->right);
460 	y->parent->right = x;
461     }
462     if (y != z) {
463 	/* We spliced out the successor of z; replace z by the successor */
464 	replace(root, z, y);
465     }
466 
467     if (spliced_is_black) {
468 	/* We removed a black node which makes the resulting tree
469 	   violate the Red-Black Tree properties. Fixup tree... */
470 
471 	while (IS_BLACK(x) && x->parent) {
472 
473 	    /*
474 	     * x has an "extra black" which we move up the tree
475 	     * until we reach the root or until we can get rid of it.
476 	     *
477 	     * y is the sibbling of x
478 	     */
479 
480 	    if (x == x->parent->left) {
481 		y = x->parent->right;
482 		RBT_ASSERT(y);
483 		if (IS_RED(y)) {
484 		    RBT_ASSERT(y->right);
485 		    RBT_ASSERT(y->left);
486 		    SET_BLACK(y);
487 		    RBT_ASSERT(IS_BLACK(x->parent));
488 		    SET_RED(x->parent);
489 		    left_rotate(root, x->parent);
490 		    y = x->parent->right;
491 		}
492 		RBT_ASSERT(y);
493 		RBT_ASSERT(IS_BLACK(y));
494 		if (IS_BLACK(y->left) && IS_BLACK(y->right)) {
495 		    SET_RED(y);
496 		    x = x->parent;
497 		}
498 		else {
499 		    if (IS_BLACK(y->right)) {
500 			SET_BLACK(y->left);
501 			SET_RED(y);
502 			right_rotate(root, y);
503 			y = x->parent->right;
504 		    }
505 		    RBT_ASSERT(y);
506 		    if (IS_RED(x->parent)) {
507 
508 			SET_BLACK(x->parent);
509 			SET_RED(y);
510 		    }
511 		    RBT_ASSERT(y->right);
512 		    SET_BLACK(y->right);
513 		    left_rotate(root, x->parent);
514 		    x = *root;
515 		    break;
516 		}
517 	    }
518 	    else {
519 		RBT_ASSERT(x == x->parent->right);
520 		y = x->parent->left;
521 		RBT_ASSERT(y);
522 		if (IS_RED(y)) {
523 		    RBT_ASSERT(y->right);
524 		    RBT_ASSERT(y->left);
525 		    SET_BLACK(y);
526 		    RBT_ASSERT(IS_BLACK(x->parent));
527 		    SET_RED(x->parent);
528 		    right_rotate(root, x->parent);
529 		    y = x->parent->left;
530 		}
531 		RBT_ASSERT(y);
532 		RBT_ASSERT(IS_BLACK(y));
533 		if (IS_BLACK(y->right) && IS_BLACK(y->left)) {
534 		    SET_RED(y);
535 		    x = x->parent;
536 		}
537 		else {
538 		    if (IS_BLACK(y->left)) {
539 			SET_BLACK(y->right);
540 			SET_RED(y);
541 			left_rotate(root, y);
542 			y = x->parent->left;
543 		    }
544 		    RBT_ASSERT(y);
545 		    if (IS_RED(x->parent)) {
546 			SET_BLACK(x->parent);
547 			SET_RED(y);
548 		    }
549 		    RBT_ASSERT(y->left);
550 		    SET_BLACK(y->left);
551 		    right_rotate(root, x->parent);
552 		    x = *root;
553 		    break;
554 		}
555 	    }
556 	}
557 	SET_BLACK(x);
558 
559 	if (null_x.parent) {
560 	    if (null_x.parent->left == &null_x)
561 		null_x.parent->left = NULL;
562 	    else {
563 		RBT_ASSERT(null_x.parent->right == &null_x);
564 		null_x.parent->right = NULL;
565 	    }
566 	    RBT_ASSERT(!null_x.left);
567 	    RBT_ASSERT(!null_x.right);
568 	}
569 	else if (*root == &null_x) {
570 	    *root = NULL;
571 	    RBT_ASSERT(!null_x.left);
572 	    RBT_ASSERT(!null_x.right);
573 	}
574     }
575 
576 
577     DESTROY_TREE_NODE(del);
578 
579 #ifdef HARD_DEBUG
580     check_tree(root, bfallctr->address_order, 0);
581 #endif
582 
583 }
584 
585 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
586  * "Address order best fit" specific callbacks.                              *
587 \*                                                                           */
588 
589 static void
aobf_link_free_block(Allctr_t * allctr,Block_t * block)590 aobf_link_free_block(Allctr_t *allctr, Block_t *block)
591 {
592     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
593     RBTree_t **root = &bfallctr->mbc_root;
594     RBTree_t *blk = (RBTree_t *) block;
595     Uint blk_sz = BF_BLK_SZ(blk);
596 
597 
598     blk->flags	= 0;
599     blk->left	= NULL;
600     blk->right	= NULL;
601 
602     if (!*root) {
603 	blk->parent = NULL;
604 	SET_BLACK(blk);
605 	*root = blk;
606     }
607     else {
608 	RBTree_t *x = *root;
609 	while (1) {
610 	    Uint size;
611 
612 	    size = BF_BLK_SZ(x);
613 
614 	    if (blk_sz < size || (blk_sz == size && blk < x)) {
615 		if (!x->left) {
616 		    blk->parent = x;
617 		    x->left = blk;
618 		    break;
619 		}
620 		x = x->left;
621 	    }
622 	    else {
623 		if (!x->right) {
624 		    blk->parent = x;
625 		    x->right = blk;
626 		    break;
627 		}
628 		x = x->right;
629 	    }
630 
631 	}
632 
633 	/* Insert block into size tree */
634 	RBT_ASSERT(blk->parent);
635 
636 	SET_RED(blk);
637 	if (IS_RED(blk->parent))
638 	    tree_insert_fixup(root, blk);
639     }
640 
641 #ifdef HARD_DEBUG
642     check_tree(root, 1, 0);
643 #endif
644 }
645 
646 #if 0 /* tree_delete() is directly used instead */
647 static void
648 aobf_unlink_free_block(Allctr_t *allctr, Block_t *block)
649 {
650     tree_delete(allctr, block);
651 }
652 #endif
653 
654 static Block_t *
aobf_get_free_block(Allctr_t * allctr,Uint size,Block_t * cand_blk,Uint cand_size)655 aobf_get_free_block(Allctr_t *allctr, Uint size,
656 		    Block_t *cand_blk, Uint cand_size)
657 {
658     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
659     RBTree_t **root = &bfallctr->mbc_root;
660     RBTree_t *x = *root;
661     RBTree_t *blk = NULL;
662     Uint blk_sz;
663 
664     ASSERT(!cand_blk || cand_size >= size);
665 
666     while (x) {
667 	blk_sz = BF_BLK_SZ(x);
668 	if (blk_sz < size) {
669 	    x = x->right;
670 	}
671 	else {
672 	    blk = x;
673 	    x = x->left;
674 	}
675     }
676 
677     if (!blk)
678 	return NULL;
679 
680 #ifdef HARD_DEBUG
681     ASSERT(blk == check_tree(root, 1, size));
682 #endif
683 
684     if (cand_blk) {
685 	blk_sz = BF_BLK_SZ(blk);
686 	if (cand_size < blk_sz)
687 	    return NULL; /* cand_blk was better */
688 	if (cand_size == blk_sz && ((void *) cand_blk) < ((void *) blk))
689 	    return NULL; /* cand_blk was better */
690     }
691 
692     aobf_unlink_free_block(allctr, (Block_t *) blk);
693 
694     return (Block_t *) blk;
695 }
696 
697 
698 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
699  * "Best fit" specific callbacks.                                            *
700 \*                                                                           */
701 
702 static void
bf_link_free_block(Allctr_t * allctr,Block_t * block)703 bf_link_free_block(Allctr_t *allctr, Block_t *block)
704 {
705     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
706     RBTree_t **root = &bfallctr->mbc_root;
707     RBTree_t *blk = (RBTree_t *) block;
708     Uint blk_sz = BF_BLK_SZ(blk);
709 
710     SET_TREE_NODE(blk);
711 
712 
713     blk->flags	= 0;
714     blk->left	= NULL;
715     blk->right	= NULL;
716 
717     if (!*root) {
718 	blk->parent = NULL;
719 	SET_BLACK(blk);
720 	*root = blk;
721     }
722     else {
723 	RBTree_t *x = *root;
724 	while (1) {
725 	    Uint size;
726 
727 	    size = BF_BLK_SZ(x);
728 
729 	    if (blk_sz == size) {
730 
731 		SET_LIST_ELEM(blk);
732 		BF_LIST_NEXT(blk) = BF_LIST_NEXT(x);
733 		BF_LIST_PREV(blk) = x;
734 		if (BF_LIST_NEXT(x))
735 		    BF_LIST_PREV(BF_LIST_NEXT(x)) = blk;
736 		BF_LIST_NEXT(x) = blk;
737 
738 		return; /* Finnished */
739 	    }
740 	    else if (blk_sz < size) {
741 		if (!x->left) {
742 		    blk->parent = x;
743 		    x->left = blk;
744 		    break;
745 		}
746 		x = x->left;
747 	    }
748 	    else {
749 		if (!x->right) {
750 		    blk->parent = x;
751 		    x->right = blk;
752 		    break;
753 		}
754 		x = x->right;
755 	    }
756 	}
757 
758 	RBT_ASSERT(blk->parent);
759 
760 	SET_RED(blk);
761 	if (IS_RED(blk->parent))
762 	    tree_insert_fixup(root, blk);
763 
764     }
765 
766     SET_TREE_NODE(blk);
767     BF_LIST_NEXT(blk) = NULL;
768 
769 #ifdef HARD_DEBUG
770     check_tree(root, 0, 0);
771 #endif
772 }
773 
774 static ERTS_INLINE void
bf_unlink_free_block(Allctr_t * allctr,Block_t * block)775 bf_unlink_free_block(Allctr_t *allctr, Block_t *block)
776 {
777     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
778     RBTree_t **root = &bfallctr->mbc_root;
779     RBTree_t *x = (RBTree_t *) block;
780 
781     if (IS_LIST_ELEM(x)) {
782 	/* Remove from list */
783 	ASSERT(BF_LIST_PREV(x));
784 	BF_LIST_NEXT(BF_LIST_PREV(x)) = BF_LIST_NEXT(x);
785 	if (BF_LIST_NEXT(x))
786 	    BF_LIST_PREV(BF_LIST_NEXT(x)) = BF_LIST_PREV(x);
787     }
788     else if (BF_LIST_NEXT(x)) {
789 	/* Replace tree node by next element in list... */
790 
791 	ASSERT(BF_BLK_SZ(BF_LIST_NEXT(x)) == BF_BLK_SZ(x));
792 	ASSERT(IS_TREE_NODE(x));
793 	ASSERT(IS_LIST_ELEM(BF_LIST_NEXT(x)));
794 
795 #ifdef HARD_DEBUG
796 	check_tree(root, 0, 0);
797 #endif
798 	replace(root, x, BF_LIST_NEXT(x));
799 
800 #ifdef HARD_DEBUG
801 	check_tree(bfallctr, 0);
802 #endif
803     }
804     else {
805 	/* Remove from tree */
806 	tree_delete(allctr, block);
807     }
808 
809     DESTROY_LIST_ELEM(x);
810 }
811 
812 
813 static Block_t *
bf_get_free_block(Allctr_t * allctr,Uint size,Block_t * cand_blk,Uint cand_size)814 bf_get_free_block(Allctr_t *allctr, Uint size,
815 		  Block_t *cand_blk, Uint cand_size)
816 {
817     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
818     RBTree_t **root = &bfallctr->mbc_root;
819     RBTree_t *x = *root;
820     RBTree_t *blk = NULL;
821     Uint blk_sz;
822 
823     ASSERT(!cand_blk || cand_size >= size);
824 
825     while (x) {
826 	blk_sz = BF_BLK_SZ(x);
827 	if (blk_sz < size) {
828 	    x = x->right;
829 	}
830 	else {
831 	    blk = x;
832 	    if (blk_sz == size)
833 		break;
834 	    x = x->left;
835 	}
836     }
837 
838     if (!blk)
839 	return NULL;
840 
841     ASSERT(IS_TREE_NODE(blk));
842 
843 
844 #ifdef HARD_DEBUG
845     {
846 	RBTree_t *ct_blk = check_tree(root, 0, size);
847 	ASSERT(BF_BLK_SZ(ct_blk) == BF_BLK_SZ(blk));
848     }
849 #endif
850 
851     if (cand_blk && cand_size <= BF_BLK_SZ(blk))
852 	return NULL; /* cand_blk was better */
853 
854     /* Use next block if it exist in order to avoid replacing
855        the tree node */
856     blk = BF_LIST_NEXT(blk) ? BF_LIST_NEXT(blk) : blk;
857 
858     bf_unlink_free_block(allctr, (Block_t *) blk);
859     return (Block_t *) blk;
860 }
861 
862 
863 /*
864  * info_options()
865  */
866 
867 static struct {
868     Eterm as;
869     Eterm aobf;
870     Eterm bf;
871 #ifdef DEBUG
872     Eterm end_of_atoms;
873 #endif
874 } am;
875 
atom_init(Eterm * atom,char * name)876 static void ERTS_INLINE atom_init(Eterm *atom, char *name)
877 {
878     *atom = am_atom_put(name, sys_strlen(name));
879 }
880 #define AM_INIT(AM) atom_init(&am.AM, #AM)
881 
882 static void
init_atoms(void)883 init_atoms(void)
884 {
885 #ifdef DEBUG
886     Eterm *atom;
887 #endif
888 
889     if (atoms_initialized)
890 	return;
891 
892 #ifdef DEBUG
893     for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
894 	*atom = THE_NON_VALUE;
895     }
896 #endif
897     AM_INIT(as);
898     AM_INIT(aobf);
899     AM_INIT(bf);
900 
901 #ifdef DEBUG
902     for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
903 	ASSERT(*atom != THE_NON_VALUE);
904     }
905 #endif
906 
907     atoms_initialized = 1;
908 }
909 
910 
911 #define bld_uint	erts_bld_uint
912 #define bld_cons	erts_bld_cons
913 #define bld_tuple	erts_bld_tuple
914 
915 static ERTS_INLINE void
add_2tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2)916 add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
917 {
918     *lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 2, el1, el2), *lp);
919 }
920 
921 static Eterm
info_options(Allctr_t * allctr,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)922 info_options(Allctr_t *allctr,
923 	     char *prefix,
924 	     fmtfn_t *print_to_p,
925 	     void *print_to_arg,
926 	     Uint **hpp,
927 	     Uint *szp)
928 {
929     BFAllctr_t *bfallctr = (BFAllctr_t *) allctr;
930     Eterm res = THE_NON_VALUE;
931 
932     if (print_to_p) {
933 	erts_print(*print_to_p,
934 		   print_to_arg,
935 		   "%sas: %s\n",
936 		   prefix,
937 		   bfallctr->address_order ? "aobf" : "bf");
938     }
939 
940     if (hpp || szp) {
941 
942 	if (!atoms_initialized)
943 	    erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized",
944 		     __FILE__, __LINE__);;
945 
946 	res = NIL;
947 	add_2tup(hpp, szp, &res,
948 		 am.as,
949 		 bfallctr->address_order ? am.aobf : am.bf);
950     }
951 
952     return res;
953 }
954 
955 
956 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
957  * NOTE:  erts_bfalc_test() is only supposed to be used for testing.         *
958  *                                                                           *
959  * Keep alloc_SUITE_data/allocator_test.h updated if changes are made        *
960  * to erts_bfalc_test()                                                      *
961 \*                                                                           */
962 
963 UWord
erts_bfalc_test(UWord op,UWord a1,UWord a2)964 erts_bfalc_test(UWord op, UWord a1, UWord a2)
965 {
966     switch (op) {
967     case 0x200:	return (UWord) ((BFAllctr_t *) a1)->address_order; /* IS_AOBF */
968     case 0x201:	return (UWord) ((BFAllctr_t *) a1)->mbc_root;
969     case 0x202:	return (UWord) ((RBTree_t *) a1)->parent;
970     case 0x203:	return (UWord) ((RBTree_t *) a1)->left;
971     case 0x204:	return (UWord) ((RBTree_t *) a1)->right;
972     case 0x205:	return (UWord) BF_LIST_NEXT(a1);
973     case 0x206:	return (UWord) IS_BLACK((RBTree_t *) a1);
974     case 0x207:	return (UWord) IS_TREE_NODE((RBTree_t *) a1);
975     case 0x208:	return (UWord) 1; /* IS_BF_ALGO */
976     case 0x20a: return (UWord) !((BFAllctr_t *) a1)->address_order; /* IS_BF */
977     case 0x20b:	return (UWord) BF_LIST_PREV(a1);
978     default:	ASSERT(0); return ~((UWord) 0);
979     }
980 }
981 
982 
983 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
984  * Debug functions                                                           *
985 \*                                                                           */
986 
987 
988 #ifdef HARD_DEBUG
989 
990 #define IS_LEFT_VISITED(FB)	((FB)->flags & LEFT_VISITED_FLG)
991 #define IS_RIGHT_VISITED(FB)	((FB)->flags & RIGHT_VISITED_FLG)
992 
993 #define SET_LEFT_VISITED(FB)	((FB)->flags |= LEFT_VISITED_FLG)
994 #define SET_RIGHT_VISITED(FB)	((FB)->flags |= RIGHT_VISITED_FLG)
995 
996 #define UNSET_LEFT_VISITED(FB)	((FB)->flags &= ~LEFT_VISITED_FLG)
997 #define UNSET_RIGHT_VISITED(FB)	((FB)->flags &= ~RIGHT_VISITED_FLG)
998 
999 
1000 #if 0
1001 #  define PRINT_TREE
1002 #else
1003 #  undef PRINT_TREE
1004 #endif
1005 
1006 #ifdef PRINT_TREE
1007 static void print_tree(RBTree_t *, int);
1008 #endif
1009 
1010 /*
1011  * Checks that the order between parent and children are correct,
1012  * and that the Red-Black Tree properies are satisfied. if size > 0,
1013  * check_tree() returns a node that satisfies "best fit" resp.
1014  * "address order best fit".
1015  *
1016  * The Red-Black Tree properies are:
1017  *   1. Every node is either red or black.
1018  *   2. Every leaf (NIL) is black.
1019  *   3. If a node is red, then both its children are black.
1020  *   4. Every simple path from a node to a descendant leaf
1021  *      contains the same number of black nodes.
1022  */
1023 
1024 static RBTree_t *
check_tree(RBTree_t * root,int ao,Uint size)1025 check_tree(RBTree_t *root, int ao, Uint size)
1026 {
1027     RBTree_t *res = NULL;
1028     Sint blacks;
1029     Sint curr_blacks;
1030     RBTree_t *x;
1031 
1032 #ifdef PRINT_TREE
1033     print_tree(root, ao);
1034 #endif
1035 
1036     if (!root)
1037 	return res;
1038 
1039     x = root;
1040     ASSERT(IS_BLACK(x));
1041     ASSERT(!x->parent);
1042     curr_blacks = 1;
1043     blacks = -1;
1044 
1045     while (x) {
1046 	if (!IS_LEFT_VISITED(x)) {
1047 	    SET_LEFT_VISITED(x);
1048 	    if (x->left) {
1049 		x = x->left;
1050 		if (IS_BLACK(x))
1051 		    curr_blacks++;
1052 		continue;
1053 	    }
1054 	    else {
1055 		if (blacks < 0)
1056 		    blacks = curr_blacks;
1057 		ASSERT(blacks == curr_blacks);
1058 	    }
1059 	}
1060 
1061 	if (!IS_RIGHT_VISITED(x)) {
1062 	    SET_RIGHT_VISITED(x);
1063 	    if (x->right) {
1064 		x = x->right;
1065 		if (IS_BLACK(x))
1066 		    curr_blacks++;
1067 		continue;
1068 	    }
1069 	    else {
1070 		if (blacks < 0)
1071 		    blacks = curr_blacks;
1072 		ASSERT(blacks == curr_blacks);
1073 	    }
1074 	}
1075 
1076 
1077 	if (IS_RED(x)) {
1078 	    ASSERT(IS_BLACK(x->right));
1079 	    ASSERT(IS_BLACK(x->left));
1080 	}
1081 
1082 	ASSERT(x->parent || x == root);
1083 
1084 	if (x->left) {
1085 	    ASSERT(x->left->parent == x);
1086 	    if (ao) {
1087 		ASSERT(BF_BLK_SZ(x->left) < BF_BLK_SZ(x)
1088 		       || (BF_BLK_SZ(x->left) == BF_BLK_SZ(x) && x->left < x));
1089 	    }
1090 	    else {
1091 		ASSERT(IS_TREE_NODE(x->left));
1092 		ASSERT(BF_BLK_SZ(x->left) < BF_BLK_SZ(x));
1093 	    }
1094 	}
1095 
1096 	if (x->right) {
1097 	    ASSERT(x->right->parent == x);
1098 	    if (ao) {
1099 		ASSERT(BF_BLK_SZ(x->right) > BF_BLK_SZ(x)
1100 		       || (BF_BLK_SZ(x->right) == BF_BLK_SZ(x) && x->right > x));
1101 	    }
1102 	    else {
1103 		ASSERT(IS_TREE_NODE(x->right));
1104 		ASSERT(BF_BLK_SZ(x->right) > BF_BLK_SZ(x));
1105 	    }
1106 	}
1107 
1108 	if (size && BF_BLK_SZ(x) >= size) {
1109 	    if (ao) {
1110 		if (!res
1111 		    || BF_BLK_SZ(x) < BF_BLK_SZ(res)
1112 		    || (BF_BLK_SZ(x) == BF_BLK_SZ(res) && x < res))
1113 		    res = x;
1114 	    }
1115 	    else {
1116 		if (!res || BF_BLK_SZ(x) < BF_BLK_SZ(res))
1117 		    res = x;
1118 	    }
1119 	}
1120 
1121 	UNSET_LEFT_VISITED(x);
1122 	UNSET_RIGHT_VISITED(x);
1123 	if (IS_BLACK(x))
1124 	    curr_blacks--;
1125 	x = x->parent;
1126 
1127     }
1128 
1129     ASSERT(curr_blacks == 0);
1130 
1131     UNSET_LEFT_VISITED(root);
1132     UNSET_RIGHT_VISITED(root);
1133 
1134     return res;
1135 
1136 }
1137 
1138 
1139 #ifdef PRINT_TREE
1140 #define INDENT_STEP 2
1141 
1142 #include <stdio.h>
1143 
1144 static void
print_tree_aux(RBTree_t * x,int indent)1145 print_tree_aux(RBTree_t *x, int indent)
1146 {
1147     int i;
1148 
1149     if (!x) {
1150 	for (i = 0; i < indent; i++) {
1151 	    putc(' ', stderr);
1152 	}
1153 	fprintf(stderr, "BLACK: nil\r\n");
1154     }
1155     else {
1156 	print_tree_aux(x->right, indent + INDENT_STEP);
1157 	for (i = 0; i < indent; i++) {
1158 	    putc(' ', stderr);
1159 	}
1160 	fprintf(stderr, "%s: sz=%lu addr=0x%lx\r\n",
1161 		IS_BLACK(x) ? "BLACK" : "RED",
1162 		BF_BLK_SZ(x),
1163 		(Uint) x);
1164 	print_tree_aux(x->left,  indent + INDENT_STEP);
1165     }
1166 }
1167 
1168 
1169 static void
print_tree(RBTree_t * root,int ao)1170 print_tree(RBTree_t *root, int ao)
1171 {
1172     char *type = ao ? "Size-Adress" : "Size";
1173     fprintf(stderr, " --- %s tree begin ---\r\n", type);
1174     print_tree_aux(root, 0);
1175     fprintf(stderr, " --- %s tree end ---\r\n", type);
1176 }
1177 
1178 #endif
1179 
1180 #endif
1181