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