huge.c revision 1036ddbf11b7e9ec566b92b3dd50e105fc5f6932
1#define	JEMALLOC_HUGE_C_
2#include "jemalloc/internal/jemalloc_internal.h"
3
4/******************************************************************************/
5/* Data. */
6
7/* Protects chunk-related data structures. */
8static malloc_mutex_t	huge_mtx;
9
10/******************************************************************************/
11
12/* Tree of chunks that are stand-alone huge allocations. */
13static extent_tree_t	huge;
14
15void *
16huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero, bool try_tcache)
17{
18	size_t usize;
19
20	usize = s2u(size);
21	if (usize == 0) {
22		/* size_t overflow. */
23		return (NULL);
24	}
25
26	return (huge_palloc(tsd, arena, usize, chunksize, zero, try_tcache));
27}
28
29void *
30huge_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
31    bool zero, bool try_tcache)
32{
33	void *ret;
34	extent_node_t *node;
35	bool is_zeroed;
36
37	/* Allocate one or more contiguous chunks for this request. */
38
39	/* Allocate an extent node with which to track the chunk. */
40	node = ipalloct(tsd, CACHELINE_CEILING(sizeof(extent_node_t)),
41	    CACHELINE, false, try_tcache, NULL);
42	if (node == NULL)
43		return (NULL);
44
45	/*
46	 * Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that
47	 * it is possible to make correct junk/zero fill decisions below.
48	 */
49	is_zeroed = zero;
50	arena = arena_choose(tsd, arena);
51	if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(arena,
52	    usize, alignment, &is_zeroed)) == NULL) {
53		idalloct(tsd, node, try_tcache);
54		return (NULL);
55	}
56
57	/* Insert node into huge. */
58	node->addr = ret;
59	node->size = usize;
60	node->zeroed = is_zeroed;
61	node->arena = arena;
62
63	malloc_mutex_lock(&huge_mtx);
64	extent_tree_ad_insert(&huge, node);
65	malloc_mutex_unlock(&huge_mtx);
66
67	if (zero || (config_fill && unlikely(opt_zero))) {
68		if (!is_zeroed)
69			memset(ret, 0, usize);
70	} else if (config_fill && unlikely(opt_junk))
71		memset(ret, 0xa5, usize);
72
73	return (ret);
74}
75
76#ifdef JEMALLOC_JET
77#undef huge_dalloc_junk
78#define	huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk_impl)
79#endif
80static void
81huge_dalloc_junk(void *ptr, size_t usize)
82{
83
84	if (config_fill && have_dss && unlikely(opt_junk)) {
85		/*
86		 * Only bother junk filling if the chunk isn't about to be
87		 * unmapped.
88		 */
89		if (!config_munmap || (have_dss && chunk_in_dss(ptr)))
90			memset(ptr, 0x5a, usize);
91	}
92}
93#ifdef JEMALLOC_JET
94#undef huge_dalloc_junk
95#define	huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk)
96huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl);
97#endif
98
99static void
100huge_ralloc_no_move_similar(void *ptr, size_t oldsize, size_t usize,
101    size_t size, size_t extra, bool zero)
102{
103	size_t usize_next;
104	bool zeroed;
105	extent_node_t *node, key;
106	arena_t *arena;
107
108	/* Increase usize to incorporate extra. */
109	while (usize < s2u(size+extra) && (usize_next = s2u(usize+1)) < oldsize)
110		usize = usize_next;
111
112	if (oldsize == usize)
113		return;
114
115	/* Fill if necessary (shrinking). */
116	if (oldsize > usize) {
117		size_t sdiff = CHUNK_CEILING(usize) - usize;
118		zeroed = (sdiff != 0) ? !pages_purge((void *)((uintptr_t)ptr +
119		    usize), sdiff) : true;
120		if (config_fill && unlikely(opt_junk)) {
121			memset((void *)((uintptr_t)ptr + usize), 0x5a, oldsize -
122			    usize);
123			zeroed = false;
124		}
125	} else
126		zeroed = true;
127
128	malloc_mutex_lock(&huge_mtx);
129	key.addr = ptr;
130	node = extent_tree_ad_search(&huge, &key);
131	assert(node != NULL);
132	assert(node->addr == ptr);
133	arena = node->arena;
134	/* Update the size of the huge allocation. */
135	assert(node->size != usize);
136	node->size = usize;
137	/* Clear node->zeroed if zeroing failed above. */
138	node->zeroed = (node->zeroed && zeroed);
139	malloc_mutex_unlock(&huge_mtx);
140
141	arena_chunk_ralloc_huge_similar(arena, ptr, oldsize, usize);
142
143	/* Fill if necessary (growing). */
144	if (oldsize < usize) {
145		if (zero || (config_fill && unlikely(opt_zero))) {
146			if (!zeroed) {
147				memset((void *)((uintptr_t)ptr + oldsize), 0,
148				    usize - oldsize);
149			}
150		} else if (config_fill && unlikely(opt_junk)) {
151			memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize -
152			    oldsize);
153		}
154	}
155}
156
157static void
158huge_ralloc_no_move_shrink(void *ptr, size_t oldsize, size_t usize)
159{
160	size_t sdiff;
161	bool zeroed;
162	extent_node_t *node, key;
163	arena_t *arena;
164
165	sdiff = CHUNK_CEILING(usize) - usize;
166	zeroed = (sdiff != 0) ? !pages_purge((void *)((uintptr_t)ptr + usize),
167	    sdiff) : true;
168	if (config_fill && unlikely(opt_junk)) {
169		huge_dalloc_junk((void *)((uintptr_t)ptr + usize), oldsize -
170		    usize);
171		zeroed = false;
172	}
173
174	malloc_mutex_lock(&huge_mtx);
175	key.addr = ptr;
176	node = extent_tree_ad_search(&huge, &key);
177	assert(node != NULL);
178	assert(node->addr == ptr);
179	arena = node->arena;
180	/* Update the size of the huge allocation. */
181	node->size = usize;
182	/* Clear node->zeroed if zeroing failed above. */
183	node->zeroed = (node->zeroed && zeroed);
184	malloc_mutex_unlock(&huge_mtx);
185
186	/* Zap the excess chunks. */
187	arena_chunk_ralloc_huge_shrink(arena, ptr, oldsize, usize);
188}
189
190static bool
191huge_ralloc_no_move_expand(void *ptr, size_t oldsize, size_t size, bool zero) {
192	size_t usize;
193	extent_node_t *node, key;
194	arena_t *arena;
195	bool is_zeroed_subchunk, is_zeroed_chunk;
196
197	usize = s2u(size);
198	if (usize == 0) {
199		/* size_t overflow. */
200		return (true);
201	}
202
203	malloc_mutex_lock(&huge_mtx);
204	key.addr = ptr;
205	node = extent_tree_ad_search(&huge, &key);
206	assert(node != NULL);
207	assert(node->addr == ptr);
208	arena = node->arena;
209	is_zeroed_subchunk = node->zeroed;
210	malloc_mutex_unlock(&huge_mtx);
211
212	/*
213	 * Copy zero into is_zeroed_chunk and pass the copy to chunk_alloc(), so
214	 * that it is possible to make correct junk/zero fill decisions below.
215	 */
216	is_zeroed_chunk = zero;
217
218	if (arena_chunk_ralloc_huge_expand(arena, ptr, oldsize, usize,
219	     &is_zeroed_chunk))
220		return (true);
221
222	malloc_mutex_lock(&huge_mtx);
223	/* Update the size of the huge allocation. */
224	node->size = usize;
225	malloc_mutex_unlock(&huge_mtx);
226
227	if (zero || (config_fill && unlikely(opt_zero))) {
228		if (!is_zeroed_subchunk) {
229			memset((void *)((uintptr_t)ptr + oldsize), 0,
230			    CHUNK_CEILING(oldsize) - oldsize);
231		}
232		if (!is_zeroed_chunk) {
233			memset((void *)((uintptr_t)ptr +
234			    CHUNK_CEILING(oldsize)), 0, usize -
235			    CHUNK_CEILING(oldsize));
236		}
237	} else if (config_fill && unlikely(opt_junk)) {
238		memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize -
239		    oldsize);
240	}
241
242	return (false);
243}
244
245bool
246huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra,
247    bool zero)
248{
249	size_t usize;
250
251	/* Both allocations must be huge to avoid a move. */
252	if (oldsize < chunksize)
253		return (true);
254
255	assert(s2u(oldsize) == oldsize);
256	usize = s2u(size);
257	if (usize == 0) {
258		/* size_t overflow. */
259		return (true);
260	}
261
262	/*
263	 * Avoid moving the allocation if the existing chunk size accommodates
264	 * the new size.
265	 */
266	if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize)
267	    && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) {
268		huge_ralloc_no_move_similar(ptr, oldsize, usize, size, extra,
269		    zero);
270		return (false);
271	}
272
273	/* Shrink the allocation in-place. */
274	if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize)) {
275		huge_ralloc_no_move_shrink(ptr, oldsize, usize);
276		return (false);
277	}
278
279	/* Attempt to expand the allocation in-place. */
280	if (huge_ralloc_no_move_expand(ptr, oldsize, size + extra,
281	    zero)) {
282		if (extra == 0)
283			return (true);
284
285		/* Try again, this time without extra. */
286		return (huge_ralloc_no_move_expand(ptr, oldsize, size, zero));
287	}
288	return (false);
289}
290
291void *
292huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t size,
293    size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,
294    bool try_tcache_dalloc)
295{
296	void *ret;
297	size_t copysize;
298
299	/* Try to avoid moving the allocation. */
300	if (!huge_ralloc_no_move(ptr, oldsize, size, extra, zero))
301		return (ptr);
302
303	/*
304	 * size and oldsize are different enough that we need to use a
305	 * different size class.  In that case, fall back to allocating new
306	 * space and copying.
307	 */
308	if (alignment > chunksize) {
309		ret = huge_palloc(tsd, arena, size + extra, alignment, zero,
310		    try_tcache_alloc);
311	} else {
312		ret = huge_malloc(tsd, arena, size + extra, zero,
313		    try_tcache_alloc);
314	}
315
316	if (ret == NULL) {
317		if (extra == 0)
318			return (NULL);
319		/* Try again, this time without extra. */
320		if (alignment > chunksize) {
321			ret = huge_palloc(tsd, arena, size, alignment, zero,
322			    try_tcache_alloc);
323		} else {
324			ret = huge_malloc(tsd, arena, size, zero,
325			    try_tcache_alloc);
326		}
327
328		if (ret == NULL)
329			return (NULL);
330	}
331
332	/*
333	 * Copy at most size bytes (not size+extra), since the caller has no
334	 * expectation that the extra bytes will be reliably preserved.
335	 */
336	copysize = (size < oldsize) ? size : oldsize;
337	memcpy(ret, ptr, copysize);
338	isqalloc(tsd, ptr, oldsize, try_tcache_dalloc);
339	return (ret);
340}
341
342void
343huge_dalloc(tsd_t *tsd, void *ptr, bool try_tcache)
344{
345	extent_node_t *node, key;
346
347	malloc_mutex_lock(&huge_mtx);
348	/* Extract from tree of huge allocations. */
349	key.addr = ptr;
350	node = extent_tree_ad_search(&huge, &key);
351	assert(node != NULL);
352	assert(node->addr == ptr);
353	extent_tree_ad_remove(&huge, node);
354	malloc_mutex_unlock(&huge_mtx);
355
356	huge_dalloc_junk(node->addr, node->size);
357	arena_chunk_dalloc_huge(node->arena, node->addr, node->size);
358	idalloct(tsd, node, try_tcache);
359}
360
361size_t
362huge_salloc(const void *ptr)
363{
364	size_t ret;
365	extent_node_t *node, key;
366
367	malloc_mutex_lock(&huge_mtx);
368
369	/* Extract from tree of huge allocations. */
370	key.addr = __DECONST(void *, ptr);
371	node = extent_tree_ad_search(&huge, &key);
372	assert(node != NULL);
373
374	ret = node->size;
375
376	malloc_mutex_unlock(&huge_mtx);
377
378	return (ret);
379}
380
381prof_tctx_t *
382huge_prof_tctx_get(const void *ptr)
383{
384	prof_tctx_t *ret;
385	extent_node_t *node, key;
386
387	malloc_mutex_lock(&huge_mtx);
388
389	/* Extract from tree of huge allocations. */
390	key.addr = __DECONST(void *, ptr);
391	node = extent_tree_ad_search(&huge, &key);
392	assert(node != NULL);
393
394	ret = node->prof_tctx;
395
396	malloc_mutex_unlock(&huge_mtx);
397
398	return (ret);
399}
400
401void
402huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx)
403{
404	extent_node_t *node, key;
405
406	malloc_mutex_lock(&huge_mtx);
407
408	/* Extract from tree of huge allocations. */
409	key.addr = __DECONST(void *, ptr);
410	node = extent_tree_ad_search(&huge, &key);
411	assert(node != NULL);
412
413	node->prof_tctx = tctx;
414
415	malloc_mutex_unlock(&huge_mtx);
416}
417
418bool
419huge_boot(void)
420{
421
422	/* Initialize chunks data. */
423	if (malloc_mutex_init(&huge_mtx))
424		return (true);
425	extent_tree_ad_new(&huge);
426
427	return (false);
428}
429
430void
431huge_prefork(void)
432{
433
434	malloc_mutex_prefork(&huge_mtx);
435}
436
437void
438huge_postfork_parent(void)
439{
440
441	malloc_mutex_postfork_parent(&huge_mtx);
442}
443
444void
445huge_postfork_child(void)
446{
447
448	malloc_mutex_postfork_child(&huge_mtx);
449}
450