1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2003-2018. 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 fast allocator intended for temporary allocation.
24  *              When allocating, only the first block in the free list
25  *              is inspected, if this block doesn't fit a new carrier
26  *              is created. NOTE: this allocator can behave really bad
27  *              if misused.
28  *
29  *              This module is a callback-module for erl_alloc_util.c
30  *
31  * Author: 	Rickard Green
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #  include "config.h"
36 #endif
37 #include "global.h"
38 #define GET_ERL_AF_ALLOC_IMPL
39 #include "erl_afit_alloc.h"
40 
41 struct AFFreeBlock_t_ {
42     Block_t block_head;
43     AFFreeBlock_t *prev;
44     AFFreeBlock_t *next;
45 };
46 #define AF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->block_head)
47 
48 #define MIN_MBC_SZ		(16*1024)
49 #define MIN_MBC_FIRST_FREE_SZ	(4*1024)
50 
51 /* Prototypes of callback functions */
52 static Block_t *	get_free_block		(Allctr_t *, Uint, Block_t *, Uint);
53 static void		link_free_block		(Allctr_t *, Block_t *);
54 static void		unlink_free_block	(Allctr_t *, Block_t *);
55 
56 
57 static Eterm		info_options		(Allctr_t *, char *, fmtfn_t *,
58 						 void *arg, Uint **, Uint *);
59 static void		init_atoms		(void);
60 
61 static int atoms_initialized = 0;
62 
63 void
erts_afalc_init(void)64 erts_afalc_init(void)
65 {
66     atoms_initialized = 0;
67 }
68 
69 Allctr_t *
erts_afalc_start(AFAllctr_t * afallctr,AFAllctrInit_t * afinit,AllctrInit_t * init)70 erts_afalc_start(AFAllctr_t *afallctr,
71 		 AFAllctrInit_t *afinit,
72 		 AllctrInit_t *init)
73 {
74     struct {
75 	int dummy;
76 	AFAllctr_t allctr;
77     } zero = {0};
78     /* The struct with a dummy element first is used in order to avoid (an
79        incorrect) gcc warning. gcc warns if {0} is used as initializer of
80        a struct when the first member is a struct (not if, for example,
81        the third member is a struct). */
82 
83     Allctr_t *allctr = (Allctr_t *) afallctr;
84 
85     sys_memcpy((void *) afallctr, (void *) &zero.allctr, sizeof(AFAllctr_t));
86 
87     allctr->mbc_header_size		= sizeof(Carrier_t);
88     allctr->min_mbc_size		= MIN_MBC_SZ;
89     allctr->min_mbc_first_free_size	= MIN_MBC_FIRST_FREE_SZ;
90     allctr->min_block_size		= sizeof(AFFreeBlock_t);
91     allctr->vsn_str			= ERTS_ALC_AF_ALLOC_VSN_STR;
92 
93     /* Callback functions */
94     allctr->get_free_block		= get_free_block;
95     allctr->link_free_block		= link_free_block;
96     allctr->unlink_free_block		= unlink_free_block;
97     allctr->info_options		= info_options;
98 
99     allctr->get_next_mbc_size		= NULL;
100     allctr->creating_mbc		= NULL;
101     allctr->destroying_mbc		= NULL;
102     allctr->add_mbc                     = NULL;
103     allctr->remove_mbc                  = NULL;
104     allctr->largest_fblk_in_mbc         = NULL;
105     allctr->init_atoms			= init_atoms;
106 
107 #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG
108     allctr->check_block			= NULL;
109     allctr->check_mbc			= NULL;
110 #endif
111 
112     allctr->atoms_initialized		= 0;
113 
114     if (!erts_alcu_start(allctr, init))
115 	return NULL;
116 
117     return allctr;
118 }
119 
120 static Block_t *
get_free_block(Allctr_t * allctr,Uint size,Block_t * cand_blk,Uint cand_size)121 get_free_block(Allctr_t *allctr, Uint size, Block_t *cand_blk, Uint cand_size)
122 {
123     AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
124 
125     ASSERT(!cand_blk || cand_size >= size);
126 
127     if (afallctr->free_list && AF_BLK_SZ(afallctr->free_list) >= size) {
128 	AFFreeBlock_t *res = afallctr->free_list;
129 	afallctr->free_list = res->next;
130 	if (res->next)
131 	    res->next->prev = NULL;
132 	return (Block_t *) res;
133     }
134     else
135 	return NULL;
136 }
137 
138 static void
link_free_block(Allctr_t * allctr,Block_t * block)139 link_free_block(Allctr_t *allctr, Block_t *block)
140 {
141     AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
142     AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
143 
144     if (afallctr->free_list && AF_BLK_SZ(afallctr->free_list) > AF_BLK_SZ(blk)) {
145 	blk->next = afallctr->free_list->next;
146 	blk->prev = afallctr->free_list;
147 	afallctr->free_list->next = blk;
148     }
149     else {
150 	blk->next = afallctr->free_list;
151 	blk->prev = NULL;
152 	afallctr->free_list = blk;
153     }
154 
155     if (blk->next)
156 	blk->next->prev = blk;
157 }
158 
159 static void
unlink_free_block(Allctr_t * allctr,Block_t * block)160 unlink_free_block(Allctr_t *allctr, Block_t *block)
161 {
162     AFFreeBlock_t *blk = (AFFreeBlock_t *) block;
163     AFAllctr_t *afallctr = (AFAllctr_t *) allctr;
164 
165     if (blk->prev)
166 	blk->prev->next = blk->next;
167     else
168 	afallctr->free_list = blk->next;
169     if (blk->next)
170 	blk->next->prev = blk->prev;
171 }
172 
173 
174 static struct {
175     Eterm as;
176     Eterm af;
177 #ifdef DEBUG
178     Eterm end_of_atoms;
179 #endif
180 } am;
181 
atom_init(Eterm * atom,char * name)182 static void ERTS_INLINE atom_init(Eterm *atom, char *name)
183 {
184     *atom = am_atom_put(name, sys_strlen(name));
185 }
186 #define AM_INIT(AM) atom_init(&am.AM, #AM)
187 
188 static void
init_atoms(void)189 init_atoms(void)
190 {
191 #ifdef DEBUG
192     Eterm *atom;
193 #endif
194 
195     if (atoms_initialized)
196 	return;
197 
198 #ifdef DEBUG
199     for (atom = (Eterm *) &am; atom <= &am.end_of_atoms; atom++) {
200 	*atom = THE_NON_VALUE;
201     }
202 #endif
203 
204     AM_INIT(as);
205     AM_INIT(af);
206 
207 #ifdef DEBUG
208     for (atom = (Eterm *) &am; atom < &am.end_of_atoms; atom++) {
209 	ASSERT(*atom != THE_NON_VALUE);
210     }
211 #endif
212 
213     atoms_initialized = 1;
214 }
215 
216 
217 #define bld_uint	erts_bld_uint
218 #define bld_cons	erts_bld_cons
219 #define bld_tuple	erts_bld_tuple
220 
221 static ERTS_INLINE void
add_2tup(Uint ** hpp,Uint * szp,Eterm * lp,Eterm el1,Eterm el2)222 add_2tup(Uint **hpp, Uint *szp, Eterm *lp, Eterm el1, Eterm el2)
223 {
224     *lp = bld_cons(hpp, szp, bld_tuple(hpp, szp, 2, el1, el2), *lp);
225 }
226 
227 static Eterm
info_options(Allctr_t * allctr,char * prefix,fmtfn_t * print_to_p,void * print_to_arg,Uint ** hpp,Uint * szp)228 info_options(Allctr_t *allctr,
229 	     char *prefix,
230 	     fmtfn_t *print_to_p,
231 	     void *print_to_arg,
232 	     Uint **hpp,
233 	     Uint *szp)
234 {
235     Eterm res = THE_NON_VALUE;
236 
237     if (print_to_p) {
238 	erts_print(*print_to_p, print_to_arg, "%sas: af\n", prefix);
239     }
240 
241     if (hpp || szp) {
242 
243 	if (!atoms_initialized)
244 	    erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error: Atoms not initialized",
245 		     __FILE__, __LINE__);;
246 
247 	res = NIL;
248 	add_2tup(hpp, szp, &res, am.as, am.af);
249     }
250 
251     return res;
252 }
253 
254 
255 
256 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
257  * NOTE:  erts_afalc_test() is only supposed to be used for testing.         *
258  *                                                                           *
259  * Keep alloc_SUITE_data/allocator_test.h updated if changes are made        *
260  * to erts_afalc_test()                                                      *
261 \*                                                                           */
262 
263 UWord
erts_afalc_test(UWord op,UWord a1,UWord a2)264 erts_afalc_test(UWord op, UWord a1, UWord a2)
265 {
266     switch (op) {
267     default:	ASSERT(0); return ~((UWord) 0);
268     }
269 }
270