• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #define	JEMALLOC_CHUNK_C_
2 #include "jemalloc/internal/jemalloc_internal.h"
3 
4 /******************************************************************************/
5 /* Data. */
6 
7 const char	*opt_dss = DSS_DEFAULT;
8 size_t		opt_lg_chunk = LG_CHUNK_DEFAULT;
9 
10 malloc_mutex_t	chunks_mtx;
11 chunk_stats_t	stats_chunks;
12 
13 /*
14  * Trees of chunks that were previously allocated (trees differ only in node
15  * ordering).  These are used when allocating chunks, in an attempt to re-use
16  * address space.  Depending on function, different tree orderings are needed,
17  * which is why there are two trees with the same contents.
18  */
19 static extent_tree_t	chunks_szad_mmap;
20 static extent_tree_t	chunks_ad_mmap;
21 static extent_tree_t	chunks_szad_dss;
22 static extent_tree_t	chunks_ad_dss;
23 
24 rtree_t		*chunks_rtree;
25 
26 /* Various chunk-related settings. */
27 size_t		chunksize;
28 size_t		chunksize_mask; /* (chunksize - 1). */
29 size_t		chunk_npages;
30 size_t		map_bias;
31 size_t		arena_maxclass; /* Max size class for arenas. */
32 
33 /******************************************************************************/
34 /*
35  * Function prototypes for static functions that are referenced prior to
36  * definition.
37  */
38 
39 static void	chunk_dalloc_core(void *chunk, size_t size);
40 
41 /******************************************************************************/
42 
43 static void *
chunk_recycle(extent_tree_t * chunks_szad,extent_tree_t * chunks_ad,size_t size,size_t alignment,bool base,bool * zero)44 chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
45     size_t alignment, bool base, bool *zero)
46 {
47 	void *ret;
48 	extent_node_t *node;
49 	extent_node_t key;
50 	size_t alloc_size, leadsize, trailsize;
51 	bool zeroed;
52 
53 	if (base) {
54 		/*
55 		 * This function may need to call base_node_{,de}alloc(), but
56 		 * the current chunk allocation request is on behalf of the
57 		 * base allocator.  Avoid deadlock (and if that weren't an
58 		 * issue, potential for infinite recursion) by returning NULL.
59 		 */
60 		return (NULL);
61 	}
62 
63 	alloc_size = size + alignment - chunksize;
64 	/* Beware size_t wrap-around. */
65 	if (alloc_size < size)
66 		return (NULL);
67 	key.addr = NULL;
68 	key.size = alloc_size;
69 	malloc_mutex_lock(&chunks_mtx);
70 	node = extent_tree_szad_nsearch(chunks_szad, &key);
71 	if (node == NULL) {
72 		malloc_mutex_unlock(&chunks_mtx);
73 		return (NULL);
74 	}
75 	leadsize = ALIGNMENT_CEILING((uintptr_t)node->addr, alignment) -
76 	    (uintptr_t)node->addr;
77 	assert(node->size >= leadsize + size);
78 	trailsize = node->size - leadsize - size;
79 	ret = (void *)((uintptr_t)node->addr + leadsize);
80 	zeroed = node->zeroed;
81 	if (zeroed)
82 	    *zero = true;
83 	/* Remove node from the tree. */
84 	extent_tree_szad_remove(chunks_szad, node);
85 	extent_tree_ad_remove(chunks_ad, node);
86 	if (leadsize != 0) {
87 		/* Insert the leading space as a smaller chunk. */
88 		node->size = leadsize;
89 		extent_tree_szad_insert(chunks_szad, node);
90 		extent_tree_ad_insert(chunks_ad, node);
91 		node = NULL;
92 	}
93 	if (trailsize != 0) {
94 		/* Insert the trailing space as a smaller chunk. */
95 		if (node == NULL) {
96 			/*
97 			 * An additional node is required, but
98 			 * base_node_alloc() can cause a new base chunk to be
99 			 * allocated.  Drop chunks_mtx in order to avoid
100 			 * deadlock, and if node allocation fails, deallocate
101 			 * the result before returning an error.
102 			 */
103 			malloc_mutex_unlock(&chunks_mtx);
104 			node = base_node_alloc();
105 			if (node == NULL) {
106 				chunk_dalloc_core(ret, size);
107 				return (NULL);
108 			}
109 			malloc_mutex_lock(&chunks_mtx);
110 		}
111 		node->addr = (void *)((uintptr_t)(ret) + size);
112 		node->size = trailsize;
113 		node->zeroed = zeroed;
114 		extent_tree_szad_insert(chunks_szad, node);
115 		extent_tree_ad_insert(chunks_ad, node);
116 		node = NULL;
117 	}
118 	malloc_mutex_unlock(&chunks_mtx);
119 
120 	if (node != NULL)
121 		base_node_dalloc(node);
122 	if (*zero) {
123 		if (zeroed == false)
124 			memset(ret, 0, size);
125 		else if (config_debug) {
126 			size_t i;
127 			size_t *p = (size_t *)(uintptr_t)ret;
128 
129 			JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, size);
130 			for (i = 0; i < size / sizeof(size_t); i++)
131 				assert(p[i] == 0);
132 		}
133 	}
134 	return (ret);
135 }
136 
137 /*
138  * If the caller specifies (*zero == false), it is still possible to receive
139  * zeroed memory, in which case *zero is toggled to true.  arena_chunk_alloc()
140  * takes advantage of this to avoid demanding zeroed chunks, but taking
141  * advantage of them if they are returned.
142  */
143 static void *
chunk_alloc_core(size_t size,size_t alignment,bool base,bool * zero,dss_prec_t dss_prec)144 chunk_alloc_core(size_t size, size_t alignment, bool base, bool *zero,
145     dss_prec_t dss_prec)
146 {
147 	void *ret;
148 
149 	assert(size != 0);
150 	assert((size & chunksize_mask) == 0);
151 	assert(alignment != 0);
152 	assert((alignment & chunksize_mask) == 0);
153 
154 	/* "primary" dss. */
155 	if (have_dss && dss_prec == dss_prec_primary) {
156 		if ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss, size,
157 		    alignment, base, zero)) != NULL)
158 			return (ret);
159 		if ((ret = chunk_alloc_dss(size, alignment, zero)) != NULL)
160 			return (ret);
161 	}
162 	/* mmap. */
163 	if ((ret = chunk_recycle(&chunks_szad_mmap, &chunks_ad_mmap, size,
164 	    alignment, base, zero)) != NULL)
165 		return (ret);
166 	if ((ret = chunk_alloc_mmap(size, alignment, zero)) != NULL)
167 		return (ret);
168 	/* "secondary" dss. */
169 	if (have_dss && dss_prec == dss_prec_secondary) {
170 		if ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss, size,
171 		    alignment, base, zero)) != NULL)
172 			return (ret);
173 		if ((ret = chunk_alloc_dss(size, alignment, zero)) != NULL)
174 			return (ret);
175 	}
176 
177 	/* All strategies for allocation failed. */
178 	return (NULL);
179 }
180 
181 static bool
chunk_register(void * chunk,size_t size,bool base)182 chunk_register(void *chunk, size_t size, bool base)
183 {
184 
185 	assert(chunk != NULL);
186 	assert(CHUNK_ADDR2BASE(chunk) == chunk);
187 
188 	if (config_ivsalloc && base == false) {
189 		if (rtree_set(chunks_rtree, (uintptr_t)chunk, 1))
190 			return (true);
191 	}
192 	if (config_stats || config_prof) {
193 		bool gdump;
194 		malloc_mutex_lock(&chunks_mtx);
195 		if (config_stats)
196 			stats_chunks.nchunks += (size / chunksize);
197 		stats_chunks.curchunks += (size / chunksize);
198 		if (stats_chunks.curchunks > stats_chunks.highchunks) {
199 			stats_chunks.highchunks =
200 			    stats_chunks.curchunks;
201 			if (config_prof)
202 				gdump = true;
203 		} else if (config_prof)
204 			gdump = false;
205 		malloc_mutex_unlock(&chunks_mtx);
206 		if (config_prof && opt_prof && opt_prof_gdump && gdump)
207 			prof_gdump();
208 	}
209 	if (config_valgrind)
210 		JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(chunk, size);
211 	return (false);
212 }
213 
214 void *
chunk_alloc_base(size_t size)215 chunk_alloc_base(size_t size)
216 {
217 	void *ret;
218 	bool zero;
219 
220 	zero = false;
221 	ret = chunk_alloc_core(size, chunksize, true, &zero,
222 	    chunk_dss_prec_get());
223 	if (ret == NULL)
224 		return (NULL);
225 	if (chunk_register(ret, size, true)) {
226 		chunk_dalloc_core(ret, size);
227 		return (NULL);
228 	}
229 	return (ret);
230 }
231 
232 void *
chunk_alloc_arena(chunk_alloc_t * chunk_alloc,chunk_dalloc_t * chunk_dalloc,unsigned arena_ind,size_t size,size_t alignment,bool * zero)233 chunk_alloc_arena(chunk_alloc_t *chunk_alloc, chunk_dalloc_t *chunk_dalloc,
234     unsigned arena_ind, size_t size, size_t alignment, bool *zero)
235 {
236 	void *ret;
237 
238 	ret = chunk_alloc(size, alignment, zero, arena_ind);
239 	if (ret != NULL && chunk_register(ret, size, false)) {
240 		chunk_dalloc(ret, size, arena_ind);
241 		ret = NULL;
242 	}
243 
244 	return (ret);
245 }
246 
247 /* Default arena chunk allocation routine in the absence of user override. */
248 void *
chunk_alloc_default(size_t size,size_t alignment,bool * zero,unsigned arena_ind)249 chunk_alloc_default(size_t size, size_t alignment, bool *zero,
250     unsigned arena_ind)
251 {
252 
253 	return (chunk_alloc_core(size, alignment, false, zero,
254 	    arenas[arena_ind]->dss_prec));
255 }
256 
257 static void
chunk_record(extent_tree_t * chunks_szad,extent_tree_t * chunks_ad,void * chunk,size_t size)258 chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
259     size_t size)
260 {
261 	bool unzeroed;
262 	extent_node_t *xnode, *node, *prev, *xprev, key;
263 
264 	unzeroed = pages_purge(chunk, size);
265 	JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size);
266 
267 	/*
268 	 * Allocate a node before acquiring chunks_mtx even though it might not
269 	 * be needed, because base_node_alloc() may cause a new base chunk to
270 	 * be allocated, which could cause deadlock if chunks_mtx were already
271 	 * held.
272 	 */
273 	xnode = base_node_alloc();
274 	/* Use xprev to implement conditional deferred deallocation of prev. */
275 	xprev = NULL;
276 
277 	malloc_mutex_lock(&chunks_mtx);
278 	key.addr = (void *)((uintptr_t)chunk + size);
279 	node = extent_tree_ad_nsearch(chunks_ad, &key);
280 	/* Try to coalesce forward. */
281 	if (node != NULL && node->addr == key.addr) {
282 		/*
283 		 * Coalesce chunk with the following address range.  This does
284 		 * not change the position within chunks_ad, so only
285 		 * remove/insert from/into chunks_szad.
286 		 */
287 		extent_tree_szad_remove(chunks_szad, node);
288 		node->addr = chunk;
289 		node->size += size;
290 		node->zeroed = (node->zeroed && (unzeroed == false));
291 		extent_tree_szad_insert(chunks_szad, node);
292 	} else {
293 		/* Coalescing forward failed, so insert a new node. */
294 		if (xnode == NULL) {
295 			/*
296 			 * base_node_alloc() failed, which is an exceedingly
297 			 * unlikely failure.  Leak chunk; its pages have
298 			 * already been purged, so this is only a virtual
299 			 * memory leak.
300 			 */
301 			goto label_return;
302 		}
303 		node = xnode;
304 		xnode = NULL; /* Prevent deallocation below. */
305 		node->addr = chunk;
306 		node->size = size;
307 		node->zeroed = (unzeroed == false);
308 		extent_tree_ad_insert(chunks_ad, node);
309 		extent_tree_szad_insert(chunks_szad, node);
310 	}
311 
312 	/* Try to coalesce backward. */
313 	prev = extent_tree_ad_prev(chunks_ad, node);
314 	if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
315 	    chunk) {
316 		/*
317 		 * Coalesce chunk with the previous address range.  This does
318 		 * not change the position within chunks_ad, so only
319 		 * remove/insert node from/into chunks_szad.
320 		 */
321 		extent_tree_szad_remove(chunks_szad, prev);
322 		extent_tree_ad_remove(chunks_ad, prev);
323 
324 		extent_tree_szad_remove(chunks_szad, node);
325 		node->addr = prev->addr;
326 		node->size += prev->size;
327 		node->zeroed = (node->zeroed && prev->zeroed);
328 		extent_tree_szad_insert(chunks_szad, node);
329 
330 		xprev = prev;
331 	}
332 
333 label_return:
334 	malloc_mutex_unlock(&chunks_mtx);
335 	/*
336 	 * Deallocate xnode and/or xprev after unlocking chunks_mtx in order to
337 	 * avoid potential deadlock.
338 	 */
339 	if (xnode != NULL)
340 		base_node_dalloc(xnode);
341 	if (xprev != NULL)
342 		base_node_dalloc(xprev);
343 }
344 
345 void
chunk_unmap(void * chunk,size_t size)346 chunk_unmap(void *chunk, size_t size)
347 {
348 	assert(chunk != NULL);
349 	assert(CHUNK_ADDR2BASE(chunk) == chunk);
350 	assert(size != 0);
351 	assert((size & chunksize_mask) == 0);
352 
353 	if (have_dss && chunk_in_dss(chunk))
354 		chunk_record(&chunks_szad_dss, &chunks_ad_dss, chunk, size);
355 	else if (chunk_dalloc_mmap(chunk, size))
356 		chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size);
357 }
358 
359 static void
chunk_dalloc_core(void * chunk,size_t size)360 chunk_dalloc_core(void *chunk, size_t size)
361 {
362 
363 	assert(chunk != NULL);
364 	assert(CHUNK_ADDR2BASE(chunk) == chunk);
365 	assert(size != 0);
366 	assert((size & chunksize_mask) == 0);
367 
368 	if (config_ivsalloc)
369 		rtree_set(chunks_rtree, (uintptr_t)chunk, 0);
370 	if (config_stats || config_prof) {
371 		malloc_mutex_lock(&chunks_mtx);
372 		assert(stats_chunks.curchunks >= (size / chunksize));
373 		stats_chunks.curchunks -= (size / chunksize);
374 		malloc_mutex_unlock(&chunks_mtx);
375 	}
376 
377 	chunk_unmap(chunk, size);
378 }
379 
380 /* Default arena chunk deallocation routine in the absence of user override. */
381 bool
chunk_dalloc_default(void * chunk,size_t size,unsigned arena_ind)382 chunk_dalloc_default(void *chunk, size_t size, unsigned arena_ind)
383 {
384 
385 	chunk_dalloc_core(chunk, size);
386 	return (false);
387 }
388 
389 bool
chunk_boot(void)390 chunk_boot(void)
391 {
392 
393 	/* Set variables according to the value of opt_lg_chunk. */
394 	chunksize = (ZU(1) << opt_lg_chunk);
395 	assert(chunksize >= PAGE);
396 	chunksize_mask = chunksize - 1;
397 	chunk_npages = (chunksize >> LG_PAGE);
398 
399 	if (config_stats || config_prof) {
400 		if (malloc_mutex_init(&chunks_mtx))
401 			return (true);
402 		memset(&stats_chunks, 0, sizeof(chunk_stats_t));
403 	}
404 	if (have_dss && chunk_dss_boot())
405 		return (true);
406 	extent_tree_szad_new(&chunks_szad_mmap);
407 	extent_tree_ad_new(&chunks_ad_mmap);
408 	extent_tree_szad_new(&chunks_szad_dss);
409 	extent_tree_ad_new(&chunks_ad_dss);
410 	if (config_ivsalloc) {
411 		chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) -
412 		    opt_lg_chunk, base_alloc, NULL);
413 		if (chunks_rtree == NULL)
414 			return (true);
415 	}
416 
417 	return (false);
418 }
419 
420 void
chunk_prefork(void)421 chunk_prefork(void)
422 {
423 
424 	malloc_mutex_prefork(&chunks_mtx);
425 	if (config_ivsalloc)
426 		rtree_prefork(chunks_rtree);
427 	chunk_dss_prefork();
428 }
429 
430 void
chunk_postfork_parent(void)431 chunk_postfork_parent(void)
432 {
433 
434 	chunk_dss_postfork_parent();
435 	if (config_ivsalloc)
436 		rtree_postfork_parent(chunks_rtree);
437 	malloc_mutex_postfork_parent(&chunks_mtx);
438 }
439 
440 void
chunk_postfork_child(void)441 chunk_postfork_child(void)
442 {
443 
444 	chunk_dss_postfork_child();
445 	if (config_ivsalloc)
446 		rtree_postfork_child(chunks_rtree);
447 	malloc_mutex_postfork_child(&chunks_mtx);
448 }
449