huge.c revision fb1f094f163a3bf15d8958ba845e83559c0e6dfe
1#define	JEMALLOC_HUGE_C_
2#include "jemalloc/internal/jemalloc_internal.h"
3
4/******************************************************************************/
5
6static extent_node_t *
7huge_node_get(const void *ptr)
8{
9	extent_node_t *node;
10
11	node = chunk_lookup(ptr, true);
12	assert(!extent_node_achunk_get(node));
13
14	return (node);
15}
16
17static bool
18huge_node_set(tsdn_t *tsdn, const void *ptr, extent_node_t *node)
19{
20
21	assert(extent_node_addr_get(node) == ptr);
22	assert(!extent_node_achunk_get(node));
23	return (chunk_register(tsdn, ptr, node));
24}
25
26static void
27huge_node_reset(tsdn_t *tsdn, const void *ptr, extent_node_t *node)
28{
29	bool err;
30
31	err = huge_node_set(tsdn, ptr, node);
32	assert(!err);
33}
34
35static void
36huge_node_unset(const void *ptr, const extent_node_t *node)
37{
38
39	chunk_deregister(ptr, node);
40}
41
42void *
43huge_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero)
44{
45
46	assert(usize == s2u(usize));
47
48	return (huge_palloc(tsdn, arena, usize, chunksize, zero));
49}
50
51void *
52huge_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
53    bool zero)
54{
55	void *ret;
56	size_t ausize;
57	arena_t *iarena;
58	extent_node_t *node;
59	bool is_zeroed;
60
61	/* Allocate one or more contiguous chunks for this request. */
62
63	assert(!tsdn_null(tsdn) || arena != NULL);
64
65	ausize = sa2u(usize, alignment);
66	if (unlikely(ausize == 0 || ausize > HUGE_MAXCLASS))
67		return (NULL);
68	assert(ausize >= chunksize);
69
70	/* Allocate an extent node with which to track the chunk. */
71	iarena = (!tsdn_null(tsdn)) ? arena_ichoose(tsdn_tsd(tsdn), NULL) : a0get();
72	node = ipallocztm(tsdn, CACHELINE_CEILING(sizeof(extent_node_t)),
73	    CACHELINE, false, NULL, true, iarena);
74	if (node == NULL)
75		return (NULL);
76
77	/*
78	 * Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that
79	 * it is possible to make correct junk/zero fill decisions below.
80	 */
81	is_zeroed = zero;
82	/* ANDROID change */
83	if (likely(!tsdn_null(tsdn))) {
84#if !defined(__LP64__)
85		/* On 32 bit systems, using a per arena cache can exhaust
86		 * virtual address space. Force all huge allocations to
87		 * always take place in the first arena.
88		 */
89		extern arena_t *a0get(void);
90		arena = a0get();
91#else
92		arena = arena_choose(tsdn_tsd(tsdn), arena);
93#endif
94	}
95	/* End ANDROID change */
96	if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(tsdn,
97	    arena, usize, alignment, &is_zeroed)) == NULL) {
98		idalloctm(tsdn, node, NULL, true, true);
99		return (NULL);
100	}
101
102	extent_node_init(node, arena, ret, usize, is_zeroed, true);
103
104	if (huge_node_set(tsdn, ret, node)) {
105		arena_chunk_dalloc_huge(tsdn, arena, ret, usize);
106		idalloctm(tsdn, node, NULL, true, true);
107		return (NULL);
108	}
109
110	/* Insert node into huge. */
111	malloc_mutex_lock(tsdn, &arena->huge_mtx);
112	ql_elm_new(node, ql_link);
113	ql_tail_insert(&arena->huge, node, ql_link);
114	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
115
116	if (zero || (config_fill && unlikely(opt_zero))) {
117		if (!is_zeroed)
118			memset(ret, 0, usize);
119	} else if (config_fill && unlikely(opt_junk_alloc))
120		memset(ret, JEMALLOC_ALLOC_JUNK, usize);
121
122	arena_decay_tick(tsdn, arena);
123	return (ret);
124}
125
126#ifdef JEMALLOC_JET
127#undef huge_dalloc_junk
128#define	huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk_impl)
129#endif
130static void
131huge_dalloc_junk(void *ptr, size_t usize)
132{
133
134	if (config_fill && have_dss && unlikely(opt_junk_free)) {
135		/*
136		 * Only bother junk filling if the chunk isn't about to be
137		 * unmapped.
138		 */
139		if (!config_munmap || (have_dss && chunk_in_dss(ptr)))
140			memset(ptr, JEMALLOC_FREE_JUNK, usize);
141	}
142}
143#ifdef JEMALLOC_JET
144#undef huge_dalloc_junk
145#define	huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk)
146huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl);
147#endif
148
149static void
150huge_ralloc_no_move_similar(tsdn_t *tsdn, void *ptr, size_t oldsize,
151    size_t usize_min, size_t usize_max, bool zero)
152{
153	size_t usize, usize_next;
154	extent_node_t *node;
155	arena_t *arena;
156	chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
157	bool pre_zeroed, post_zeroed;
158
159	/* Increase usize to incorporate extra. */
160	for (usize = usize_min; usize < usize_max && (usize_next = s2u(usize+1))
161	    <= oldsize; usize = usize_next)
162		; /* Do nothing. */
163
164	if (oldsize == usize)
165		return;
166
167	node = huge_node_get(ptr);
168	arena = extent_node_arena_get(node);
169	pre_zeroed = extent_node_zeroed_get(node);
170
171	/* Fill if necessary (shrinking). */
172	if (oldsize > usize) {
173		size_t sdiff = oldsize - usize;
174		if (config_fill && unlikely(opt_junk_free)) {
175			memset((void *)((uintptr_t)ptr + usize),
176			    JEMALLOC_FREE_JUNK, sdiff);
177			post_zeroed = false;
178		} else {
179			post_zeroed = !chunk_purge_wrapper(tsdn, arena,
180			    &chunk_hooks, ptr, CHUNK_CEILING(oldsize), usize,
181			    sdiff);
182		}
183	} else
184		post_zeroed = pre_zeroed;
185
186	malloc_mutex_lock(tsdn, &arena->huge_mtx);
187	/* Update the size of the huge allocation. */
188	huge_node_unset(ptr, node);
189	assert(extent_node_size_get(node) != usize);
190	extent_node_size_set(node, usize);
191	huge_node_reset(tsdn, ptr, node);
192	/* Update zeroed. */
193	extent_node_zeroed_set(node, post_zeroed);
194	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
195
196	arena_chunk_ralloc_huge_similar(tsdn, arena, ptr, oldsize, usize);
197
198	/* Fill if necessary (growing). */
199	if (oldsize < usize) {
200		if (zero || (config_fill && unlikely(opt_zero))) {
201			if (!pre_zeroed) {
202				memset((void *)((uintptr_t)ptr + oldsize), 0,
203				    usize - oldsize);
204			}
205		} else if (config_fill && unlikely(opt_junk_alloc)) {
206			memset((void *)((uintptr_t)ptr + oldsize),
207			    JEMALLOC_ALLOC_JUNK, usize - oldsize);
208		}
209	}
210}
211
212static bool
213huge_ralloc_no_move_shrink(tsdn_t *tsdn, void *ptr, size_t oldsize,
214    size_t usize)
215{
216	extent_node_t *node;
217	arena_t *arena;
218	chunk_hooks_t chunk_hooks;
219	size_t cdiff;
220	bool pre_zeroed, post_zeroed;
221
222	node = huge_node_get(ptr);
223	arena = extent_node_arena_get(node);
224	pre_zeroed = extent_node_zeroed_get(node);
225	chunk_hooks = chunk_hooks_get(tsdn, arena);
226
227	assert(oldsize > usize);
228
229	/* Split excess chunks. */
230	cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize);
231	if (cdiff != 0 && chunk_hooks.split(ptr, CHUNK_CEILING(oldsize),
232	    CHUNK_CEILING(usize), cdiff, true, arena->ind))
233		return (true);
234
235	if (oldsize > usize) {
236		size_t sdiff = oldsize - usize;
237		if (config_fill && unlikely(opt_junk_free)) {
238			huge_dalloc_junk((void *)((uintptr_t)ptr + usize),
239			    sdiff);
240			post_zeroed = false;
241		} else {
242			post_zeroed = !chunk_purge_wrapper(tsdn, arena,
243			    &chunk_hooks, CHUNK_ADDR2BASE((uintptr_t)ptr +
244			    usize), CHUNK_CEILING(oldsize),
245			    CHUNK_ADDR2OFFSET((uintptr_t)ptr + usize), sdiff);
246		}
247	} else
248		post_zeroed = pre_zeroed;
249
250	malloc_mutex_lock(tsdn, &arena->huge_mtx);
251	/* Update the size of the huge allocation. */
252	huge_node_unset(ptr, node);
253	extent_node_size_set(node, usize);
254	huge_node_reset(tsdn, ptr, node);
255	/* Update zeroed. */
256	extent_node_zeroed_set(node, post_zeroed);
257	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
258
259	/* Zap the excess chunks. */
260	arena_chunk_ralloc_huge_shrink(tsdn, arena, ptr, oldsize, usize);
261
262	return (false);
263}
264
265static bool
266huge_ralloc_no_move_expand(tsdn_t *tsdn, void *ptr, size_t oldsize,
267    size_t usize, bool zero) {
268	extent_node_t *node;
269	arena_t *arena;
270	bool is_zeroed_subchunk, is_zeroed_chunk;
271
272	node = huge_node_get(ptr);
273	arena = extent_node_arena_get(node);
274	malloc_mutex_lock(tsdn, &arena->huge_mtx);
275	is_zeroed_subchunk = extent_node_zeroed_get(node);
276	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
277
278	/*
279	 * Use is_zeroed_chunk to detect whether the trailing memory is zeroed,
280	 * update extent's zeroed field, and zero as necessary.
281	 */
282	is_zeroed_chunk = false;
283	if (arena_chunk_ralloc_huge_expand(tsdn, arena, ptr, oldsize, usize,
284	     &is_zeroed_chunk))
285		return (true);
286
287	malloc_mutex_lock(tsdn, &arena->huge_mtx);
288	huge_node_unset(ptr, node);
289	extent_node_size_set(node, usize);
290	extent_node_zeroed_set(node, extent_node_zeroed_get(node) &&
291	    is_zeroed_chunk);
292	huge_node_reset(tsdn, ptr, node);
293	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
294
295	if (zero || (config_fill && unlikely(opt_zero))) {
296		if (!is_zeroed_subchunk) {
297			memset((void *)((uintptr_t)ptr + oldsize), 0,
298			    CHUNK_CEILING(oldsize) - oldsize);
299		}
300		if (!is_zeroed_chunk) {
301			memset((void *)((uintptr_t)ptr +
302			    CHUNK_CEILING(oldsize)), 0, usize -
303			    CHUNK_CEILING(oldsize));
304		}
305	} else if (config_fill && unlikely(opt_junk_alloc)) {
306		memset((void *)((uintptr_t)ptr + oldsize), JEMALLOC_ALLOC_JUNK,
307		    usize - oldsize);
308	}
309
310	return (false);
311}
312
313bool
314huge_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t usize_min,
315    size_t usize_max, bool zero)
316{
317
318	assert(s2u(oldsize) == oldsize);
319	/* The following should have been caught by callers. */
320	assert(usize_min > 0 && usize_max <= HUGE_MAXCLASS);
321
322	/* Both allocations must be huge to avoid a move. */
323	if (oldsize < chunksize || usize_max < chunksize)
324		return (true);
325
326	if (CHUNK_CEILING(usize_max) > CHUNK_CEILING(oldsize)) {
327		/* Attempt to expand the allocation in-place. */
328		if (!huge_ralloc_no_move_expand(tsdn, ptr, oldsize, usize_max,
329		    zero)) {
330			arena_decay_tick(tsdn, huge_aalloc(ptr));
331			return (false);
332		}
333		/* Try again, this time with usize_min. */
334		if (usize_min < usize_max && CHUNK_CEILING(usize_min) >
335		    CHUNK_CEILING(oldsize) && huge_ralloc_no_move_expand(tsdn,
336		    ptr, oldsize, usize_min, zero)) {
337			arena_decay_tick(tsdn, huge_aalloc(ptr));
338			return (false);
339		}
340	}
341
342	/*
343	 * Avoid moving the allocation if the existing chunk size accommodates
344	 * the new size.
345	 */
346	if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize_min)
347	    && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(usize_max)) {
348		huge_ralloc_no_move_similar(tsdn, ptr, oldsize, usize_min,
349		    usize_max, zero);
350		arena_decay_tick(tsdn, huge_aalloc(ptr));
351		return (false);
352	}
353
354	/* Attempt to shrink the allocation in-place. */
355	if (CHUNK_CEILING(oldsize) > CHUNK_CEILING(usize_max)) {
356		if (!huge_ralloc_no_move_shrink(tsdn, ptr, oldsize,
357		    usize_max)) {
358			arena_decay_tick(tsdn, huge_aalloc(ptr));
359			return (false);
360		}
361	}
362	return (true);
363}
364
365static void *
366huge_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
367    size_t alignment, bool zero)
368{
369
370	if (alignment <= chunksize)
371		return (huge_malloc(tsdn, arena, usize, zero));
372	return (huge_palloc(tsdn, arena, usize, alignment, zero));
373}
374
375void *
376huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
377    size_t usize, size_t alignment, bool zero, tcache_t *tcache)
378{
379	void *ret;
380	size_t copysize;
381
382	/* The following should have been caught by callers. */
383	assert(usize > 0 && usize <= HUGE_MAXCLASS);
384
385	/* Try to avoid moving the allocation. */
386	if (!huge_ralloc_no_move(tsd_tsdn(tsd), ptr, oldsize, usize, usize,
387	    zero))
388		return (ptr);
389
390	/*
391	 * usize and oldsize are different enough that we need to use a
392	 * different size class.  In that case, fall back to allocating new
393	 * space and copying.
394	 */
395	ret = huge_ralloc_move_helper(tsd_tsdn(tsd), arena, usize, alignment,
396	    zero);
397	if (ret == NULL)
398		return (NULL);
399
400	copysize = (usize < oldsize) ? usize : oldsize;
401	memcpy(ret, ptr, copysize);
402	isqalloc(tsd, ptr, oldsize, tcache, true);
403	return (ret);
404}
405
406void
407huge_dalloc(tsdn_t *tsdn, void *ptr)
408{
409	extent_node_t *node;
410	arena_t *arena;
411
412	node = huge_node_get(ptr);
413	arena = extent_node_arena_get(node);
414	huge_node_unset(ptr, node);
415	malloc_mutex_lock(tsdn, &arena->huge_mtx);
416	ql_remove(&arena->huge, node, ql_link);
417	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
418
419	huge_dalloc_junk(extent_node_addr_get(node),
420	    extent_node_size_get(node));
421	arena_chunk_dalloc_huge(tsdn, extent_node_arena_get(node),
422	    extent_node_addr_get(node), extent_node_size_get(node));
423	idalloctm(tsdn, node, NULL, true, true);
424
425	arena_decay_tick(tsdn, arena);
426}
427
428arena_t *
429huge_aalloc(const void *ptr)
430{
431
432	return (extent_node_arena_get(huge_node_get(ptr)));
433}
434
435size_t
436huge_salloc(tsdn_t *tsdn, const void *ptr)
437{
438	size_t size;
439	extent_node_t *node;
440	arena_t *arena;
441
442	node = huge_node_get(ptr);
443	arena = extent_node_arena_get(node);
444	malloc_mutex_lock(tsdn, &arena->huge_mtx);
445	size = extent_node_size_get(node);
446	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
447
448	return (size);
449}
450
451prof_tctx_t *
452huge_prof_tctx_get(tsdn_t *tsdn, const void *ptr)
453{
454	prof_tctx_t *tctx;
455	extent_node_t *node;
456	arena_t *arena;
457
458	node = huge_node_get(ptr);
459	arena = extent_node_arena_get(node);
460	malloc_mutex_lock(tsdn, &arena->huge_mtx);
461	tctx = extent_node_prof_tctx_get(node);
462	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
463
464	return (tctx);
465}
466
467void
468huge_prof_tctx_set(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx)
469{
470	extent_node_t *node;
471	arena_t *arena;
472
473	node = huge_node_get(ptr);
474	arena = extent_node_arena_get(node);
475	malloc_mutex_lock(tsdn, &arena->huge_mtx);
476	extent_node_prof_tctx_set(node, tctx);
477	malloc_mutex_unlock(tsdn, &arena->huge_mtx);
478}
479
480void
481huge_prof_tctx_reset(tsdn_t *tsdn, const void *ptr)
482{
483
484	huge_prof_tctx_set(tsdn, ptr, (prof_tctx_t *)(uintptr_t)1U);
485}
486