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