2 * Copyright (c) 1999, 2006, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 /* Author: Bertrand Serlet, August 1999 */
27 Multithread enhancements for "tiny" allocations introduced February 2008.
28 These are in the spirit of "Hoard". See:
29 Berger, E.D.; McKinley, K.S.; Blumofe, R.D.; Wilson, P.R. (2000).
30 "Hoard: a scalable memory allocator for multithreaded applications".
31 ACM SIGPLAN Notices 35 (11): 117-128. Berger2000.
32 <http://portal.acm.org/citation.cfm?id=356989.357000>
33 Retrieved on 2008-02-22.
36 /* gcc -g -O3 magazine_malloc.c malloc.c -o libmagmalloc.dylib -I. \
37 -I/System/Library/Frameworks/System.framework/PrivateHeaders/ -funit-at-a-time \
38 -dynamiclib -Wall -arch x86_64 -arch i386 -arch ppc */
40 #include <TargetConditionals.h>
42 #include "scalable_malloc.h"
43 #include "malloc_printf.h"
45 #include "magmallocProvider.h"
47 #include <pthread_internals.h> /* for pthread_lock_t SPI */
48 #include <pthread.h> /* for pthread API */
52 #include <mach/vm_statistics.h>
53 #include <mach/mach_init.h>
54 #include <sys/types.h>
56 #include <sys/param.h>
58 #if defined(__i386__) || defined(__x86_64__)
59 #define __APPLE_API_PRIVATE
60 #include <machine/cpu_capabilities.h>
61 #define _COMM_PAGE_VERSION_REQD 9
62 #undef __APPLE_API_PRIVATE
64 #include <sys/sysctl.h>
67 #include <libkern/OSAtomic.h>
68 #include <mach-o/dyld.h> /* for NSVersionOfLinkTimeLibrary() */
69 #include <mach-o/dyld_priv.h> /* for _dyld_get_image_slide() */
70 #include <crt_externs.h> /* for _NSGetMachExecuteHeader() */
71 #include <mach/vm_param.h>
72 #include <sys/vmparam.h>
74 #include <CrashReporterClient.h>
76 /********************* DEFINITIONS ************************/
78 #define DEBUG_MALLOC 0 // set to one to debug malloc itself
80 #define DEBUG_CLIENT 0 // set to one to debug malloc client
82 #define DEBUG_MADVISE 0
84 // <rdar://problem/10397726>
85 #define RELAXED_INVARIANT_CHECKS 1
88 #warning DEBUG_MALLOC ENABLED
91 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) \
93 if (__is_threaded && TRY_LOCK(mag_ptr->magazine_lock)) { \
94 malloc_printf("*** magazine_lock was not set %p in %s\n", \
95 mag_ptr->magazine_lock, fun); \
99 # define INLINE __inline__
100 # define ALWAYSINLINE __attribute__((always_inline))
101 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) {}
104 # define NOINLINE __attribute__((noinline))
106 #if defined(__i386__) || defined(__x86_64__)
107 #define CACHE_ALIGN __attribute__ ((aligned (128) )) /* Future-proofing at 128B */
108 #elif defined(__ppc__) || defined(__ppc64__)
109 #define CACHE_ALIGN __attribute__ ((aligned (128) ))
115 #define ASLR_INTERNAL 1
119 * Access to global variables is slow, so optimise our handling of vm_page_size
122 #define _vm_page_size vm_page_size /* to get to the originals */
123 #define _vm_page_shift vm_page_shift
124 #define vm_page_size 4096 /* our normal working sizes */
125 #define vm_page_shift 12
128 * msize - a type to refer to the number of quanta of a tiny or small
129 * allocation. A tiny block with an msize of 3 would be 3 << SHIFT_TINY_QUANTUM
132 typedef unsigned short msize_t
;
144 typedef unsigned int grain_t
; // N.B. wide enough to index all free slots
146 typedef int mag_index_t
;
148 #define CHECK_REGIONS (1 << 31)
149 #define DISABLE_ASLR (1 << 30)
151 #define MAX_RECORDER_BUFFER 256
153 /********************* DEFINITIONS for tiny ************************/
156 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the
157 * szone's hashed_regions pointer.
159 * Each region is laid out as a heap, followed by a header block, all within
160 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header
161 * is 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
163 * The header block is arranged as in struct tiny_region defined just below, and
164 * consists of two bitfields (or bit arrays) interleaved 32 bits by 32 bits.
166 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding
167 * TINY_QUANTUM block within the heap.
169 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
170 * that the corresponding quantum is the first quantum in a block (either in use or free). The
171 * in-use bit is set for the header if the block has been handed out (allocated). If the header
172 * bit is not set, the in-use bit is invalid.
174 * The szone maintains an array of NUM_TINY_SLOTS freelists, each of which is used to hold
175 * free objects of the corresponding quantum size.
177 * A free block is laid out depending on its size, in order to fit all free
178 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
179 * not store their size in the block, instead relying on the header information
180 * to determine their size. Blocks of two or more quanta have room to store
181 * their size in the block, and store it both after the 'next' pointer, and in
182 * the last 2 bytes of the block.
185 * Offset (32-bit mode) (64-bit mode)
191 * Offset (32-bit mode) (64-bit mode)
194 * 0x8 0x10 : size (in quantum counts)
195 * end - 2 end - 2 : size (in quantum counts)
198 * All fields are pointer-sized, except for the size which is an unsigned short.
202 #define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
203 #define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
205 #define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
208 #define NUM_TINY_SLOTS 64 // number of slots for free-lists
210 #define NUM_TINY_SLOTS 32 // number of slots for free-lists
213 #define NUM_TINY_BLOCKS 64520
214 #define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
215 #define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
216 #define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM) // 20
218 #define TINY_ENTROPY_BITS 15
219 #define TINY_ENTROPY_MASK ((1 << TINY_ENTROPY_BITS) - 1)
222 * Avoid having so much entropy that the end of a valid tiny allocation
223 * might overrun the end of the tiny region.
225 #if TINY_ENTROPY_MASK + NUM_TINY_SLOTS > NUM_TINY_BLOCKS
226 #error Too many entropy bits for tiny region requested
230 * Enough room for the data, followed by the bit arrays (2-bits per block)
231 * plus rounding to the nearest page.
233 #define CEIL_NUM_TINY_BLOCKS_WORDS (((NUM_TINY_BLOCKS + 31) & ~31) >> 5)
234 #define TINY_METADATA_SIZE (sizeof(region_trailer_t) + sizeof(tiny_header_inuse_pair_t) * CEIL_NUM_TINY_BLOCKS_WORDS)
235 #define TINY_REGION_SIZE \
236 ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
238 #define TINY_METADATA_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
241 * Beginning and end pointers for a region's heap.
243 #define TINY_REGION_ADDRESS(region) ((void *)(region))
244 #define TINY_REGION_END(region) ((void *)(((uintptr_t)(region)) + (NUM_TINY_BLOCKS * TINY_QUANTUM)))
247 * Locate the heap base for a pointer known to be within a tiny region.
249 #define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
252 * Convert between byte and msize units.
254 #define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
255 #define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
258 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
260 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
262 #define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
265 * Layout of a tiny region
267 typedef uint32_t tiny_block_t
[4]; // assert(TINY_QUANTUM == sizeof(tiny_block_t))
269 typedef struct tiny_header_inuse_pair
273 } tiny_header_inuse_pair_t
;
275 typedef struct region_trailer
277 struct region_trailer
*prev
;
278 struct region_trailer
*next
;
279 boolean_t recirc_suitable
;
280 boolean_t failedREUSE
;
281 volatile int pinned_to_depot
;
283 mag_index_t mag_index
;
286 typedef struct tiny_region
288 tiny_block_t blocks
[NUM_TINY_BLOCKS
];
290 region_trailer_t trailer
;
292 // The interleaved bit arrays comprising the header and inuse bitfields.
293 // The unused bits of each component in the last pair will be initialized to sentinel values.
294 tiny_header_inuse_pair_t pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
];
296 uint8_t pad
[TINY_REGION_SIZE
- (NUM_TINY_BLOCKS
* sizeof(tiny_block_t
)) - TINY_METADATA_SIZE
];
300 * Per-region meta data for tiny allocator
302 #define REGION_TRAILER_FOR_TINY_REGION(r) (&(((tiny_region_t)(r))->trailer))
303 #define MAGAZINE_INDEX_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->mag_index)
304 #define BYTES_USED_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->bytes_used)
307 * Locate the block header for a pointer known to be within a tiny region.
309 #define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)&(((tiny_region_t)TINY_REGION_FOR_PTR(_p))->pairs))
312 * Locate the inuse map for a given block header pointer.
314 #define TINY_INUSE_FOR_HEADER(_h) ((void *)&(((tiny_header_inuse_pair_t *)(_h))->inuse))
317 * Compute the bitmap index for a pointer known to be within a tiny region.
319 #define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
321 #define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
324 #warning TINY_CACHE turned off
327 #define TINY_REGION_PAYLOAD_BYTES (NUM_TINY_BLOCKS * TINY_QUANTUM)
329 /********************* DEFINITIONS for small ************************/
332 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's hashed_regions
335 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
336 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
337 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
340 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
341 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
343 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
344 * of the corresponding quantum size.
346 * A free block is laid out as:
348 * Offset (32-bit mode) (64-bit mode)
351 * 0x8 0x10 : size (in quantum counts)
352 * end - 2 end - 2 : size (in quantum counts)
355 * All fields are pointer-sized, except for the size which is an unsigned short.
359 #define SMALL_IS_FREE (1 << 15)
361 #define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
362 #define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
364 #define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
367 * The number of slots in the free-list for small blocks. To avoid going to
368 * vm system as often on large memory machines, increase the number of free list
369 * spots above some amount of RAM installed in the system.
371 #define NUM_SMALL_SLOTS 32
372 #define NUM_SMALL_SLOTS_LARGEMEM 256
373 #define SMALL_BITMAP_WORDS 8
376 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
377 * convention msize=0 => msize = (1<<15)
379 #define NUM_SMALL_BLOCKS 16320
380 #define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
381 #define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
382 #define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
384 #define SMALL_ENTROPY_BITS 13
385 #define SMALL_ENTROPY_MASK ((1 << SMALL_ENTROPY_BITS) - 1)
388 * Avoid having so much entropy that the end of a valid small allocation
389 * might overrun the end of the small region.
391 #if SMALL_ENTROPY_MASK + NUM_SMALL_SLOTS > NUM_SMALL_BLOCKS
392 #error Too many entropy bits for small region requested
395 #define SMALL_METADATA_SIZE (sizeof(region_trailer_t) + NUM_SMALL_BLOCKS * sizeof(msize_t))
396 #define SMALL_REGION_SIZE \
397 ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
399 #define SMALL_METADATA_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
402 * Beginning and end pointers for a region's heap.
404 #define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
405 #define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
408 * Locate the heap base for a pointer known to be within a small region.
410 #define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
413 * Convert between byte and msize units.
415 #define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
416 #define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
418 #define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
421 * Layout of a small region
423 typedef uint32_t small_block_t
[SMALL_QUANTUM
/sizeof(uint32_t)];
425 typedef struct small_region
427 small_block_t blocks
[NUM_SMALL_BLOCKS
];
429 region_trailer_t trailer
;
431 msize_t small_meta_words
[NUM_SMALL_BLOCKS
];
433 uint8_t pad
[SMALL_REGION_SIZE
- (NUM_SMALL_BLOCKS
* sizeof(small_block_t
)) - SMALL_METADATA_SIZE
];
437 * Per-region meta data for small allocator
439 #define REGION_TRAILER_FOR_SMALL_REGION(r) (&(((small_region_t)(r))->trailer))
440 #define MAGAZINE_INDEX_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->mag_index)
441 #define BYTES_USED_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->bytes_used)
444 * Locate the metadata base for a pointer known to be within a small region.
446 #define SMALL_META_HEADER_FOR_PTR(_p) (((small_region_t)SMALL_REGION_FOR_PTR(_p))->small_meta_words)
449 * Compute the metadata index for a pointer known to be within a small region.
451 #define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
454 * Find the metadata word for a pointer known to be within a small region.
456 #define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
459 * Determine whether a pointer known to be within a small region points to memory which is free.
461 #define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
464 * Extract the msize value for a pointer known to be within a small region.
466 #define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
468 #define SMALL_CACHE 1
470 #warning SMALL_CACHE turned off
473 #define SMALL_REGION_PAYLOAD_BYTES (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
475 /************************* DEFINITIONS for large ****************************/
477 #define LARGE_THRESHOLD (15 * 1024) // strictly above this use "large"
478 #define LARGE_THRESHOLD_LARGEMEM (127 * 1024)
480 #if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
481 #error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
484 #if (LARGE_THRESHOLD_LARGEMEM > NUM_SMALL_SLOTS_LARGEMEM * SMALL_QUANTUM)
485 #error LARGE_THRESHOLD_LARGEMEM should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
489 * When all memory is touched after a copy, vm_copy() is always a lose
490 * But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages
493 * This must be larger than LARGE_THRESHOLD
495 #define VM_COPY_THRESHOLD (40 * 1024)
496 #define VM_COPY_THRESHOLD_LARGEMEM (128 * 1024)
499 vm_address_t address
;
501 boolean_t did_madvise_reusable
;
504 #if !TARGET_OS_EMBEDDED
505 #define LARGE_CACHE 1
507 #define LARGE_CACHE 0
510 #warning LARGE_CACHE turned off
512 #if defined(__LP64__)
513 #define LARGE_ENTRY_CACHE_SIZE 16
514 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x80000000) /* 2Gb */
516 #define LARGE_ENTRY_CACHE_SIZE 8
517 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x02000000) /* 32Mb */
519 #define LARGE_CACHE_SIZE_ENTRY_LIMIT (LARGE_CACHE_SIZE_LIMIT/LARGE_ENTRY_CACHE_SIZE)
521 #define SZONE_FLOTSAM_THRESHOLD_LOW (1024 * 512)
522 #define SZONE_FLOTSAM_THRESHOLD_HIGH (1024 * 1024)
524 /*******************************************************************************
525 * Definitions for region hash
526 ******************************************************************************/
528 typedef void * region_t
;
529 typedef region_t
* rgnhdl_t
; /* A pointer into hashed_regions array. */
531 #define INITIAL_NUM_REGIONS_SHIFT 6 // log2(INITIAL_NUM_REGIONS)
532 #define INITIAL_NUM_REGIONS (1 << INITIAL_NUM_REGIONS_SHIFT) // Must be a power of 2!
533 #define HASHRING_OPEN_ENTRY ((region_t) 0) // Initial value and sentinel marking end of collision chain
534 #define HASHRING_REGION_DEALLOCATED ((region_t)-1) // Region at this slot reclaimed by OS
535 #define HASH_BLOCKS_ALIGN TINY_BLOCKS_ALIGN // MIN( TINY_BLOCKS_ALIGN, SMALL_BLOCKS_ALIGN, ... )
537 typedef struct region_hash_generation
{
538 size_t num_regions_allocated
;
539 size_t num_regions_allocated_shift
; // log2(num_regions_allocated)
540 region_t
*hashed_regions
; // hashed by location
541 struct region_hash_generation
*nextgen
;
542 } region_hash_generation_t
;
544 /*******************************************************************************
545 * Per-processor magazine for tiny and small allocators
546 ******************************************************************************/
548 typedef struct { // vm_allocate()'d, so the array of magazines is page-aligned to begin with.
549 // Take magazine_lock first, Depot lock when needed for recirc, then szone->{tiny,small}_regions_lock when needed for alloc
550 pthread_lock_t magazine_lock CACHE_ALIGN
;
551 // Protection for the crtical section that does allocate_pages outside the magazine_lock
552 volatile boolean_t alloc_underway
;
554 // One element deep "death row", optimizes malloc/free/malloc for identical size.
555 void *mag_last_free
; // low SHIFT_{TINY,SMALL}_QUANTUM bits indicate the msize
556 region_t mag_last_free_rgn
; // holds the region for mag_last_free
558 free_list_t
*mag_free_list
[256]; // assert( 256 >= MAX( NUM_TINY_SLOTS, NUM_SMALL_SLOTS_LARGEMEM ))
559 unsigned mag_bitmap
[8]; // assert( sizeof(mag_bitmap) << 3 >= sizeof(mag_free_list)/sizeof(free_list_t) )
561 // the first and last free region in the last block are treated as big blocks in use that are not accounted for
562 size_t mag_bytes_free_at_end
;
563 size_t mag_bytes_free_at_start
;
564 region_t mag_last_region
; // Valid iff mag_bytes_free_at_end || mag_bytes_free_at_start > 0
567 unsigned mag_num_objects
;
568 size_t mag_num_bytes_in_objects
;
569 size_t num_bytes_in_magazine
;
571 // recirculation list -- invariant: all regions owned by this magazine that meet the emptiness criteria
572 // are located nearer to the head of the list than any region that doesn't satisfy that criteria.
573 // Doubly linked list for efficient extraction.
574 unsigned recirculation_entries
;
575 region_trailer_t
*firstNode
;
576 region_trailer_t
*lastNode
;
579 uint64_t pad
[48]; // So sizeof(magazine_t) is 2560 bytes. FIXME: assert this at compile time
581 uint32_t pad
[12]; // So sizeof(magazine_t) is 1280 bytes. FIXME: assert this at compile time
585 #define TINY_MAX_MAGAZINES 32 /* MUST BE A POWER OF 2! */
586 #define TINY_MAGAZINE_PAGED_SIZE \
587 (((sizeof(magazine_t) * (TINY_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
588 ~ (vm_page_size - 1)) /* + 1 for the Depot */
590 #define SMALL_MAX_MAGAZINES 32 /* MUST BE A POWER OF 2! */
591 #define SMALL_MAGAZINE_PAGED_SIZE \
592 (((sizeof(magazine_t) * (SMALL_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
593 ~ (vm_page_size - 1)) /* + 1 for the Depot */
595 #define DEPOT_MAGAZINE_INDEX -1
597 /****************************** zone itself ***********************************/
600 * Note that objects whose adddress are held in pointers here must be pursued
601 * individually in the {tiny,small}_in_use_enumeration() routines. See for
602 * example the treatment of region_hash_generation and tiny_magazines below.
605 typedef struct szone_s
{ // vm_allocate()'d, so page-aligned to begin with.
606 malloc_zone_t basic_zone
; // first page will be given read-only protection
607 uint8_t pad
[vm_page_size
- sizeof(malloc_zone_t
)];
609 pthread_key_t cpu_id_key
; // remainder of structure is R/W (contains no function pointers)
610 unsigned debug_flags
;
613 /* Regions for tiny objects */
614 pthread_lock_t tiny_regions_lock CACHE_ALIGN
;
615 size_t num_tiny_regions
;
616 size_t num_tiny_regions_dealloc
;
617 region_hash_generation_t
*tiny_region_generation
;
618 region_hash_generation_t trg
[2];
620 int num_tiny_magazines
;
621 unsigned num_tiny_magazines_mask
;
622 int num_tiny_magazines_mask_shift
;
623 magazine_t
*tiny_magazines
; // array of per-processor magazines
625 #if TARGET_OS_EMBEDDED
626 uintptr_t last_tiny_advise
;
629 /* Regions for small objects */
630 pthread_lock_t small_regions_lock CACHE_ALIGN
;
631 size_t num_small_regions
;
632 size_t num_small_regions_dealloc
;
633 region_hash_generation_t
*small_region_generation
;
634 region_hash_generation_t srg
[2];
636 unsigned num_small_slots
; // determined by physmem size
638 int num_small_magazines
;
639 unsigned num_small_magazines_mask
;
640 int num_small_magazines_mask_shift
;
641 magazine_t
*small_magazines
; // array of per-processor magazines
643 #if TARGET_OS_EMBEDDED
644 uintptr_t last_small_advise
;
647 /* large objects: all the rest */
648 pthread_lock_t large_szone_lock CACHE_ALIGN
; // One customer at a time for large
649 unsigned num_large_objects_in_use
;
650 unsigned num_large_entries
;
651 large_entry_t
*large_entries
; // hashed by location; null entries don't count
652 size_t num_bytes_in_large_objects
;
655 int large_entry_cache_oldest
;
656 int large_entry_cache_newest
;
657 large_entry_t large_entry_cache
[LARGE_ENTRY_CACHE_SIZE
]; // "death row" for large malloc/free
658 boolean_t large_legacy_reset_mprotect
;
659 size_t large_entry_cache_reserve_bytes
;
660 size_t large_entry_cache_reserve_limit
;
661 size_t large_entry_cache_bytes
; // total size of death row, bytes
664 /* flag and limits pertaining to altered malloc behavior for systems with
665 large amounts of physical memory */
666 unsigned is_largemem
;
667 unsigned large_threshold
;
668 unsigned vm_copy_threshold
;
670 /* security cookie */
673 /* Initial region list */
674 region_t initial_tiny_regions
[INITIAL_NUM_REGIONS
];
675 region_t initial_small_regions
[INITIAL_NUM_REGIONS
];
677 /* The purgeable zone constructed by create_purgeable_zone() would like to hand off tiny and small
678 * allocations to the default scalable zone. Record the latter as the "helper" zone here. */
679 struct szone_s
*helper_zone
;
681 boolean_t flotsam_enabled
;
684 #define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
686 #if DEBUG_MALLOC || DEBUG_CLIENT
687 static void szone_sleep(void);
689 __private_extern__
void malloc_error_break(void);
691 // msg prints after fmt, ...
692 static NOINLINE
void szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
695 static void protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
);
696 static void *allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
,
698 static void *allocate_pages_securely(szone_t
*szone
, size_t size
, unsigned char align
,
700 static void deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
);
701 #if TARGET_OS_EMBEDDED
702 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
, uintptr_t *last
);
704 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
);
706 static kern_return_t
_szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
);
708 static INLINE mag_index_t
mag_get_thread_index(szone_t
*szone
) ALWAYSINLINE
;
709 static magazine_t
*mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
,
710 mag_index_t mag_index
);
712 static INLINE rgnhdl_t
hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
)
714 static void hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
);
715 static region_t
*hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
);
716 static region_t
*hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
,
717 size_t *mutable_shift
, size_t *new_size
);
719 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
) ALWAYSINLINE
;
720 static INLINE
uintptr_t free_list_checksum_ptr(szone_t
*szone
, void *p
) ALWAYSINLINE
;
721 static INLINE
void *free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
) ALWAYSINLINE
;
722 static unsigned free_list_count(szone_t
*szone
, free_list_t
*ptr
);
724 static INLINE
void recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
725 static INLINE
void recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
726 static INLINE
void recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
728 static INLINE
void BITARRAY_SET(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
729 static INLINE
void BITARRAY_CLR(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
730 static INLINE boolean_t
BITARRAY_BIT(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
732 static msize_t
get_tiny_free_size(const void *ptr
);
733 static msize_t
get_tiny_previous_free_msize(const void *ptr
);
734 static INLINE msize_t
get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
) ALWAYSINLINE
;
735 static INLINE
void set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
736 static INLINE
void set_tiny_meta_header_in_use_1(const void *ptr
) ALWAYSINLINE
;
737 static INLINE
void set_tiny_meta_header_middle(const void *ptr
) ALWAYSINLINE
;
738 static INLINE
void set_tiny_meta_header_free(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
739 static INLINE boolean_t
tiny_meta_header_is_free(const void *ptr
) ALWAYSINLINE
;
740 static INLINE
void *tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
) ALWAYSINLINE
;
742 static void tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
743 static void tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
744 static INLINE region_t
tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
746 static void tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
);
747 static int tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
748 static size_t tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
749 static void tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
750 static region_t
tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
751 static boolean_t
tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
);
752 static region_t
tiny_find_msize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
753 static boolean_t
tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
755 static INLINE boolean_t
tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
,
756 void *ptr
, msize_t msize
) ALWAYSINLINE
;
757 static void *tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
758 msize_t msize
, void *fresh_region
);
759 static boolean_t
tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
760 static boolean_t
tiny_check_region(szone_t
*szone
, region_t region
);
761 static kern_return_t
tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
762 memory_reader_t reader
, vm_range_recorder_t recorder
);
763 static void *tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
765 static INLINE
void *tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
766 static INLINE
void free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
) ALWAYSINLINE
;
767 static void print_tiny_free_list(szone_t
*szone
);
768 static void print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
);
769 static boolean_t
tiny_free_list_check(szone_t
*szone
, grain_t slot
);
771 static INLINE
void small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
) ALWAYSINLINE
;
772 static INLINE
void small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
) ALWAYSINLINE
;
773 static INLINE
void small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
) ALWAYSINLINE
;
774 static void small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
775 static void small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
776 static INLINE region_t
small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
778 static void small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
);
779 static int small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
780 static size_t small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
781 static void small_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
782 static region_t
small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
783 static boolean_t
small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
);
784 static region_t
small_find_msize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
785 static boolean_t
small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
786 static INLINE boolean_t
small_free_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, region_t region
,
787 void *ptr
, msize_t msize
) ALWAYSINLINE
;
788 static void *small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
789 msize_t msize
, void *fresh_region
);
790 static boolean_t
small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
791 static boolean_t
small_check_region(szone_t
*szone
, region_t region
);
792 static kern_return_t
small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
793 memory_reader_t reader
, vm_range_recorder_t recorder
);
794 static void *small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
796 static INLINE
void *small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
797 static INLINE
void free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
) ALWAYSINLINE
;
798 static void print_small_free_list(szone_t
*szone
);
799 static void print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
);
800 static boolean_t
small_free_list_check(szone_t
*szone
, grain_t grain
);
803 static void large_debug_print(szone_t
*szone
);
805 static large_entry_t
*large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
);
806 static void large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
);
807 static INLINE
void large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
) ALWAYSINLINE
;
808 static INLINE large_entry_t
*large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
) ALWAYSINLINE
;
809 static void large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
,
810 vm_range_t
*range_to_deallocate
);
811 static large_entry_t
*large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
);
812 static vm_range_t
large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
);
813 static NOINLINE kern_return_t
large_in_use_enumerator(task_t task
, void *context
,
814 unsigned type_mask
, vm_address_t large_entries_address
,
815 unsigned num_entries
, memory_reader_t reader
,
816 vm_range_recorder_t recorder
);
817 static void *large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
, boolean_t cleared_requested
);
818 static NOINLINE
void free_large(szone_t
*szone
, void *ptr
);
819 static INLINE
int large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
822 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
824 static NOINLINE
void szone_free(szone_t
*szone
, void *ptr
);
825 static NOINLINE
void *szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
);
826 static NOINLINE
void *szone_malloc(szone_t
*szone
, size_t size
);
827 static NOINLINE
void *szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
828 static NOINLINE
void *szone_valloc(szone_t
*szone
, size_t size
);
829 static NOINLINE
size_t szone_size_try_large(szone_t
*szone
, const void *ptr
);
830 static NOINLINE
size_t szone_size(szone_t
*szone
, const void *ptr
);
831 static NOINLINE
void *szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
832 static NOINLINE
void *szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
833 static NOINLINE
void szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
834 static NOINLINE
unsigned szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
835 static NOINLINE
void szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
836 static void szone_destroy(szone_t
*szone
);
837 static NOINLINE
size_t szone_good_size(szone_t
*szone
, size_t size
);
839 static NOINLINE boolean_t
szone_check_all(szone_t
*szone
, const char *function
);
840 static boolean_t
szone_check(szone_t
*szone
);
841 static kern_return_t
szone_ptr_in_use_enumerator(task_t task
, void *context
,
842 unsigned type_mask
, vm_address_t zone_address
,
843 memory_reader_t reader
, vm_range_recorder_t recorder
);
844 static NOINLINE
void szone_print(szone_t
*szone
, boolean_t verbose
);
845 static void szone_log(malloc_zone_t
*zone
, void *log_address
);
846 static void szone_force_lock(szone_t
*szone
);
847 static void szone_force_unlock(szone_t
*szone
);
848 static boolean_t
szone_locked(szone_t
*szone
);
850 static void szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
852 static void purgeable_free(szone_t
*szone
, void *ptr
);
853 static void *purgeable_malloc(szone_t
*szone
, size_t size
);
854 static void *purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
855 static void *purgeable_valloc(szone_t
*szone
, size_t size
);
856 static size_t purgeable_size(szone_t
*szone
, const void *ptr
);
857 static void *purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
858 static void *purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
859 static void purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
860 static unsigned purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
861 static void purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
862 static void purgeable_destroy(szone_t
*szone
);
863 static size_t purgeable_good_size(szone_t
*szone
, size_t size
);
865 static boolean_t
purgeable_check(szone_t
*szone
);
866 static kern_return_t
purgeable_ptr_in_use_enumerator(task_t task
, void *context
,
867 unsigned type_mask
, vm_address_t zone_address
,
868 memory_reader_t reader
, vm_range_recorder_t recorder
);
869 static void purgeable_print(szone_t
*szone
, boolean_t verbose
);
870 static void purgeable_log(malloc_zone_t
*zone
, void *log_address
);
871 static void purgeable_force_lock(szone_t
*szone
);
872 static void purgeable_force_unlock(szone_t
*szone
);
873 static boolean_t
purgeable_locked(szone_t
*szone
);
875 static void purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
877 static void *frozen_malloc(szone_t
*zone
, size_t new_size
);
878 static void *frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
);
879 static void *frozen_valloc(szone_t
*zone
, size_t new_size
);
880 static void *frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
);
881 static void frozen_free(szone_t
*zone
, void *ptr
);
882 static void frozen_destroy(szone_t
*zone
);
884 static volatile uintptr_t entropic_address
= 0;
885 static volatile uintptr_t entropic_limit
= 0;
886 #define ENTROPIC_KABILLION 0x10000000 /* 256Mb */
888 __private_extern__
uint64_t malloc_entropy
[2];
890 #define SZONE_LOCK(szone) \
892 LOCK(szone->large_szone_lock); \
895 #define SZONE_UNLOCK(szone) \
897 UNLOCK(szone->large_szone_lock); \
900 #define SZONE_TRY_LOCK(szone) \
901 TRY_LOCK(szone->large_szone_lock);
903 #define SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr) \
905 LOCK(mag_ptr->magazine_lock); \
908 #define SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr) \
910 UNLOCK(mag_ptr->magazine_lock); \
913 #define SZONE_MAGAZINE_PTR_TRY_LOCK(szone, mag_ptr) \
914 TRY_LOCK(mag_ptr->magazine_lock);
917 # define LOG(szone,ptr) \
918 (szone->log_address && (((uintptr_t)szone->log_address == -1) || \
919 (szone->log_address == (void *)(ptr))))
921 # define LOG(szone,ptr) 0
924 #if DEBUG_MALLOC || DEBUG_CLIENT
925 # define CHECK(szone,fun) \
926 if ((szone)->debug_flags & CHECK_REGIONS) \
927 szone_check_all(szone, fun)
929 # define CHECK(szone,fun) \
933 /********************* VERY LOW LEVEL UTILITIES ************************/
938 if (getenv("MallocErrorStop")) {
939 _malloc_printf(ASL_LEVEL_NOTICE
, "*** sending SIGSTOP to help debug\n");
940 kill(getpid(), SIGSTOP
);
941 } else if (getenv("MallocErrorSleep")) {
942 _malloc_printf(ASL_LEVEL_NOTICE
, "*** sleeping to help debug\n");
943 sleep(3600); // to help debug
947 // msg prints after fmt, ...
949 szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
952 _SIMPLE_STRING b
= _simple_salloc();
954 if (szone
) SZONE_UNLOCK(szone
); // FIXME: unlock magazine and region locks?
958 _simple_vsprintf(b
, fmt
, ap
);
962 _simple_sprintf(b
, "*** error for object %p: %s\n", ptr
, msg
);
964 _simple_sprintf(b
, "*** error: %s\n", msg
);
966 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b
));
969 * Should only get here if vm_allocate() can't get a single page of
970 * memory, implying _simple_asl_log() would also fail. So we just
971 * print to the file descriptor.
975 _malloc_vprintf(MALLOC_PRINTF_NOLOG
, fmt
, ap
);
979 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error for object %p: %s\n", ptr
, msg
);
981 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error: %s\n", msg
);
983 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** set a breakpoint in malloc_error_break to debug\n");
985 malloc_error_break();
987 szone_print(szone
, 1);
990 // Call abort() if this is a memory corruption error and the abort on
991 // corruption flag is set, or if any error should abort.
992 if ((is_corruption
&& (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_CORRUPTION
)) ||
993 (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_ERROR
)) {
994 CRSetCrashLogMessage(b
? _simple_string(b
) : msg
);
1002 protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
)
1006 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_PRELUDE
)) {
1007 err
= mprotect((void *)((uintptr_t)address
- vm_page_size
), vm_page_size
, protection
);
1009 malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
1010 protection
,(uintptr_t)address
- (1 << vm_page_shift
));
1013 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE
)) {
1014 err
= mprotect((void *)((uintptr_t)address
+ size
), vm_page_size
, protection
);
1016 malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
1017 protection
, (uintptr_t)address
+ size
);
1023 allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
, int vm_page_label
)
1025 // align specifies a desired alignment (as a log) or 0 if no alignment requested
1027 uintptr_t addr
= 0, aligned_address
;
1028 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
1029 boolean_t purgeable
= debug_flags
& SCALABLE_MALLOC_PURGEABLE
;
1030 size_t allocation_size
= round_page(size
);
1032 int alloc_flags
= VM_MAKE_TAG(vm_page_label
);
1034 if (align
) add_guard_pages
= 0; // too cumbersome to deal with that
1035 if (!allocation_size
) allocation_size
= 1 << vm_page_shift
;
1036 if (add_guard_pages
) allocation_size
+= 2 * (1 << vm_page_shift
);
1037 if (align
) allocation_size
+= (size_t)1 << align
;
1038 if (purgeable
) alloc_flags
|= VM_FLAGS_PURGABLE
;
1040 if (allocation_size
< size
) // size_t arithmetic wrapped!
1043 vm_addr
= mmap(0 /* addr */,
1044 allocation_size
/* size */,
1045 PROT_READ
| PROT_WRITE
/* prot */,
1046 MAP_ANON
| MAP_PRIVATE
/* flags */,
1047 alloc_flags
/* fd being used to pass "purgeable" and "vm_page_label" */,
1049 if ((uintptr_t)vm_addr
== -1) {
1050 szone_error(szone
, 0, "can't allocate region", NULL
, "*** mmap(size=%lu) failed (error code=%d)\n",
1051 allocation_size
, errno
);
1054 addr
= (uintptr_t)vm_addr
;
1057 aligned_address
= (addr
+ ((uintptr_t)1 << align
) - 1) & ~ (((uintptr_t)1 << align
) - 1);
1058 if (aligned_address
!= addr
) {
1059 delta
= aligned_address
- addr
;
1060 if (munmap((void *)addr
, delta
) == -1)
1061 malloc_printf("*** munmap unaligned header failed with %d\n", errno
);
1062 addr
= aligned_address
;
1063 allocation_size
-= delta
;
1065 if (allocation_size
> size
) {
1066 if (munmap((void *)(addr
+ size
), allocation_size
- size
) == -1)
1067 malloc_printf("*** munmap unaligned footer failed with %d\n", errno
);
1070 if (add_guard_pages
) {
1071 addr
+= (uintptr_t)1 << vm_page_shift
;
1072 protect((void *)addr
, size
, PROT_NONE
, debug_flags
);
1074 return (void *)addr
;
1078 allocate_pages_securely(szone_t
*szone
, size_t size
, unsigned char align
, int vm_page_label
)
1080 // align specifies a desired alignment (as a log) or 0 if no alignment requested
1082 uintptr_t addr
, aligned_address
;
1083 size_t delta
, allocation_size
= MAX(round_page(size
), vm_page_size
);
1084 int alloc_flags
= VM_MAKE_TAG(vm_page_label
);
1086 if (szone
->debug_flags
& DISABLE_ASLR
)
1087 return allocate_pages(szone
, size
, align
, 0, vm_page_label
);
1090 allocation_size
+= (size_t)1 << align
;
1092 if (allocation_size
< size
) // size_t arithmetic wrapped!
1096 vm_addr
= mmap((void *)entropic_address
/* kernel finds next available range at or above this address */,
1097 allocation_size
/* size */,
1098 PROT_READ
| PROT_WRITE
/* prot */,
1099 MAP_ANON
| MAP_PRIVATE
/* flags */,
1100 alloc_flags
/* fd being used to pass "vm_page_label" */,
1102 if (MAP_FAILED
== vm_addr
) {
1103 szone_error(szone
, 0, "can't allocate region securely", NULL
, "*** mmap(size=%lu) failed (error code=%d)\n",
1107 addr
= (uintptr_t)vm_addr
;
1109 // Don't allow allocation to rise above entropic_limit (for tidiness).
1110 if (addr
+ allocation_size
> entropic_limit
) { // Exhausted current range?
1111 uintptr_t t
= entropic_address
;
1112 uintptr_t u
= t
- ENTROPIC_KABILLION
;
1114 if (u
< t
) { // provided we don't wrap, unmap and retry, in the expanded entropic range
1115 munmap((void *)addr
, allocation_size
);
1116 (void)__sync_bool_compare_and_swap(&entropic_address
, t
, u
); // Just one reduction please
1119 // fall through to use what we got
1122 if (addr
< entropic_address
) { // mmap wrapped to find this allocation, expand the entropic range
1123 uintptr_t t
= entropic_address
;
1124 uintptr_t u
= t
- ENTROPIC_KABILLION
;
1126 (void)__sync_bool_compare_and_swap(&entropic_address
, t
, u
); // Just one reduction please
1127 // fall through to use what we got
1130 // unmap any excess address range used for alignment padding
1132 aligned_address
= (addr
+ ((uintptr_t)1 << align
) - 1) & ~ (((uintptr_t)1 << align
) - 1);
1133 if (aligned_address
!= addr
) {
1134 delta
= aligned_address
- addr
;
1135 if (munmap((void *)addr
, delta
) == -1)
1136 malloc_printf("*** munmap unaligned header failed with %d\n", errno
);
1137 addr
= aligned_address
;
1138 allocation_size
-= delta
;
1140 if (allocation_size
> size
) {
1141 if (munmap((void *)(addr
+ size
), allocation_size
- size
) == -1)
1142 malloc_printf("*** munmap unaligned footer failed with %d\n", errno
);
1145 return (void *)addr
;
1149 deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
)
1152 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
1154 if (add_guard_pages
) {
1155 addr
= (void *)((uintptr_t)addr
- (1 << vm_page_shift
));
1156 size
+= 2 * (1 << vm_page_shift
);
1158 err
= munmap(addr
, size
);
1159 if ((err
== -1) && szone
)
1160 szone_error(szone
, 0, "Can't deallocate_pages region", addr
, NULL
);
1164 #if TARGET_OS_EMBEDDED
1165 madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
, uintptr_t *last
)
1167 madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
)
1171 size_t len
= pgHi
- pgLo
;
1174 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
)
1175 memset((void *)pgLo
, 0xed, len
); // Scribble on MADV_FREEd memory
1178 #if TARGET_OS_EMBEDDED
1187 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)r
, (void *)pgLo
, len
); // DTrace USDT Probe
1188 #if TARGET_OS_EMBEDDED
1189 if (-1 == madvise((void *)pgLo
, len
, MADV_FREE
)) {
1191 if (-1 == madvise((void *)pgLo
, len
, MADV_FREE_REUSABLE
)) {
1193 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
1195 szone_error(szone
, 0, "madvise_free_range madvise(..., MADV_FREE_REUSABLE) failed",
1196 (void *)pgLo
, "length=%d\n", len
);
1203 static kern_return_t
1204 _szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
)
1206 *ptr
= (void *)address
;
1210 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1211 // pthread_t's are page aligned, (sometimes even in ascending sequence). These hash well.
1212 // See Knuth TAOCP, Vol. 3.
1214 #define HASH_SELF() \
1215 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 11400714819323198549ULL) >> (64 - szone->num_tiny_magazines_mask_shift)
1217 #define HASH_SELF() \
1218 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 2654435761UL) >> (32 - szone->num_tiny_magazines_mask_shift)
1221 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
1223 * These commpage routines provide fast access to the logical cpu number
1224 * of the calling processor assuming no pre-emption occurs.
1227 static INLINE mag_index_t
1228 mag_get_thread_index(szone_t
*szone
)
1233 return cpu_number() & (TINY_MAX_MAGAZINES
- 1);
1237 #warning deriving magazine index from pthread_self() [want processor number]
1239 static INLINE mag_index_t
1240 mag_get_thread_index(szone_t
*szone
)
1244 else if ((pthread_key_t
) -1 == szone
->cpu_id_key
) { // In case pthread_key_create() failed.
1247 mag_index_t idx
= (mag_index_t
)(intptr_t)pthread_getspecific(szone
->cpu_id_key
);
1249 // Has this thread been hinted with a non-zero value [i.e. 1 + cpuid()] ?
1250 // If so, bump down the hint to a zero-based magazine index and return it.
1254 // No hint available. Contruct a magazine index for this thread ...
1257 // bump up the hint to exclude zero and try to memorize it ...
1258 pthread_setspecific(szone
->cpu_id_key
, (const void *)((uintptr_t)idx
+ 1));
1260 // and return the (zero-based) magazine index.
1268 mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
, mag_index_t mag_index
)
1270 mag_index_t refreshed_index
;
1271 magazine_t
*mag_ptr
= &(magazines
[mag_index
]);
1273 // Take the lock on entry.
1274 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1276 // Now in the time it took to acquire the lock, the region may have migrated
1277 // from one magazine to another. In which case the magazine lock we obtained
1278 // (namely magazines[mag_index].mag_lock) is stale. If so, keep on tryin' ...
1279 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
1281 SZONE_MAGAZINE_PTR_UNLOCK(szone
, mag_ptr
);
1283 mag_index
= refreshed_index
;
1284 mag_ptr
= &(magazines
[mag_index
]);
1285 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1291 /*******************************************************************************
1292 * Region hash implementation
1294 * This is essentially a duplicate of the existing Large allocator hash, minus
1295 * the ability to remove entries. The two should be combined eventually.
1296 ******************************************************************************/
1297 #pragma mark region hash
1300 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
1303 * FIXME: If consecutive queries of the same region are likely, a one-entry
1304 * cache would likely be a significant performance win here.
1306 static INLINE rgnhdl_t
1307 hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1308 size_t index
, hash_index
;
1314 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1315 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1316 // this hash works really well. See Knuth TAOCP, Vol. 3.
1318 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1320 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1323 entry
= regions
+ index
;
1328 if (++index
== num_entries
)
1330 } while (index
!= hash_index
);
1335 * hash_region_insert_no_lock - Insert a region into the hash ring.
1338 hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1339 size_t index
, hash_index
;
1342 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1343 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1344 // this hash works really well. See Knuth TAOCP, Vol. 3.
1346 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1348 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1351 entry
= regions
+ index
;
1352 if (*entry
== HASHRING_OPEN_ENTRY
|| *entry
== HASHRING_REGION_DEALLOCATED
) {
1356 if (++index
== num_entries
)
1358 } while (index
!= hash_index
);
1362 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
1363 * must be a VM allocation as to avoid recursing between allocating a new small
1364 * region, and asking the small region to allocate space for the new list of
1368 hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
)
1370 size_t size
= num_entries
* sizeof(region_t
);
1372 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC
);
1376 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
1377 * Return the new region and new size to update the szone. Do not deallocate
1378 * the old entries since someone may still be allocating them.
1381 hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
, size_t *mutable_shift
,
1384 // double in size and allocate memory for the regions
1385 *new_size
= old_size
+ old_size
;
1386 *mutable_shift
= *mutable_shift
+ 1;
1387 region_t
*new_regions
= hash_regions_alloc_no_lock(szone
, *new_size
);
1389 // rehash the entries into the new list
1391 for (index
= 0; index
< old_size
; ++index
) {
1392 region_t r
= regions
[index
];
1393 if (r
!= HASHRING_OPEN_ENTRY
&& r
!= HASHRING_REGION_DEALLOCATED
)
1394 hash_region_insert_no_lock(new_regions
, *new_size
, *mutable_shift
, r
);
1399 /********************* FREE LIST UTILITIES ************************/
1401 // A free list entry is comprised of a pair of pointers, previous and next.
1402 // These are used to implement a doubly-linked list, which permits efficient
1405 // Because the free list entries are previously freed objects, a misbehaved
1406 // program may write to a pointer after it has called free() on that pointer,
1407 // either by dereference or buffer overflow from an adjacent pointer. This write
1408 // would then corrupt the free list's previous and next pointers, leading to a
1409 // crash. In order to detect this case, we take advantage of the fact that
1410 // malloc'd pointers are known to be at least 16 byte aligned, and thus have
1411 // at least 4 trailing zero bits.
1413 // When an entry is added to the free list, a checksum of the previous and next
1414 // pointers is calculated and written to the high four bits of the respective
1415 // pointers. Upon detection of an invalid checksum, an error is logged and NULL
1416 // is returned. Since all code which un-checksums pointers checks for a NULL
1417 // return, a potentially crashing or malicious dereference is avoided at the
1418 // cost of leaking the corrupted block, and any subsequent blocks on the free
1419 // list of that size.
1421 static NOINLINE
void
1422 free_list_checksum_botch(szone_t
*szone
, free_list_t
*ptr
)
1424 szone_error(szone
, 1, "incorrect checksum for freed object "
1425 "- object was probably modified after being freed.", ptr
, NULL
);
1428 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
)
1432 chk
= (unsigned char)(ptr
>> 0);
1433 chk
+= (unsigned char)(ptr
>> 8);
1434 chk
+= (unsigned char)(ptr
>> 16);
1435 chk
+= (unsigned char)(ptr
>> 24);
1437 chk
+= (unsigned char)(ptr
>> 32);
1438 chk
+= (unsigned char)(ptr
>> 40);
1439 chk
+= (unsigned char)(ptr
>> 48);
1440 chk
+= (unsigned char)(ptr
>> 56);
1443 return chk
& (uintptr_t)0xF;
1448 #define ANTI_NYBBLE (64 - NYBBLE)
1450 #define ANTI_NYBBLE (32 - NYBBLE)
1453 static INLINE
uintptr_t
1454 free_list_checksum_ptr(szone_t
*szone
, void *ptr
)
1456 uintptr_t p
= (uintptr_t)ptr
;
1457 return (p
>> NYBBLE
) | (free_list_gen_checksum(p
^ szone
->cookie
) << ANTI_NYBBLE
); // compiles to rotate instruction
1460 static INLINE
void *
1461 free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
)
1464 uintptr_t t
= ptr
->u
;
1466 t
= (t
<< NYBBLE
) | (t
>> ANTI_NYBBLE
); // compiles to rotate instruction
1467 p
.u
= t
& ~(uintptr_t)0xF;
1469 if ((t
& (uintptr_t)0xF) != free_list_gen_checksum(p
.u
^ szone
->cookie
))
1471 free_list_checksum_botch(szone
, (free_list_t
*)ptr
);
1481 free_list_count(szone_t
*szone
, free_list_t
*ptr
)
1487 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
1493 recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1495 // excise node from list
1496 if (NULL
== node
->prev
)
1497 mag_ptr
->firstNode
= node
->next
;
1499 node
->prev
->next
= node
->next
;
1501 if (NULL
== node
->next
)
1502 mag_ptr
->lastNode
= node
->prev
;
1504 node
->next
->prev
= node
->prev
;
1506 mag_ptr
->recirculation_entries
--;
1510 recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1512 if (NULL
== mag_ptr
->lastNode
) {
1513 mag_ptr
->firstNode
= node
;
1516 node
->prev
= mag_ptr
->lastNode
;
1517 mag_ptr
->lastNode
->next
= node
;
1519 mag_ptr
->lastNode
= node
;
1521 node
->recirc_suitable
= FALSE
;
1522 mag_ptr
->recirculation_entries
++;
1526 recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1528 if (NULL
== mag_ptr
->firstNode
) {
1529 mag_ptr
->lastNode
= node
;
1532 node
->next
= mag_ptr
->firstNode
;
1533 mag_ptr
->firstNode
->prev
= node
;
1535 mag_ptr
->firstNode
= node
;
1537 node
->recirc_suitable
= FALSE
;
1538 mag_ptr
->recirculation_entries
++;
1541 /* Macros used to manipulate the uint32_t quantity mag_bitmap. */
1543 /* BITMAPV variants are used by tiny. */
1544 #if defined(__LP64__)
1545 // assert(NUM_SLOTS == 64) in which case (slot >> 5) is either 0 or 1
1546 #define BITMAPV_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1547 #define BITMAPV_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1548 #define BITMAPV_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1549 #define BITMAPV_CTZ(bitmap) (__builtin_ctzl(bitmap))
1551 // assert(NUM_SLOTS == 32) in which case (slot >> 5) is always 0, so code it that way
1552 #define BITMAPV_SET(bitmap,slot) (bitmap[0] |= 1 << (slot))
1553 #define BITMAPV_CLR(bitmap,slot) (bitmap[0] &= ~ (1 << (slot)))
1554 #define BITMAPV_BIT(bitmap,slot) ((bitmap[0] >> (slot)) & 1)
1555 #define BITMAPV_CTZ(bitmap) (__builtin_ctz(bitmap))
1558 /* BITMAPN is used by small. (slot >> 5) takes on values from 0 to 7. */
1559 #define BITMAPN_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1560 #define BITMAPN_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1561 #define BITMAPN_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1563 /* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
1564 #define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap[0]))
1566 /********************* TINY FREE LIST UTILITIES ************************/
1568 // We encode the meta-headers as follows:
1569 // Each quantum has an associated set of 2 bits:
1570 // block_header when 1 says this block is the beginning of a block
1571 // in_use when 1 says this block is in use
1572 // so a block in use of size 3 is 1-1 0-X 0-X
1573 // for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
1574 // for a block middle the bits are 0-0
1576 // We store the meta-header bit arrays by interleaving them 32 bits at a time.
1577 // Initial 32 bits of block_header, followed by initial 32 bits of in_use, followed
1578 // by next 32 bits of block_header, followed by next 32 bits of in_use, etc.
1579 // This localizes memory references thereby reducing cache and TLB pressures.
1582 BITARRAY_SET(uint32_t *bits
, msize_t index
)
1584 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1585 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1586 bits
[(index
>> 5) << 1] |= (1 << (index
& 31));
1590 BITARRAY_CLR(uint32_t *bits
, msize_t index
)
1592 bits
[(index
>> 5) << 1] &= ~(1 << (index
& 31));
1595 static INLINE boolean_t
1596 BITARRAY_BIT(uint32_t *bits
, msize_t index
)
1598 return ((bits
[(index
>> 5) << 1]) >> (index
& 31)) & 1;
1602 static INLINE
void bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
) ALWAYSINLINE
;
1605 bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
)
1607 // start >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1608 // (start >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1609 uint32_t *addr
= bits
+ ((start
>> 5) << 1);
1611 uint32_t span
= end
- start
;
1616 addr
[0] &= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1617 addr
[2] &= (0xFFFFFFFFU
<< (end
- 32));
1619 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1620 mask
|= (0xFFFFFFFFU
<< end
);
1627 * Obtain the size of a free tiny block (in msize_t units).
1630 get_tiny_free_size(const void *ptr
)
1632 void *next_block
= (void *)((uintptr_t)ptr
+ TINY_QUANTUM
);
1633 void *region_end
= TINY_REGION_END(TINY_REGION_FOR_PTR(ptr
));
1635 // check whether the next block is outside the tiny region or a block header
1636 // if so, then the size of this block is one, and there is no stored size.
1637 if (next_block
< region_end
)
1639 uint32_t *next_header
= TINY_BLOCK_HEADER_FOR_PTR(next_block
);
1640 msize_t next_index
= TINY_INDEX_FOR_PTR(next_block
);
1642 if (!BITARRAY_BIT(next_header
, next_index
))
1643 return TINY_FREE_SIZE(ptr
);
1649 * Get the size of the previous free block, which is stored in the last two
1650 * bytes of the block. If the previous block is not free, then the result is
1654 get_tiny_previous_free_msize(const void *ptr
)
1656 // check whether the previous block is in the tiny region and a block header
1657 // if so, then the size of the previous block is one, and there is no stored
1659 if (ptr
!= TINY_REGION_FOR_PTR(ptr
))
1661 void *prev_block
= (void *)((uintptr_t)ptr
- TINY_QUANTUM
);
1662 uint32_t *prev_header
= TINY_BLOCK_HEADER_FOR_PTR(prev_block
);
1663 msize_t prev_index
= TINY_INDEX_FOR_PTR(prev_block
);
1664 if (BITARRAY_BIT(prev_header
, prev_index
))
1666 return TINY_PREVIOUS_MSIZE(ptr
);
1668 // don't read possibly unmapped memory before the beginning of the region
1672 static INLINE msize_t
1673 get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
)
1675 // returns msize and is_free
1676 // may return 0 for the msize component (meaning 65536)
1677 uint32_t *block_header
;
1680 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1681 index
= TINY_INDEX_FOR_PTR(ptr
);
1683 msize_t midx
= (index
>> 5) << 1;
1684 uint32_t mask
= 1 << (index
& 31);
1686 if (0 == (block_header
[midx
] & mask
)) // if (!BITARRAY_BIT(block_header, index))
1688 if (0 == (block_header
[midx
+ 1] & mask
)) { // if (!BITARRAY_BIT(in_use, index))
1690 return get_tiny_free_size(ptr
);
1693 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1694 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1695 #if defined(__LP64__)
1696 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1697 // That's guaranteed to be somewhwere in the next 64 bits. And those bits could span three
1698 // uint32_t block_header elements. Collect the bits into a single uint64_t and measure up with ffsl.
1699 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1700 uint32_t bitidx
= index
& 31;
1701 uint64_t word_lo
= addr
[0];
1702 uint64_t word_mid
= addr
[2];
1703 uint64_t word_hi
= addr
[4];
1704 uint64_t word_lomid
= (word_lo
>> bitidx
) | (word_mid
<< (32 - bitidx
));
1705 uint64_t word
= bitidx
? word_lomid
| (word_hi
<< (64 - bitidx
)) : word_lomid
;
1706 uint32_t result
= __builtin_ffsl(word
>> 1);
1708 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1709 // That's guaranteed to be somwhwere in the next 32 bits. And those bits could span two
1710 // uint32_t block_header elements. Collect the bits into a single uint32_t and measure up with ffs.
1711 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1712 uint32_t bitidx
= index
& 31;
1713 uint32_t word
= bitidx
? (addr
[0] >> bitidx
) | (addr
[2] << (32 - bitidx
)) : addr
[0];
1714 uint32_t result
= __builtin_ffs(word
>> 1);
1720 set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
)
1722 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1723 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1724 msize_t clr_msize
= msize
- 1;
1725 msize_t midx
= (index
>> 5) << 1;
1726 uint32_t val
= (1 << (index
& 31));
1729 if (msize
>= NUM_TINY_SLOTS
)
1730 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr
, msize
);
1731 if ((unsigned)index
+ (unsigned)msize
> 0x10000)
1732 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr
, msize
);
1735 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1736 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1738 // bitarray_mclr(block_header, index, end_bit);
1739 // bitarray_mclr(in_use, index, end_bit);
1742 midx
= (index
>> 5) << 1;
1744 unsigned start
= index
& 31;
1745 unsigned end
= start
+ clr_msize
;
1747 #if defined(__LP64__)
1749 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1750 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 64));
1751 block_header
[midx
+ 0] &= mask0
; // clear header
1752 block_header
[midx
+ 1] &= mask0
; // clear in_use
1753 block_header
[midx
+ 2] = 0; // clear header
1754 block_header
[midx
+ 3] = 0; // clear in_use
1755 block_header
[midx
+ 4] &= mask1
; // clear header
1756 block_header
[midx
+ 5] &= mask1
; // clear in_use
1760 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1761 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 32));
1762 block_header
[midx
+ 0] &= mask0
;
1763 block_header
[midx
+ 1] &= mask0
;
1764 block_header
[midx
+ 2] &= mask1
;
1765 block_header
[midx
+ 3] &= mask1
;
1767 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1768 mask
|= (0xFFFFFFFFU
<< end
);
1769 block_header
[midx
+ 0] &= mask
;
1770 block_header
[midx
+ 1] &= mask
;
1773 // we set the block_header bit for the following block to reaffirm next block is a block
1775 midx
= (index
>> 5) << 1;
1776 val
= (1 << (index
& 31));
1777 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize));
1783 mf
= get_tiny_meta_header(ptr
, &ff
);
1785 malloc_printf("setting header for tiny in_use %p : %d\n", ptr
, msize
);
1786 malloc_printf("reading header for tiny %p : %d %d\n", ptr
, mf
, ff
);
1793 set_tiny_meta_header_in_use_1(const void *ptr
) // As above with msize == 1
1795 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1796 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1797 msize_t midx
= (index
>> 5) << 1;
1798 uint32_t val
= (1 << (index
& 31));
1800 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1801 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1804 midx
= (index
>> 5) << 1;
1805 val
= (1 << (index
& 31));
1807 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize))
1811 set_tiny_meta_header_middle(const void *ptr
)
1813 // indicates this block is in the middle of an in use block
1814 uint32_t *block_header
;
1818 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1819 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1820 index
= TINY_INDEX_FOR_PTR(ptr
);
1822 BITARRAY_CLR(block_header
, index
);
1823 BITARRAY_CLR(in_use
, index
);
1827 set_tiny_meta_header_free(const void *ptr
, msize_t msize
)
1829 // !msize is acceptable and means 65536
1830 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1831 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1832 msize_t midx
= (index
>> 5) << 1;
1833 uint32_t val
= (1 << (index
& 31));
1836 if ((unsigned)index
+ (unsigned)msize
> 0x10000) {
1837 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr
, msize
);
1841 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1842 block_header
[midx
+ 1] &= ~val
; // BITARRAY_CLR(in_use, index);
1844 // mark the end of this block if msize is > 1. For msize == 0, the whole
1845 // region is free, so there is no following block. For msize == 1, there is
1846 // no space to write the size on 64 bit systems. The size for 1 quantum
1847 // blocks is computed from the metadata bitmaps.
1849 void *follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
1850 TINY_PREVIOUS_MSIZE(follower
) = msize
;
1851 TINY_FREE_SIZE(ptr
) = msize
;
1854 TINY_FREE_SIZE(ptr
) = msize
;
1858 msize_t mf
= get_tiny_meta_header(ptr
, &ff
);
1859 if ((msize
!= mf
) || !ff
) {
1860 malloc_printf("setting header for tiny free %p : %u\n", ptr
, msize
);
1861 malloc_printf("reading header for tiny %p : %u %u\n", ptr
, mf
, ff
);
1866 static INLINE boolean_t
1867 tiny_meta_header_is_free(const void *ptr
)
1869 uint32_t *block_header
;
1873 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1874 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1875 index
= TINY_INDEX_FOR_PTR(ptr
);
1876 if (!BITARRAY_BIT(block_header
, index
))
1878 return !BITARRAY_BIT(in_use
, index
);
1881 static INLINE
void *
1882 tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
)
1884 // returns the previous block, assuming and verifying it's free
1885 uint32_t *block_header
;
1888 msize_t previous_msize
;
1889 msize_t previous_index
;
1892 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1893 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1894 index
= TINY_INDEX_FOR_PTR(ptr
);
1898 if ((previous_msize
= get_tiny_previous_free_msize(ptr
)) > index
)
1901 previous_index
= index
- previous_msize
;
1902 previous_ptr
= (void *)((uintptr_t)TINY_REGION_FOR_PTR(ptr
) + TINY_BYTES_FOR_MSIZE(previous_index
));
1903 if (!BITARRAY_BIT(block_header
, previous_index
))
1905 if (BITARRAY_BIT(in_use
, previous_index
))
1907 if (get_tiny_free_size(previous_ptr
) != previous_msize
)
1910 // conservative check did match true check
1911 *prev_msize
= previous_msize
;
1912 return previous_ptr
;
1916 * Adds an item to the proper free list, and also marks the meta-header of the
1918 * Assumes szone has been locked
1921 tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1923 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1924 free_list_t
*free_ptr
= ptr
;
1925 free_list_t
*free_head
= tiny_mag_ptr
->mag_free_list
[slot
];
1928 if (LOG(szone
,ptr
)) {
1929 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1931 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
1932 szone_error(szone
, 1, "tiny_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
1935 set_tiny_meta_header_free(ptr
, msize
);
1938 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
1939 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
1940 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
1942 if (! tiny_meta_header_is_free(free_head
)) {
1943 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
1944 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
1947 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
1949 BITMAPV_SET(tiny_mag_ptr
->mag_bitmap
, slot
);
1951 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
1952 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
1954 tiny_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
1958 * Removes the item pointed to by ptr in the proper free list.
1959 * Assumes szone has been locked
1962 tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1964 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1965 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
1967 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
1968 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
1971 if (LOG(szone
,ptr
)) {
1972 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1976 // The block to remove is the head of the free list
1978 if (tiny_mag_ptr
->mag_free_list
[slot
] != ptr
) {
1979 szone_error(szone
, 1, "tiny_free_list_remove_ptr: Internal invariant broken (tiny_mag_ptr->mag_free_list[slot])", ptr
,
1980 "ptr=%p slot=%d msize=%d tiny_mag_ptr->mag_free_list[slot]=%p\n",
1981 ptr
, slot
, msize
, (void *)tiny_mag_ptr
->mag_free_list
[slot
]);
1985 tiny_mag_ptr
->mag_free_list
[slot
] = next
;
1986 if (!next
) BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
1988 // We know free_ptr is already checksummed, so we don't need to do it
1990 previous
->next
= free_ptr
->next
;
1993 // We know free_ptr is already checksummed, so we don't need to do it
1995 next
->previous
= free_ptr
->previous
;
2000 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
2001 * or NULL if not found.
2003 static INLINE region_t
2004 tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
2006 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2007 szone
->tiny_region_generation
->num_regions_allocated
,
2008 szone
->tiny_region_generation
->num_regions_allocated_shift
,
2009 TINY_REGION_FOR_PTR(ptr
));
2014 tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
) {
2015 void *last_block
, *previous_block
;
2016 uint32_t *last_header
;
2017 msize_t last_msize
, previous_msize
, last_index
;
2019 // It is possible that the block prior to the last block in the region has
2020 // been free'd, but was not coalesced with the free bytes at the end of the
2021 // block, since we treat the bytes at the end of the region as "in use" in
2022 // the meta headers. Attempt to coalesce the last block with the previous
2023 // block, so we don't violate the "no consecutive free blocks" invariant.
2025 // FIXME: Need to investigate how much work would be required to increase
2026 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
2027 // than performing this workaround.
2030 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2031 last_block
= (void *)
2032 ((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) - tiny_mag_ptr
->mag_bytes_free_at_end
);
2033 last_msize
= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
2034 last_header
= TINY_BLOCK_HEADER_FOR_PTR(last_block
);
2035 last_index
= TINY_INDEX_FOR_PTR(last_block
);
2037 // Before anything we transform any remaining mag_bytes_free_at_end into a
2038 // regular free block. We take special care here to update the bitfield
2039 // information, since we are bypassing the normal free codepath. If there
2040 // is more than one quanta worth of memory in mag_bytes_free_at_end, then
2041 // there will be two block headers:
2042 // 1) header for the free space at end, msize = 1
2043 // 2) header inserted by set_tiny_meta_header_in_use after block
2044 // We must clear the second one so that when the free block's size is
2045 // queried, we do not think the block is only 1 quantum in size because
2046 // of the second set header bit.
2047 if (last_index
!= (NUM_TINY_BLOCKS
- 1))
2048 BITARRAY_CLR(last_header
, (last_index
+ 1));
2050 previous_block
= tiny_previous_preceding_free(last_block
, &previous_msize
);
2051 if (previous_block
) {
2052 set_tiny_meta_header_middle(last_block
);
2053 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous_block
, previous_msize
);
2054 last_block
= previous_block
;
2055 last_msize
+= previous_msize
;
2058 // splice last_block into the free list
2059 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, last_block
, last_msize
);
2060 tiny_mag_ptr
->mag_bytes_free_at_end
= 0;
2064 // Coalesce the big free block at start with any following free blocks
2065 if (tiny_mag_ptr
->mag_bytes_free_at_start
) {
2066 last_block
= TINY_REGION_ADDRESS(tiny_mag_ptr
->mag_last_region
);
2067 last_msize
= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_start
);
2069 void *next_block
= (void *) ((uintptr_t)last_block
+ tiny_mag_ptr
->mag_bytes_free_at_start
);
2071 // clear the in use bit we were using to mark the end of the big start block
2072 set_tiny_meta_header_middle((uintptr_t)next_block
- TINY_QUANTUM
);
2074 // Coalesce the big start block with any following free blocks
2075 if (tiny_meta_header_is_free(next_block
)) {
2076 msize_t next_msize
= get_tiny_free_size(next_block
);
2077 set_tiny_meta_header_middle(next_block
);
2078 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2079 last_msize
+= next_msize
;
2082 // splice last_block into the free list
2083 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, last_block
, last_msize
);
2084 tiny_mag_ptr
->mag_bytes_free_at_start
= 0;
2088 tiny_mag_ptr
->mag_last_region
= NULL
;
2092 tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
2093 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
2094 uintptr_t current
= start
;
2095 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
2098 int total_alloc
= 0;
2100 while (current
< limit
) {
2101 msize
= get_tiny_meta_header((void *)current
, &is_free
);
2102 if (is_free
&& !msize
&& (current
== start
)) {
2103 // first block is all free
2108 malloc_printf("*** tiny_free_detach_region error with %p: msize=%d is_free =%d\n",
2109 (void *)current
, msize
, is_free
);
2114 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
2118 current
+= TINY_BYTES_FOR_MSIZE(msize
);
2124 tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
2125 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
2126 uintptr_t current
= start
;
2127 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
2130 size_t total_alloc
= 0;
2132 while (current
< limit
) {
2133 msize
= get_tiny_meta_header((void *)current
, &is_free
);
2134 if (is_free
&& !msize
&& (current
== start
)) {
2135 // first block is all free
2140 malloc_printf("*** tiny_free_reattach_region error with %p: msize=%d is_free =%d\n",
2141 (void *)current
, msize
, is_free
);
2146 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
2148 total_alloc
+= TINY_BYTES_FOR_MSIZE(msize
);
2150 current
+= TINY_BYTES_FOR_MSIZE(msize
);
2159 static void NOINLINE
/* want private stack frame for automatic array */
2160 tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
2161 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
2162 uintptr_t current
= start
;
2163 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
2166 tiny_pg_pair_t advisory
[((TINY_REGION_PAYLOAD_BYTES
+ vm_page_size
- 1) >> vm_page_shift
) >> 1]; // 256bytes stack allocated
2169 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
2171 while (current
< limit
) {
2172 msize
= get_tiny_meta_header((void *)current
, &is_free
);
2173 if (is_free
&& !msize
&& (current
== start
)) {
2174 // first block is all free
2176 malloc_printf("*** tiny_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
2177 (void *)current
, msize
, is_free
);
2179 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
2180 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
2183 advisory
[advisories
].pnum
= (pgLo
- start
) >> vm_page_shift
;
2184 advisory
[advisories
].size
= (pgHi
- pgLo
) >> vm_page_shift
;
2191 malloc_printf("*** tiny_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
2192 (void *)current
, msize
, is_free
);
2197 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
2198 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
2201 advisory
[advisories
].pnum
= (pgLo
- start
) >> vm_page_shift
;
2202 advisory
[advisories
].size
= (pgHi
- pgLo
) >> vm_page_shift
;
2206 current
+= TINY_BYTES_FOR_MSIZE(msize
);
2209 if (advisories
> 0) {
2212 // So long as the following hold for this region:
2213 // (1) No malloc()'s are ever performed from the depot (hence free pages remain free,)
2214 // (2) The region is not handed over to a per-CPU magazine (where malloc()'s could be performed),
2215 // (3) The entire region is not mumap()'d (so the madvise's are applied to the intended addresses),
2216 // then the madvise opportunities collected just above can be applied outside all locks.
2217 // (1) is ensured by design, (2) and (3) are ensured by bumping the globally visible counter node->pinned_to_depot.
2219 OSAtomicIncrement32Barrier(&(REGION_TRAILER_FOR_TINY_REGION(r
)->pinned_to_depot
));
2220 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
);
2221 for (i
= 0; i
< advisories
; ++i
) {
2222 uintptr_t addr
= (advisory
[i
].pnum
<< vm_page_shift
) + start
;
2223 size_t size
= advisory
[i
].size
<< vm_page_shift
;
2225 #if TARGET_OS_EMBEDDED
2226 madvise_free_range(szone
, r
, addr
, addr
+ size
, NULL
);
2228 madvise_free_range(szone
, r
, addr
, addr
+ size
);
2231 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
2232 OSAtomicDecrement32Barrier(&(REGION_TRAILER_FOR_TINY_REGION(r
)->pinned_to_depot
));
2237 tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
2239 if (0 < node
->bytes_used
||
2240 0 < node
->pinned_to_depot
||
2241 depot_ptr
->recirculation_entries
< (szone
->num_tiny_magazines
* 2)) {
2245 // disconnect node from Depot
2246 recirc_list_extract(szone
, depot_ptr
, node
);
2248 // Iterate the region pulling its free entries off the (locked) Depot's free list
2249 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2250 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2252 if (0 == objects_in_use
) {
2253 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
2254 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
2255 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2256 szone
->tiny_region_generation
->num_regions_allocated
,
2257 szone
->tiny_region_generation
->num_regions_allocated_shift
, sparse_region
);
2258 if (NULL
== pSlot
) {
2259 szone_error(szone
, 1, "tiny_free_try_depot_unmap_no_lock hash lookup failed:", NULL
, "%p\n", sparse_region
);
2262 *pSlot
= HASHRING_REGION_DEALLOCATED
;
2263 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2264 __sync_fetch_and_add( &(szone
->num_tiny_regions_dealloc
), 1); // Atomically increment num_tiny_regions_dealloc
2266 // Caller will transfer ownership of the region back to the OS with no locks held
2267 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
, TINY_REGION_SIZE
); // DTrace USDT Probe
2268 return sparse_region
;
2270 szone_error(szone
, 1, "tiny_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
2276 tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2278 // The entire magazine crossed the "emptiness threshold". Transfer a region
2279 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2280 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2281 region_trailer_t
*node
= tiny_mag_ptr
->firstNode
;
2283 while (node
&& !node
->recirc_suitable
) {
2289 malloc_printf("*** tiny_free_do_recirc_to_depot end of list\n");
2291 return TRUE
; // Caller must SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
2294 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2296 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at_start
2297 if (sparse_region
== tiny_mag_ptr
->mag_last_region
&& (tiny_mag_ptr
->mag_bytes_free_at_end
|| tiny_mag_ptr
->mag_bytes_free_at_start
)) {
2298 tiny_finalize_region(szone
, tiny_mag_ptr
);
2301 // disconnect "suitable" node from magazine
2302 recirc_list_extract(szone
, tiny_mag_ptr
, node
);
2304 // Iterate the region pulling its free entries off its (locked) magazine's free list
2305 int objects_in_use
= tiny_free_detach_region(szone
, tiny_mag_ptr
, sparse_region
);
2306 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2308 // hand over the region to the (locked) Depot
2309 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2310 // this will cause tiny_free_list_add_ptr called by tiny_free_reattach_region to use
2311 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
2312 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
2313 node
->pinned_to_depot
= 0;
2315 // Iterate the region putting its free entries on Depot's free list
2316 size_t bytes_inplay
= tiny_free_reattach_region(szone
, depot_ptr
, sparse_region
);
2318 tiny_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2319 tiny_mag_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2320 tiny_mag_ptr
->mag_num_objects
-= objects_in_use
;
2322 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
); // Unlock the originating magazine
2324 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2325 depot_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2326 depot_ptr
->mag_num_objects
+= objects_in_use
;
2328 // connect to Depot as last node
2329 recirc_list_splice_last(szone
, depot_ptr
, node
);
2331 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (void *)sparse_region
, TINY_REGION_SIZE
,
2332 (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2334 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
2335 tiny_free_scan_madvise_free(szone
, depot_ptr
, sparse_region
);
2337 // If the region is entirely empty vm_deallocate() it outside the depot lock
2338 region_t r_dealloc
= tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
2339 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2341 deallocate_pages(szone
, r_dealloc
, TINY_REGION_SIZE
, 0);
2342 return FALSE
; // Caller need not unlock the originating magazine
2346 tiny_find_msize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2349 grain_t slot
= msize
- 1;
2350 free_list_t
**free_list
= tiny_mag_ptr
->mag_free_list
;
2351 free_list_t
**the_slot
= free_list
+ slot
;
2352 free_list_t
**limit
;
2353 #if defined(__LP64__)
2358 // Assumes we've locked the magazine
2359 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2361 // Look for an exact match by checking the freelist for this msize.
2364 return TINY_REGION_FOR_PTR(ptr
);
2366 // Mask off the bits representing slots holding free blocks smaller than the
2367 // size we need. If there are no larger free blocks, try allocating from
2368 // the free space at the end of the tiny region.
2369 #if defined(__LP64__)
2370 bitmap
= ((uint64_t *)(tiny_mag_ptr
->mag_bitmap
))[0] & ~ ((1ULL << slot
) - 1);
2372 bitmap
= tiny_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
2377 slot
= BITMAPV_CTZ(bitmap
);
2378 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
2381 if (free_list
< limit
) {
2384 return TINY_REGION_FOR_PTR(ptr
);
2386 /* Shouldn't happen. Fall through to look at last slot. */
2388 malloc_printf("in tiny_find_msize_region(), mag_bitmap out of sync, slot=%d\n",slot
);
2393 // We are now looking at the last slot, which contains blocks equal to, or
2394 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
2397 return TINY_REGION_FOR_PTR(ptr
);
2403 tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2405 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2407 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2408 if (szone
->num_tiny_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
2412 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
2413 szone_error(szone
, 1, "tiny_get_region_from_depot called for magazine index -1", NULL
, NULL
);
2418 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2420 // Appropriate a Depot'd region that can satisfy requested msize.
2421 region_trailer_t
*node
;
2422 region_t sparse_region
;
2425 sparse_region
= tiny_find_msize_region(szone
, depot_ptr
, DEPOT_MAGAZINE_INDEX
, msize
);
2426 if (NULL
== sparse_region
) { // Depot empty?
2427 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2431 node
= REGION_TRAILER_FOR_TINY_REGION(sparse_region
);
2432 if (0 >= node
->pinned_to_depot
)
2435 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2437 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2440 // disconnect node from Depot
2441 recirc_list_extract(szone
, depot_ptr
, node
);
2443 // Iterate the region pulling its free entries off the (locked) Depot's free list
2444 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2446 // Transfer ownership of the region
2447 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = mag_index
;
2448 node
->pinned_to_depot
= 0;
2450 // Iterate the region putting its free entries on its new (locked) magazine's free list
2451 size_t bytes_inplay
= tiny_free_reattach_region(szone
, tiny_mag_ptr
, sparse_region
);
2453 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2454 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2455 depot_ptr
->mag_num_objects
-= objects_in_use
;
2457 tiny_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2458 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2459 tiny_mag_ptr
->mag_num_objects
+= objects_in_use
;
2461 // connect to magazine as first node
2462 recirc_list_splice_first(szone
, tiny_mag_ptr
, node
);
2464 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2466 // madvise() outside the Depot lock
2467 #if TARGET_OS_EMBEDDED
2468 if (node
->failedREUSE
) {
2470 if (node
->failedREUSE
||
2471 -1 == madvise((void *)sparse_region
, TINY_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
2473 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
2475 szone_error(szone
, 0, "tiny_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed",
2476 sparse_region
, "length=%d\n", TINY_REGION_PAYLOAD_BYTES
);
2478 node
->failedREUSE
= TRUE
;
2481 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (void *)sparse_region
, TINY_REGION_SIZE
,
2482 (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2487 #define K 1.5 // headroom measured in number of 1Mb regions
2488 #define DENSITY_THRESHOLD(a) \
2489 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
2491 static INLINE boolean_t
2492 tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
, void *ptr
,
2495 void *original_ptr
= ptr
;
2496 size_t original_size
= TINY_BYTES_FOR_MSIZE(msize
);
2497 void *next_block
= ((unsigned char *)ptr
+ original_size
);
2498 msize_t previous_msize
, next_msize
;
2500 free_list_t
*big_free_block
;
2501 free_list_t
*after_next_block
;
2502 free_list_t
*before_next_block
;
2505 if (LOG(szone
,ptr
)) {
2506 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2509 szone_error(szone
, 1, "trying to free tiny block that is too small", ptr
,
2510 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2514 // We try to coalesce this block with the preceeding one
2515 previous
= tiny_previous_preceding_free(ptr
, &previous_msize
);
2518 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
2519 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
2523 // clear the meta_header since this is no longer the start of a block
2524 set_tiny_meta_header_middle(ptr
);
2525 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous
, previous_msize
);
2527 msize
+= previous_msize
;
2529 // We try to coalesce with the next block
2530 if ((next_block
< TINY_REGION_END(region
)) && tiny_meta_header_is_free(next_block
)) {
2531 next_msize
= get_tiny_free_size(next_block
);
2533 if (LOG(szone
, ptr
) || LOG(szone
, next_block
)) {
2534 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
2535 ptr
, msize
, next_block
, next_msize
);
2538 // If we are coalescing with the next block, and the next block is in
2539 // the last slot of the free list, then we optimize this case here to
2540 // avoid removing next_block from the slot (NUM_TINY_SLOTS - 1) and then adding ptr back
2541 // to slot (NUM_TINY_SLOTS - 1).
2542 if (next_msize
>= NUM_TINY_SLOTS
) {
2543 msize
+= next_msize
;
2545 big_free_block
= (free_list_t
*)next_block
;
2546 after_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->next
);
2547 before_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->previous
);
2549 if (!before_next_block
) {
2550 tiny_mag_ptr
->mag_free_list
[NUM_TINY_SLOTS
-1] = ptr
;
2552 before_next_block
->next
.u
= free_list_checksum_ptr(szone
, ptr
);
2555 if (after_next_block
) {
2556 after_next_block
->previous
.u
= free_list_checksum_ptr(szone
, ptr
);
2559 // we don't need to checksum these since they are already checksummed
2560 ((free_list_t
*)ptr
)->previous
= big_free_block
->previous
;
2561 ((free_list_t
*)ptr
)->next
= big_free_block
->next
;
2563 // clear the meta_header to enable coalescing backwards
2564 set_tiny_meta_header_middle(big_free_block
);
2565 set_tiny_meta_header_free(ptr
, msize
);
2567 goto tiny_free_ending
;
2569 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2570 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2571 msize
+= next_msize
;
2574 // The tiny cache already scribbles free blocks as they go through the
2575 // cache whenever msize < TINY_QUANTUM , so we do not need to do it here.
2576 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
&& (msize
>= TINY_QUANTUM
))
2577 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
2579 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, ptr
, msize
);
2583 tiny_mag_ptr
->mag_num_objects
--;
2584 // we use original_size and not msize to avoid double counting the coalesced blocks
2585 tiny_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
2587 // Update this region's bytes in use count
2588 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2589 size_t bytes_used
= node
->bytes_used
- original_size
;
2590 node
->bytes_used
= bytes_used
;
2592 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
2593 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2594 if (szone
->num_tiny_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
2596 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
2597 // Emptiness discriminant
2598 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2599 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
2600 recirculation candidates list. */
2601 node
->recirc_suitable
= TRUE
;
2603 /* After this free, we've found the region is still dense, so it must have been even more so before
2604 the free. That implies the region is already correctly marked. Do nothing. */
2607 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
2608 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2609 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2610 size_t a
= tiny_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
2611 size_t u
= tiny_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
2613 if (a
- u
> ((3 * TINY_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
)) {
2614 return tiny_free_do_recirc_to_depot(szone
, tiny_mag_ptr
, mag_index
);
2619 // Freed to Depot. N.B. Lock on tiny_magazines[DEPOT_MAGAZINE_INDEX] is already held
2620 // Calcuate the first page in the coalesced block that would be safe to mark MADV_FREE
2621 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
2622 uintptr_t round_safe
= round_page(safe_ptr
);
2624 // Calcuate the last page in the coalesced block that would be safe to mark MADV_FREE
2625 uintptr_t safe_extent
= (uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
2626 uintptr_t trunc_extent
= trunc_page(safe_extent
);
2628 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
2629 if (round_safe
< trunc_extent
) { // Safe area covers a page (perhaps many)
2630 uintptr_t lo
= trunc_page((uintptr_t)original_ptr
);
2631 uintptr_t hi
= round_page((uintptr_t)original_ptr
+ original_size
);
2633 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, ptr
, msize
);
2634 set_tiny_meta_header_in_use(ptr
, msize
);
2636 OSAtomicIncrement32Barrier(&(node
->pinned_to_depot
));
2637 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2638 #if TARGET_OS_EMBEDDED
2639 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
), &szone
->last_tiny_advise
);
2641 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
));
2643 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
2644 OSAtomicDecrement32Barrier(&(node
->pinned_to_depot
));
2646 set_tiny_meta_header_free(ptr
, msize
);
2647 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, ptr
, msize
);
2650 #if !TARGET_OS_EMBEDDED
2651 if (0 < bytes_used
|| 0 < node
->pinned_to_depot
) {
2652 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
2653 so as to avoid thrashing between the Depot's free list and a magazines's free list
2654 with detach_region/reattach_region */
2656 /* Depot'd region is just now empty. Consider return to OS. */
2657 region_t r_dealloc
= tiny_free_try_depot_unmap_no_lock(szone
, tiny_mag_ptr
, node
);
2658 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2660 deallocate_pages(szone
, r_dealloc
, TINY_REGION_SIZE
, 0);
2661 return FALSE
; // Caller need not unlock
2666 return TRUE
; // Caller must do SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr)
2669 // Allocates from the last region or a freshly allocated region
2671 tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
2672 msize_t msize
, void * aligned_address
)
2676 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at_start
2677 if (tiny_mag_ptr
->mag_bytes_free_at_end
|| tiny_mag_ptr
->mag_bytes_free_at_start
)
2678 tiny_finalize_region(szone
, tiny_mag_ptr
);
2680 // We set the unused bits of the header in the last pair to be all ones, and those of the inuse to zeroes.
2681 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].header
=
2682 (NUM_TINY_BLOCKS
& 31) ? (0xFFFFFFFFU
<< (NUM_TINY_BLOCKS
& 31)) : 0;
2683 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].inuse
= 0;
2685 // Here find the only place in tinyland that (infrequently) takes the tiny_regions_lock.
2686 // Only one thread at a time should be permitted to assess the density of the hash
2687 // ring and adjust if needed.
2688 // Only one thread at a time should be permitted to insert its new region on
2690 // It is safe for all other threads to read the hash ring (hashed_regions) and
2691 // the associated sizes (num_regions_allocated and num_tiny_regions).
2693 LOCK(szone
->tiny_regions_lock
);
2695 // Check to see if the hash ring of tiny regions needs to grow. Try to
2696 // avoid the hash ring becoming too dense.
2697 if (szone
->tiny_region_generation
->num_regions_allocated
< (2 * szone
->num_tiny_regions
)) {
2698 region_t
*new_regions
;
2700 size_t new_shift
= szone
->tiny_region_generation
->num_regions_allocated_shift
; // In/Out parameter
2701 new_regions
= hash_regions_grow_no_lock(szone
, szone
->tiny_region_generation
->hashed_regions
,
2702 szone
->tiny_region_generation
->num_regions_allocated
,
2705 // Do not deallocate the current hashed_regions allocation since someone may
2706 // be iterating it. Instead, just leak it.
2708 // Prepare to advance to the "next generation" of the hash ring.
2709 szone
->tiny_region_generation
->nextgen
->hashed_regions
= new_regions
;
2710 szone
->tiny_region_generation
->nextgen
->num_regions_allocated
= new_size
;
2711 szone
->tiny_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
2713 // Throw the switch to atomically advance to the next generation.
2714 szone
->tiny_region_generation
= szone
->tiny_region_generation
->nextgen
;
2715 // Ensure everyone sees the advance.
2718 // Tag the region at "aligned_address" as belonging to us,
2719 // and so put it under the protection of the magazine lock we are holding.
2720 // Do this before advertising "aligned_address" on the hash ring(!)
2721 MAGAZINE_INDEX_FOR_TINY_REGION(aligned_address
) = mag_index
;
2723 // Insert the new region into the hash ring, and update malloc statistics
2724 hash_region_insert_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2725 szone
->tiny_region_generation
->num_regions_allocated
,
2726 szone
->tiny_region_generation
->num_regions_allocated_shift
,
2729 szone
->num_tiny_regions
++;
2730 UNLOCK(szone
->tiny_regions_lock
);
2732 tiny_mag_ptr
->mag_last_region
= aligned_address
;
2733 BYTES_USED_FOR_TINY_REGION(aligned_address
) = TINY_BYTES_FOR_MSIZE(msize
);
2735 int offset_msize
= malloc_entropy
[0] & TINY_ENTROPY_MASK
;
2737 if (getenv("MallocASLRForce")) offset_msize
= strtol(getenv("MallocASLRForce"), NULL
, 0) & TINY_ENTROPY_MASK
;
2738 if (getenv("MallocASLRPrint")) malloc_printf("Region: %p offset: %d\n", aligned_address
, offset_msize
);
2741 int offset_msize
= 0;
2743 ptr
= (void *)((uintptr_t) aligned_address
+ TINY_BYTES_FOR_MSIZE(offset_msize
));
2744 set_tiny_meta_header_in_use(ptr
, msize
);
2745 tiny_mag_ptr
->mag_num_objects
++;
2746 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(msize
);
2747 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2749 // We put a header on the last block so that it appears in use (for coalescing, etc...)
2750 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
)));
2751 tiny_mag_ptr
->mag_bytes_free_at_end
= TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS
- msize
- offset_msize
);
2754 // Put a header on the previous block for same reason
2755 tiny_mag_ptr
->mag_bytes_free_at_start
= TINY_BYTES_FOR_MSIZE(offset_msize
);
2757 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
- TINY_QUANTUM
));
2760 tiny_mag_ptr
->mag_bytes_free_at_start
= 0;
2763 // connect to magazine as last node
2764 recirc_list_splice_last(szone
, tiny_mag_ptr
, REGION_TRAILER_FOR_TINY_REGION(aligned_address
));
2767 if (LOG(szone
,ptr
)) {
2768 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2774 static INLINE
void *
2775 tiny_try_shrink_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_good_size
)
2777 msize_t new_msize
= TINY_MSIZE_FOR_BYTES(new_good_size
);
2778 msize_t mshrinkage
= TINY_MSIZE_FOR_BYTES(old_size
) - new_msize
;
2781 void *q
= (void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(new_msize
));
2782 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2783 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2784 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2786 // Mark q as block header and in-use, thus creating two blocks.
2787 set_tiny_meta_header_in_use(q
, mshrinkage
);
2788 tiny_mag_ptr
->mag_num_objects
++;
2790 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2791 szone_free(szone
, q
); // avoid inlining free_tiny(szone, q, ...);
2796 static INLINE boolean_t
2797 tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
2799 // returns 1 on success
2802 unsigned next_index
;
2805 msize_t next_msize
, coalesced_msize
, leftover_msize
;
2808 index
= TINY_INDEX_FOR_PTR(ptr
);
2809 old_msize
= TINY_MSIZE_FOR_BYTES(old_size
);
2810 next_index
= index
+ old_msize
;
2812 if (next_index
>= NUM_TINY_BLOCKS
) {
2815 next_block
= (char *)ptr
+ old_size
;
2817 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2818 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2819 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2822 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2825 is_free
= tiny_meta_header_is_free(next_block
);
2827 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2828 return 0; // next_block is in use;
2830 next_msize
= get_tiny_free_size(next_block
);
2831 if (old_size
+ TINY_BYTES_FOR_MSIZE(next_msize
) < new_size
) {
2832 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2833 return 0; // even with next block, not enough
2836 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2839 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2840 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2841 coalesced_msize
= TINY_MSIZE_FOR_BYTES(new_size
- old_size
+ TINY_QUANTUM
- 1);
2842 leftover_msize
= next_msize
- coalesced_msize
;
2843 if (leftover_msize
) {
2844 /* there's some left, so put the remainder back */
2845 leftover
= (void *)((uintptr_t)next_block
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
));
2847 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover
, leftover_msize
);
2849 set_tiny_meta_header_in_use(ptr
, old_msize
+ coalesced_msize
);
2851 if (LOG(szone
,ptr
)) {
2852 malloc_printf("in tiny_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, old_msize
+ coalesced_msize
);
2855 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2857 // Update this region's bytes in use count
2858 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2859 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2860 node
->bytes_used
= bytes_used
;
2862 // Emptiness discriminant
2863 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2864 /* After this reallocation the region is still sparse, so it must have been even more so before
2865 the reallocation. That implies the region is already correctly marked. Do nothing. */
2867 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2868 recirculation candidates list. */
2869 node
->recirc_suitable
= FALSE
;
2872 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2873 CHECK(szone
, __PRETTY_FUNCTION__
);
2878 tiny_check_region(szone_t
*szone
, region_t region
)
2880 uintptr_t start
, ptr
, region_end
;
2881 boolean_t prev_free
= 0;
2884 free_list_t
*free_head
;
2885 void *follower
, *previous
, *next
;
2886 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
2887 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2890 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2892 /* establish region limits */
2893 start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
2895 if (region
== tiny_mag_ptr
->mag_last_region
) {
2896 ptr
+= tiny_mag_ptr
->mag_bytes_free_at_start
;
2899 * Check the leading block's integrity here also.
2901 if (tiny_mag_ptr
->mag_bytes_free_at_start
) {
2902 msize
= get_tiny_meta_header((void *)(ptr
- TINY_QUANTUM
), &is_free
);
2903 if (is_free
|| (msize
!= 1)) {
2904 malloc_printf("*** invariant broken for leader block %p - %d %d\n", ptr
- TINY_QUANTUM
, msize
, is_free
);
2908 region_end
= (uintptr_t)TINY_REGION_END(region
);
2911 * The last region may have a trailing chunk which has not been converted into inuse/freelist
2914 if (region
== tiny_mag_ptr
->mag_last_region
)
2915 region_end
-= tiny_mag_ptr
->mag_bytes_free_at_end
;
2918 * Scan blocks within the region.
2920 while (ptr
< region_end
) {
2922 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
2925 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2926 if (is_free
&& !msize
&& (ptr
== start
)) {
2931 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
2935 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
2942 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
2945 if (msize
> (NUM_TINY_SLOTS
- 1)) {
2946 malloc_printf("*** invariant broken for %p this tiny msize=%d - size is too large\n",
2950 /* move to next block */
2951 ptr
+= TINY_BYTES_FOR_MSIZE(msize
);
2953 #if !RELAXED_INVARIANT_CHECKS
2955 * Free blocks must have been coalesced, we cannot have a free block following another
2959 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
2963 #endif // RELAXED_INVARIANT_CHECKS
2966 * Check the integrity of this block's entry in its freelist.
2968 free_head
= (free_list_t
*)ptr
;
2969 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
2970 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
2971 if (previous
&& !tiny_meta_header_is_free(previous
)) {
2972 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2976 if (next
&& !tiny_meta_header_is_free(next
)) {
2977 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
2982 * Check the free block's trailing size value.
2984 follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
2985 if (((uintptr_t)follower
!= region_end
) && (get_tiny_previous_free_msize(follower
) != msize
)) {
2986 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
2987 "(end marker incorrect) should be %d; in fact %d\n",
2988 ptr
, follower
, TINY_REGION_ADDRESS(region
), region_end
, msize
, get_tiny_previous_free_msize(follower
));
2991 /* move to next block */
2992 ptr
= (uintptr_t)follower
;
2996 * Ensure that we scanned the entire region
2998 if (ptr
!= region_end
) {
2999 malloc_printf("*** invariant broken for region end %p - %p\n", ptr
, region_end
);
3003 * Check the trailing block's integrity.
3005 if (region
== tiny_mag_ptr
->mag_last_region
) {
3006 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
3007 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
3008 if (is_free
|| (msize
!= 1)) {
3009 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr
, msize
, is_free
);
3016 static kern_return_t
3017 tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
3018 memory_reader_t reader
, vm_range_recorder_t recorder
)
3023 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
3028 vm_range_t admin_range
;
3029 vm_range_t ptr_range
;
3030 unsigned char *mapped_region
;
3031 uint32_t *block_header
;
3033 unsigned block_index
;
3034 unsigned block_limit
;
3039 magazine_t
*tiny_mag_base
= NULL
;
3041 region_hash_generation_t
*trg_ptr
;
3042 err
= reader(task
, (vm_address_t
)szone
->tiny_region_generation
, sizeof(region_hash_generation_t
), (void **)&trg_ptr
);
3043 if (err
) return err
;
3045 num_regions
= trg_ptr
->num_regions_allocated
;
3046 err
= reader(task
, (vm_address_t
)trg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
3047 if (err
) return err
;
3049 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
3050 // Map in all active magazines. Do this outside the iteration over regions.
3051 err
= reader(task
, (vm_address_t
)(szone
->tiny_magazines
),
3052 szone
->num_tiny_magazines
*sizeof(magazine_t
),(void **)&tiny_mag_base
);
3053 if (err
) return err
;
3056 for (index
= 0; index
< num_regions
; ++index
) {
3057 region
= regions
[index
];
3058 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
3059 range
.address
= (vm_address_t
)TINY_REGION_ADDRESS(region
);
3060 range
.size
= (vm_size_t
)TINY_REGION_SIZE
;
3061 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
3062 admin_range
.address
= range
.address
+ TINY_METADATA_START
;
3063 admin_range
.size
= TINY_METADATA_SIZE
;
3064 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
3066 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
3067 ptr_range
.address
= range
.address
;
3068 ptr_range
.size
= NUM_TINY_BLOCKS
* TINY_QUANTUM
;
3069 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
3071 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
3072 void *mag_last_free
;
3073 vm_address_t mag_last_free_ptr
= 0;
3074 msize_t mag_last_free_msize
= 0;
3076 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
3080 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(mapped_region
);
3081 magazine_t
*tiny_mag_ptr
= tiny_mag_base
+ mag_index
;
3083 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3084 mag_last_free
= tiny_mag_ptr
->mag_last_free
;
3085 if (mag_last_free
) {
3086 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(TINY_QUANTUM
- 1);
3087 mag_last_free_msize
= (uintptr_t) mag_last_free
& (TINY_QUANTUM
- 1);
3090 for (mag_index
= 0; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3091 if ((void *)range
.address
== (tiny_mag_base
+ mag_index
)->mag_last_free_rgn
) {
3092 mag_last_free
= (tiny_mag_base
+ mag_index
)->mag_last_free
;
3093 if (mag_last_free
) {
3094 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(TINY_QUANTUM
- 1);
3095 mag_last_free_msize
= (uintptr_t) mag_last_free
& (TINY_QUANTUM
- 1);
3101 block_header
= (uint32_t *)(mapped_region
+ TINY_METADATA_START
+ sizeof(region_trailer_t
));
3102 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
3104 block_limit
= NUM_TINY_BLOCKS
;
3105 if (region
== tiny_mag_ptr
->mag_last_region
) {
3106 block_index
+= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_start
);
3107 block_limit
-= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
3110 while (block_index
< block_limit
) {
3111 vm_size_t block_offset
= TINY_BYTES_FOR_MSIZE(block_index
);
3112 is_free
= !BITARRAY_BIT(in_use
, block_index
);
3114 mapped_ptr
= mapped_region
+ block_offset
;
3116 // mapped_region, the address at which 'range' in 'task' has been
3117 // mapped into our process, is not necessarily aligned to
3118 // TINY_BLOCKS_ALIGN.
3120 // Since the code in get_tiny_free_size() assumes the pointer came
3121 // from a properly aligned tiny region, and mapped_region is not
3122 // necessarily aligned, then do the size calculation directly.
3123 // If the next bit is set in the header bitmap, then the size is one
3124 // quantum. Otherwise, read the size field.
3125 if (!BITARRAY_BIT(block_header
, (block_index
+1)))
3126 msize
= TINY_FREE_SIZE(mapped_ptr
);
3130 } else if (range
.address
+ block_offset
!= mag_last_free_ptr
) {
3132 bit
= block_index
+ 1;
3133 while (! BITARRAY_BIT(block_header
, bit
)) {
3137 buffer
[count
].address
= range
.address
+ block_offset
;
3138 buffer
[count
].size
= TINY_BYTES_FOR_MSIZE(msize
);
3140 if (count
>= MAX_RECORDER_BUFFER
) {
3141 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
3145 // Block is not free but it matches mag_last_free_ptr so even
3146 // though it is not marked free in the bitmap, we treat it as if
3147 // it is and move on
3148 msize
= mag_last_free_msize
;
3152 return KERN_FAILURE
; // Somethings amiss. Avoid looping at this block_index.
3154 block_index
+= msize
;
3157 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
3167 tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
3171 grain_t slot
= msize
- 1;
3172 free_list_t
**free_list
= tiny_mag_ptr
->mag_free_list
;
3173 free_list_t
**the_slot
= free_list
+ slot
;
3175 free_list_t
**limit
;
3176 #if defined(__LP64__)
3181 msize_t leftover_msize
;
3182 free_list_t
*leftover_ptr
;
3184 // Assumes we've locked the region
3185 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
3187 // Look for an exact match by checking the freelist for this msize.
3191 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3193 next
->previous
= ptr
->previous
;
3195 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
3200 if (LOG(szone
, ptr
)) {
3201 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr
, this_msize
);
3204 goto return_tiny_alloc
;
3207 // Mask off the bits representing slots holding free blocks smaller than the
3208 // size we need. If there are no larger free blocks, try allocating from
3209 // the free space at the end of the tiny region.
3210 #if defined(__LP64__)
3211 bitmap
= ((uint64_t *)(tiny_mag_ptr
->mag_bitmap
))[0] & ~ ((1ULL << slot
) - 1);
3213 bitmap
= tiny_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
3216 goto try_tiny_malloc_from_end
;
3218 slot
= BITMAPV_CTZ(bitmap
);
3219 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
3222 if (free_list
< limit
) {
3225 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3228 next
->previous
= ptr
->previous
;
3230 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
3232 this_msize
= get_tiny_free_size(ptr
);
3233 goto add_leftover_and_proceed
;
3236 malloc_printf("in tiny_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
3240 // We are now looking at the last slot, which contains blocks equal to, or
3241 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
3242 // If the last freelist is not empty, and the head contains a block that is
3243 // larger than our request, then the remainder is put back on the free list.
3246 this_msize
= get_tiny_free_size(ptr
);
3247 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3248 if (this_msize
- msize
>= NUM_TINY_SLOTS
) {
3249 // the leftover will go back to the free list, so we optimize by
3250 // modifying the free list rather than a pop and push of the head
3251 leftover_msize
= this_msize
- msize
;
3252 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
3253 *limit
= leftover_ptr
;
3255 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
3257 leftover_ptr
->previous
= ptr
->previous
;
3258 leftover_ptr
->next
= ptr
->next
;
3259 set_tiny_meta_header_free(leftover_ptr
, leftover_msize
);
3261 if (LOG(szone
,ptr
)) {
3262 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n",
3263 ptr
, msize
, this_msize
);
3267 goto return_tiny_alloc
;
3270 next
->previous
= ptr
->previous
;
3273 goto add_leftover_and_proceed
;
3277 try_tiny_malloc_from_end
:
3278 // Let's see if we can use tiny_mag_ptr->mag_bytes_free_at_end
3279 if (tiny_mag_ptr
->mag_bytes_free_at_end
>= TINY_BYTES_FOR_MSIZE(msize
)) {
3280 ptr
= (free_list_t
*)((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) -
3281 tiny_mag_ptr
->mag_bytes_free_at_end
);
3282 tiny_mag_ptr
->mag_bytes_free_at_end
-= TINY_BYTES_FOR_MSIZE(msize
);
3283 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
3284 // let's add an in use block after ptr to serve as boundary
3285 set_tiny_meta_header_in_use_1((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
3289 if (LOG(szone
, ptr
)) {
3290 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr
, msize
);
3293 goto return_tiny_alloc
;
3296 // Try from start if nothing left at end
3297 if (tiny_mag_ptr
->mag_bytes_free_at_start
>= TINY_BYTES_FOR_MSIZE(msize
)) {
3298 ptr
= (free_list_t
*)(TINY_REGION_ADDRESS(tiny_mag_ptr
->mag_last_region
) +
3299 tiny_mag_ptr
->mag_bytes_free_at_start
- TINY_BYTES_FOR_MSIZE(msize
));
3300 tiny_mag_ptr
->mag_bytes_free_at_start
-= TINY_BYTES_FOR_MSIZE(msize
);
3301 if (tiny_mag_ptr
->mag_bytes_free_at_start
) {
3302 // let's add an in use block before ptr to serve as boundary
3303 set_tiny_meta_header_in_use_1((unsigned char *)ptr
- TINY_QUANTUM
);
3307 if (LOG(szone
, ptr
)) {
3308 malloc_printf("in tiny_malloc_from_free_list(), from start ptr=%p, msize=%d\n", ptr
, msize
);
3311 goto return_tiny_alloc
;
3316 add_leftover_and_proceed
:
3317 if (!this_msize
|| (this_msize
> msize
)) {
3318 leftover_msize
= this_msize
- msize
;
3319 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
3321 if (LOG(szone
,ptr
)) {
3322 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
3325 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover_ptr
, leftover_msize
);
3330 tiny_mag_ptr
->mag_num_objects
++;
3331 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(this_msize
);
3333 // Update this region's bytes in use count
3334 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3335 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(this_msize
);
3336 node
->bytes_used
= bytes_used
;
3338 // Emptiness discriminant
3339 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
3340 /* After this allocation the region is still sparse, so it must have been even more so before
3341 the allocation. That implies the region is already correctly marked. Do nothing. */
3343 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
3344 recirculation candidates list. */
3345 node
->recirc_suitable
= FALSE
;
3348 if (LOG(szone
,ptr
)) {
3349 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
3353 set_tiny_meta_header_in_use(ptr
, this_msize
);
3355 set_tiny_meta_header_in_use_1(ptr
);
3358 #undef DENSITY_THRESHOLD
3361 static INLINE
void *
3362 tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
3365 mag_index_t mag_index
= mag_get_thread_index(szone
);
3366 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3369 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
3370 szone_error(szone
, 1, "malloc called for magazine index -1", NULL
, NULL
);
3375 szone_error(szone
, 1, "invariant broken (!msize) in allocation (region)", NULL
, NULL
);
3380 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3383 ptr
= tiny_mag_ptr
->mag_last_free
;
3385 if ((((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) == msize
) {
3387 tiny_mag_ptr
->mag_last_free
= NULL
;
3388 tiny_mag_ptr
->mag_last_free_rgn
= NULL
;
3389 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3390 CHECK(szone
, __PRETTY_FUNCTION__
);
3391 ptr
= (void *)((uintptr_t)ptr
& ~ (TINY_QUANTUM
- 1));
3392 if (cleared_requested
) {
3393 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3396 if (LOG(szone
,ptr
)) {
3397 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr
, msize
);
3402 #endif /* TINY_CACHE */
3405 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3407 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3408 CHECK(szone
, __PRETTY_FUNCTION__
);
3409 if (cleared_requested
) {
3410 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3415 if (tiny_get_region_from_depot(szone
, tiny_mag_ptr
, mag_index
, msize
)) {
3416 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3418 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3419 CHECK(szone
, __PRETTY_FUNCTION__
);
3420 if (cleared_requested
) {
3421 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3427 // The magazine is exhausted. A new region (heap) must be allocated to satisfy this call to malloc().
3428 // The allocation, an mmap() system call, will be performed outside the magazine spin locks by the first
3429 // thread that suffers the exhaustion. That thread sets "alloc_underway" and enters a critical section.
3430 // Threads arriving here later are excluded from the critical section, yield the CPU, and then retry the
3431 // allocation. After some time the magazine is resupplied, the original thread leaves with its allocation,
3432 // and retry-ing threads succeed in the code just above.
3433 if (!tiny_mag_ptr
->alloc_underway
) {
3436 // time to create a new region (do this outside the magazine lock)
3437 tiny_mag_ptr
->alloc_underway
= TRUE
;
3439 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3440 fresh_region
= allocate_pages_securely(szone
, TINY_REGION_SIZE
, TINY_BLOCKS_ALIGN
, VM_MEMORY_MALLOC_TINY
);
3441 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3443 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
, fresh_region
, TINY_REGION_SIZE
); // DTrace USDT Probe
3445 if (!fresh_region
) { // out of memory!
3446 tiny_mag_ptr
->alloc_underway
= FALSE
;
3448 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3452 ptr
= tiny_malloc_from_region_no_lock(szone
, tiny_mag_ptr
, mag_index
, msize
, fresh_region
);
3454 // we don't clear because this freshly allocated space is pristine
3455 tiny_mag_ptr
->alloc_underway
= FALSE
;
3457 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3458 CHECK(szone
, __PRETTY_FUNCTION__
);
3461 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3463 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3469 static NOINLINE
void
3470 free_tiny_botch(szone_t
*szone
, free_list_t
*ptr
)
3472 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3473 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3474 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3475 szone_error(szone
, 1, "double free", ptr
, NULL
);
3479 free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
)
3483 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
3484 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3486 // ptr is known to be in tiny_region
3488 msize
= TINY_MSIZE_FOR_BYTES(known_size
+ TINY_QUANTUM
- 1);
3490 msize
= get_tiny_meta_header(ptr
, &is_free
);
3492 free_tiny_botch(szone
, ptr
);
3498 malloc_printf("*** free_tiny() block in use is too large: %p\n", ptr
);
3503 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3506 // Depot does not participate in TINY_CACHE since it can't be directly malloc()'d
3507 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3508 if (msize
< TINY_QUANTUM
) { // to see if the bits fit in the last 4 bits
3509 void *ptr2
= tiny_mag_ptr
->mag_last_free
; // Might be NULL
3510 region_t rgn2
= tiny_mag_ptr
->mag_last_free_rgn
;
3512 /* check that we don't already have this pointer in the cache */
3513 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (TINY_QUANTUM
- 1))) {
3514 free_tiny_botch(szone
, ptr
);
3518 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
3519 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
3521 tiny_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
3522 tiny_mag_ptr
->mag_last_free_rgn
= tiny_region
;
3525 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3526 CHECK(szone
, __PRETTY_FUNCTION__
);
3530 msize
= (uintptr_t)ptr2
& (TINY_QUANTUM
- 1);
3531 ptr
= (void *)(((uintptr_t)ptr2
) & ~(TINY_QUANTUM
- 1));
3535 #endif /* TINY_CACHE */
3537 // Now in the time it took to acquire the lock, the region may have migrated
3538 // from one magazine to another. I.e. trailer->mag_index is volatile.
3539 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
3540 // is stale. If so, keep on tryin' ...
3541 region_trailer_t
*trailer
= REGION_TRAILER_FOR_TINY_REGION(tiny_region
);
3542 mag_index_t refreshed_index
;
3544 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
3546 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3548 mag_index
= refreshed_index
;
3549 tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3550 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3553 if (tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
))
3554 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3556 CHECK(szone
, __PRETTY_FUNCTION__
);
3560 print_tiny_free_list(szone_t
*szone
)
3563 _SIMPLE_STRING b
= _simple_salloc();
3564 mag_index_t mag_index
;
3567 _simple_sappend(b
, "tiny free sizes:\n");
3568 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3570 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
3571 while (slot
< NUM_TINY_SLOTS
) {
3572 ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3574 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== NUM_TINY_SLOTS
-1) ? ">=" : "",
3575 (slot
+1)*TINY_QUANTUM
, free_list_count(szone
, ptr
));
3579 _simple_sappend(b
,"\n");
3581 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3587 print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
)
3589 unsigned counts
[1024];
3590 unsigned in_use
= 0;
3591 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
3592 uintptr_t current
= start
+ bytes_at_end
;
3593 uintptr_t limit
= (uintptr_t)TINY_REGION_END(region
) - bytes_at_end
;
3598 uintptr_t pgTot
= 0;
3600 if (region
== HASHRING_REGION_DEALLOCATED
) {
3601 if ((b
= _simple_salloc()) != NULL
) {
3602 _simple_sprintf(b
, "Tiny region [unknown address] was returned to the OS\n");
3603 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3609 memset(counts
, 0, sizeof(counts
));
3610 while (current
< limit
) {
3611 msize
= get_tiny_meta_header((void *)current
, &is_free
);
3612 if (is_free
& !msize
&& (current
== start
)) {
3613 // first block is all free
3614 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3615 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
3618 pgTot
+= (pgHi
- pgLo
);
3623 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
3628 if (msize
> NUM_TINY_SLOTS
)
3629 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current
, msize
);
3634 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3635 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3638 pgTot
+= (pgHi
- pgLo
);
3641 current
+= TINY_BYTES_FOR_MSIZE(msize
);
3643 if ((b
= _simple_salloc()) != NULL
) {
3644 _simple_sprintf(b
, "Tiny region [%p-%p, %y] \t", (void *)start
, TINY_REGION_END(region
), (int)TINY_REGION_SIZE
);
3645 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_TINY_REGION(region
));
3646 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_TINY_REGION(region
));
3647 if (bytes_at_end
|| bytes_at_start
)
3648 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
+ bytes_at_start
);
3649 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_TINY_REGION(region
)) {
3650 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
3652 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
3654 if (verbose
&& in_use
) {
3655 _simple_sappend(b
, "\n\tSizes in use: ");
3656 for (ci
= 0; ci
< 1024; ci
++)
3658 _simple_sprintf(b
, "%d[%d] ", TINY_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
3660 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3666 tiny_free_list_check(szone_t
*szone
, grain_t slot
)
3668 mag_index_t mag_index
;
3670 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3671 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3672 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3675 free_list_t
*ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3677 free_list_t
*previous
= NULL
;
3680 is_free
= tiny_meta_header_is_free(ptr
);
3682 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3683 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3686 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
3687 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3688 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3691 if (!tiny_region_for_ptr_no_lock(szone
, ptr
)) {
3692 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3693 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3696 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
3697 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3698 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3702 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3706 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3711 /********************* SMALL FREE LIST UTILITIES ************************/
3714 * Mark a block as free. Only the first quantum of a block is marked thusly,
3715 * the remainder are marked "middle".
3718 small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
)
3720 meta_headers
[index
] = msize
| SMALL_IS_FREE
;
3724 * Mark a block as in use. Only the first quantum of a block is marked thusly,
3725 * the remainder are marked "middle".
3728 small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
)
3730 meta_headers
[index
] = msize
;
3734 * Mark a quantum as being the second or later in a block.
3737 small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
)
3739 meta_headers
[index
] = 0;
3743 * Adds an item to the proper free list, and also marks the meta-header of the
3745 * Assumes szone has been locked
3748 small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3750 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3751 free_list_t
*free_ptr
= ptr
;
3752 free_list_t
*free_head
= small_mag_ptr
->mag_free_list
[slot
];
3756 if (LOG(szone
,ptr
)) {
3757 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3759 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
3760 szone_error(szone
, 1, "small_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
3763 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), msize
);
3767 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
3768 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
3769 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
3771 if (!SMALL_PTR_IS_FREE(free_head
)) {
3772 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
3773 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
3776 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
3778 BITMAPN_SET(small_mag_ptr
->mag_bitmap
, slot
);
3780 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
3781 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
3783 small_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
3785 // Store msize at the end of the block denoted by "ptr" (i.e. at a negative offset from "follower")
3786 follower
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
3787 SMALL_PREVIOUS_MSIZE(follower
) = msize
;
3791 * Removes the item pointed to by ptr in the proper free list.
3792 * Assumes szone has been locked
3795 small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3797 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3798 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
3800 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
3801 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
3804 if (LOG(szone
,ptr
)) {
3805 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3810 // The block to remove is the head of the free list
3812 if (small_mag_ptr
->mag_free_list
[slot
] != ptr
) {
3813 szone_error(szone
, 1, "small_free_list_remove_ptr: Internal invariant broken (small_mag_ptr->mag_free_list[slot])", ptr
,
3814 "ptr=%p slot=%d msize=%d small_mag_ptr->mag_free_list[slot]=%p\n",
3815 ptr
, slot
, msize
, (void *)small_mag_ptr
->mag_free_list
[slot
]);
3819 small_mag_ptr
->mag_free_list
[slot
] = next
;
3820 if (!next
) BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
3822 // We know free_ptr is already checksummed, so we don't need to do it
3824 previous
->next
= free_ptr
->next
;
3827 // We know free_ptr is already checksummed, so we don't need to do it
3829 next
->previous
= free_ptr
->previous
;
3834 * small_region_for_ptr_no_lock - Returns the small region containing the pointer,
3835 * or NULL if not found.
3837 static INLINE region_t
3838 small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
3840 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3841 szone
->small_region_generation
->num_regions_allocated
,
3842 szone
->small_region_generation
->num_regions_allocated_shift
,
3843 SMALL_REGION_FOR_PTR(ptr
));
3848 small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
) {
3849 void *last_block
, *previous_block
;
3850 msize_t last_msize
, previous_msize
, last_index
;
3852 // It is possible that the block prior to the last block in the region has
3853 // been free'd, but was not coalesced with the free bytes at the end of the
3854 // block, since we treat the bytes at the end of the region as "in use" in
3855 // the meta headers. Attempt to coalesce the last block with the previous
3856 // block, so we don't violate the "no consecutive free blocks" invariant.
3858 // FIXME: If we could calculate the previous small free size in the same
3859 // manner as tiny_previous_preceding_free, it would eliminate the
3860 // index & previous msize checks, which are a guard against reading
3861 // bogus data out of in-use or written-on-freed memory.
3863 // FIXME: Need to investigate how much work would be required to increase
3864 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
3865 // than performing this workaround.
3867 if (small_mag_ptr
->mag_bytes_free_at_end
) {
3868 last_block
= SMALL_REGION_END(small_mag_ptr
->mag_last_region
) - small_mag_ptr
->mag_bytes_free_at_end
;
3869 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
3871 last_index
= SMALL_META_INDEX_FOR_PTR(last_block
);
3872 previous_msize
= SMALL_PREVIOUS_MSIZE(last_block
);
3874 if (last_index
&& (previous_msize
<= last_index
)) {
3875 previous_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3876 if (*SMALL_METADATA_FOR_PTR(previous_block
) == (previous_msize
| SMALL_IS_FREE
)) {
3877 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(last_block
);
3879 small_meta_header_set_middle(meta_headers
, last_index
);
3880 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous_block
, previous_msize
);
3881 last_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3882 last_msize
+= previous_msize
;
3886 // splice last_block into the free list
3887 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3888 small_mag_ptr
->mag_bytes_free_at_end
= 0;
3892 if (small_mag_ptr
->mag_bytes_free_at_start
) {
3893 last_block
= SMALL_REGION_ADDRESS(small_mag_ptr
->mag_last_region
);
3894 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_start
);
3896 void *next_block
= (void *) ((uintptr_t)last_block
+ small_mag_ptr
->mag_bytes_free_at_start
);
3897 if (SMALL_PTR_IS_FREE(next_block
)) {
3898 msize_t next_msize
= SMALL_PTR_SIZE(next_block
);
3900 small_meta_header_set_middle(SMALL_META_HEADER_FOR_PTR(next_block
), SMALL_META_INDEX_FOR_PTR(next_block
));
3901 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
3902 last_msize
+= next_msize
;
3905 // splice last_block into the free list
3906 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3907 small_mag_ptr
->mag_bytes_free_at_start
= 0;
3911 // TODO: Will we ever need to coalesce the blocks at the beginning and end when we finalize?
3913 small_mag_ptr
->mag_last_region
= NULL
;
3917 small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3918 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3919 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3920 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3921 uintptr_t current
= start
;
3922 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3923 int total_alloc
= 0;
3925 while (current
< limit
) {
3926 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3927 msize_t msize_and_free
= meta_headers
[index
];
3928 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3929 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3933 malloc_printf("*** small_free_detach_region error with %p: msize=%d is_free =%d\n",
3934 (void *)current
, msize
, is_free
);
3939 small_free_list_remove_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3943 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3949 small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3950 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3951 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3952 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3953 uintptr_t current
= start
;
3954 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3955 size_t total_alloc
= 0;
3957 while (current
< limit
) {
3958 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3959 msize_t msize_and_free
= meta_headers
[index
];
3960 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3961 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3965 malloc_printf("*** small_free_reattach_region error with %p: msize=%d is_free =%d\n",
3966 (void *)current
, msize
, is_free
);
3971 small_free_list_add_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3973 total_alloc
+= SMALL_BYTES_FOR_MSIZE(msize
);
3975 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3981 uint16_t pnum
, size
;
3984 static void NOINLINE
/* want private stack frame for automatic array */
3985 small_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
3986 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3987 uintptr_t current
= start
;
3988 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3989 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(start
);
3990 small_pg_pair_t advisory
[((SMALL_REGION_PAYLOAD_BYTES
+ vm_page_size
- 1) >> vm_page_shift
) >> 1]; // 4096bytes stack allocated
3993 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
3995 while (current
< limit
) {
3996 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3997 msize_t msize_and_free
= meta_headers
[index
];
3998 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3999 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4001 if (is_free
&& !msize
&& (current
== start
)) {
4003 // first block is all free
4004 malloc_printf("*** small_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
4005 (void *)current
, msize
, is_free
);
4007 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
4008 uintptr_t pgHi
= trunc_page(start
+ SMALL_REGION_SIZE
- sizeof(msize_t
));
4011 advisory
[advisories
].pnum
= (pgLo
- start
) >> vm_page_shift
;
4012 advisory
[advisories
].size
= (pgHi
- pgLo
) >> vm_page_shift
;
4019 malloc_printf("*** small_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
4020 (void *)current
, msize
, is_free
);
4025 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
4026 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
4029 advisory
[advisories
].pnum
= (pgLo
- start
) >> vm_page_shift
;
4030 advisory
[advisories
].size
= (pgHi
- pgLo
) >> vm_page_shift
;
4034 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
4037 if (advisories
> 0) {
4040 OSAtomicIncrement32Barrier(&(REGION_TRAILER_FOR_SMALL_REGION(r
)->pinned_to_depot
));
4041 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
);
4042 for (i
= 0; i
< advisories
; ++i
) {
4043 uintptr_t addr
= (advisory
[i
].pnum
<< vm_page_shift
) + start
;
4044 size_t size
= advisory
[i
].size
<< vm_page_shift
;
4046 #if TARGET_OS_EMBEDDED
4047 madvise_free_range(szone
, r
, addr
, addr
+ size
, NULL
);
4049 madvise_free_range(szone
, r
, addr
, addr
+ size
);
4052 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
4053 OSAtomicDecrement32Barrier(&(REGION_TRAILER_FOR_SMALL_REGION(r
)->pinned_to_depot
));
4058 small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
4060 if (0 < node
->bytes_used
||
4061 0 < node
->pinned_to_depot
||
4062 depot_ptr
->recirculation_entries
< (szone
->num_small_magazines
* 2)) {
4066 // disconnect first node from Depot
4067 recirc_list_extract(szone
, depot_ptr
, node
);
4069 // Iterate the region pulling its free entries off the (locked) Depot's free list
4070 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
4071 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
4073 if (0 == objects_in_use
) {
4074 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
4075 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
4076 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
4077 szone
->small_region_generation
->num_regions_allocated
,
4078 szone
->small_region_generation
->num_regions_allocated_shift
, sparse_region
);
4079 if (NULL
== pSlot
) {
4080 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock hash lookup failed:", NULL
, "%p\n", sparse_region
);
4083 *pSlot
= HASHRING_REGION_DEALLOCATED
;
4084 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
4085 __sync_fetch_and_add( &(szone
->num_small_regions_dealloc
), 1); // Atomically increment num_small_regions_dealloc
4087 // Caller will transfer ownership of the region back to the OS with no locks held
4088 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
, SMALL_REGION_SIZE
); // DTrace USDT Probe
4089 return sparse_region
;
4092 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
4098 small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
4100 // The entire magazine crossed the "emptiness threshold". Transfer a region
4101 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
4102 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
4103 region_trailer_t
*node
= small_mag_ptr
->firstNode
;
4105 while (node
&& !node
->recirc_suitable
) {
4111 malloc_printf("*** small_free_do_recirc_to_depot end of list\n");
4113 return TRUE
; // Caller must SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4116 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
4118 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at start
4119 if (sparse_region
== small_mag_ptr
->mag_last_region
&& (small_mag_ptr
->mag_bytes_free_at_end
|| small_mag_ptr
->mag_bytes_free_at_start
)) {
4120 small_finalize_region(szone
, small_mag_ptr
);
4123 // disconnect "suitable" node from magazine
4124 recirc_list_extract(szone
, small_mag_ptr
, node
);
4126 // Iterate the region pulling its free entries off its (locked) magazine's free list
4127 int objects_in_use
= small_free_detach_region(szone
, small_mag_ptr
, sparse_region
);
4128 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
4130 // hand over the region to the (locked) Depot
4131 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
4132 // this will cause small_free_list_add_ptr called by small_free_reattach_region to use
4133 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
4134 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
4135 node
->pinned_to_depot
= 0;
4137 // Iterate the region putting its free entries on Depot's free list
4138 size_t bytes_inplay
= small_free_reattach_region(szone
, depot_ptr
, sparse_region
);
4140 small_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
4141 small_mag_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
4142 small_mag_ptr
->mag_num_objects
-= objects_in_use
;
4144 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
); // Unlock the originating magazine
4146 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
4147 depot_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4148 depot_ptr
->mag_num_objects
+= objects_in_use
;
4150 // connect to Depot as last node
4151 recirc_list_splice_last(szone
, depot_ptr
, node
);
4153 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (void *)sparse_region
, SMALL_REGION_SIZE
,
4154 (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
4156 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
4157 small_free_scan_madvise_free(szone
, depot_ptr
, sparse_region
);
4159 // If the region is entirely empty vm_deallocate() it outside the depot lock
4160 region_t r_dealloc
= small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
4161 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4163 deallocate_pages(szone
, r_dealloc
, SMALL_REGION_SIZE
, 0);
4164 return FALSE
; // Caller need not unlock the originating magazine
4168 small_find_msize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4171 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4172 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4173 free_list_t
**the_slot
= free_list
+ slot
;
4174 free_list_t
**limit
;
4177 // Assumes we've locked the magazine
4178 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4180 // Look for an exact match by checking the freelist for this msize.
4183 return SMALL_REGION_FOR_PTR(ptr
);
4185 // Mask off the bits representing slots holding free blocks smaller than
4186 // the size we need.
4187 if (szone
->is_largemem
) {
4188 // BITMAPN_CTZ implementation
4189 unsigned idx
= slot
>> 5;
4191 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4192 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4193 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4198 // Check for fallthrough: No bits set in bitmap
4199 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4202 // Start looking at the first set bit, plus 32 bits for every word of
4203 // zeroes or entries that were too small.
4204 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4206 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4210 slot
= BITMAP32_CTZ((&bitmap
));
4212 limit
= free_list
+ szone
->num_small_slots
- 1;
4215 if (free_list
< limit
) {
4218 return SMALL_REGION_FOR_PTR(ptr
);
4220 /* Shouldn't happen. Fall through to look at last slot. */
4222 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4227 // We are now looking at the last slot, which contains blocks equal to, or
4228 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4231 return SMALL_REGION_FOR_PTR(ptr
);
4237 small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4239 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
4241 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
4242 if (szone
->num_small_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
4246 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
4247 szone_error(szone
, 1, "small_get_region_from_depot called for magazine index -1", NULL
, NULL
);
4252 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
4254 // Appropriate a Depot'd region that can satisfy requested msize.
4255 region_trailer_t
*node
;
4256 region_t sparse_region
;
4259 sparse_region
= small_find_msize_region(szone
, depot_ptr
, DEPOT_MAGAZINE_INDEX
, msize
);
4260 if (NULL
== sparse_region
) { // Depot empty?
4261 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4265 node
= REGION_TRAILER_FOR_SMALL_REGION(sparse_region
);
4266 if (0 >= node
->pinned_to_depot
)
4269 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4271 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
4274 // disconnect node from Depot
4275 recirc_list_extract(szone
, depot_ptr
, node
);
4277 // Iterate the region pulling its free entries off the (locked) Depot's free list
4278 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
4280 // Transfer ownership of the region
4281 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = mag_index
;
4282 node
->pinned_to_depot
= 0;
4284 // Iterate the region putting its free entries on its new (locked) magazine's free list
4285 size_t bytes_inplay
= small_free_reattach_region(szone
, small_mag_ptr
, sparse_region
);
4287 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
4288 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
4289 depot_ptr
->mag_num_objects
-= objects_in_use
;
4291 small_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
4292 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4293 small_mag_ptr
->mag_num_objects
+= objects_in_use
;
4295 // connect to magazine as first node
4296 recirc_list_splice_first(szone
, small_mag_ptr
, node
);
4298 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4300 // madvise() outside the Depot lock
4301 #if TARGET_OS_EMBEDDED
4302 if (node
->failedREUSE
) {
4304 if (node
->failedREUSE
||
4305 -1 == madvise((void *)sparse_region
, SMALL_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
4307 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
4309 szone_error(szone
, 0, "small_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed",
4310 sparse_region
, "length=%d\n", SMALL_REGION_PAYLOAD_BYTES
);
4312 node
->failedREUSE
= TRUE
;
4315 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (void *)sparse_region
, SMALL_REGION_SIZE
,
4316 (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
4321 #define K 1.5 // headroom measured in number of 8Mb regions
4322 #define DENSITY_THRESHOLD(a) \
4323 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
4325 static INLINE boolean_t
4326 small_free_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, region_t region
, void *ptr
, msize_t msize
)
4328 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4329 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4330 void *original_ptr
= ptr
;
4331 size_t original_size
= SMALL_BYTES_FOR_MSIZE(msize
);
4332 unsigned char *next_block
= ((unsigned char *)ptr
+ original_size
);
4333 msize_t next_index
= index
+ msize
;
4334 msize_t previous_msize
, next_msize
;
4338 if (LOG(szone
,ptr
)) {
4339 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
4342 szone_error(szone
, 1, "trying to free small block that is too small", ptr
,
4343 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
4347 // We try to coalesce this block with the preceeding one
4348 if (index
&& (SMALL_PREVIOUS_MSIZE(ptr
) <= index
)) {
4349 previous_msize
= SMALL_PREVIOUS_MSIZE(ptr
);
4350 if (meta_headers
[index
- previous_msize
] == (previous_msize
| SMALL_IS_FREE
)) {
4351 previous
= (void *)((uintptr_t)ptr
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
4352 // previous is really to be coalesced
4354 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
4355 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
4358 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous
, previous_msize
);
4359 small_meta_header_set_middle(meta_headers
, index
);
4361 msize
+= previous_msize
;
4362 index
-= previous_msize
;
4365 // We try to coalesce with the next block
4366 if ((next_block
< SMALL_REGION_END(region
)) && (meta_headers
[next_index
] & SMALL_IS_FREE
)) {
4367 // next block is free, we coalesce
4368 next_msize
= meta_headers
[next_index
] & ~ SMALL_IS_FREE
;
4371 malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n",
4372 ptr
, msize
, next_block
, next_msize
);
4374 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4375 small_meta_header_set_middle(meta_headers
, next_index
);
4376 msize
+= next_msize
;
4378 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) {
4380 szone_error(szone
, 1, "incorrect size information - block header was damaged", ptr
, NULL
);
4382 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
4385 small_free_list_add_ptr(szone
, small_mag_ptr
, ptr
, msize
);
4386 small_mag_ptr
->mag_num_objects
--;
4387 // we use original_size and not msize to avoid double counting the coalesced blocks
4388 small_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
4390 // Update this region's bytes in use count
4391 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
4392 size_t bytes_used
= node
->bytes_used
- original_size
;
4393 node
->bytes_used
= bytes_used
;
4395 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
4396 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
4397 if (szone
->num_small_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
4399 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4400 // Emptiness discriminant
4401 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4402 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
4403 recirculation candidates list. */
4404 node
->recirc_suitable
= TRUE
;
4406 /* After this free, we've found the region is still dense, so it must have been even more so before
4407 the free. That implies the region is already correctly marked. Do nothing. */
4410 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
4411 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
4412 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
4414 size_t a
= small_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
4415 size_t u
= small_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
4417 if (a
- u
> ((3 * SMALL_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
)) {
4418 return small_free_do_recirc_to_depot(szone
, small_mag_ptr
, mag_index
);
4423 // Freed to Depot. N.B. Lock on small_magazines[DEPOT_MAGAZINE_INDEX] is already held
4424 // Calcuate the first page in the coalesced block that would be safe to mark MADV_FREE
4425 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
4426 uintptr_t round_safe
= round_page(safe_ptr
);
4428 // Calcuate the last page in the coalesced block that would be safe to mark MADV_FREE
4429 uintptr_t safe_extent
= (uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
4430 uintptr_t trunc_extent
= trunc_page(safe_extent
);
4432 // The newly freed block may complete a span of bytes that cover one or more pages. Mark the span with MADV_FREE.
4433 if (round_safe
< trunc_extent
) { // Safe area covers a page (perhaps many)
4434 uintptr_t lo
= trunc_page((uintptr_t)original_ptr
);
4435 uintptr_t hi
= round_page((uintptr_t)original_ptr
+ original_size
);
4437 small_free_list_remove_ptr(szone
, small_mag_ptr
, ptr
, msize
);
4438 small_meta_header_set_in_use(meta_headers
, index
, msize
);
4440 OSAtomicIncrement32Barrier(&(node
->pinned_to_depot
));
4441 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4442 #if TARGET_OS_EMBEDDED
4443 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
), &szone
->last_small_advise
);
4445 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
));
4447 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4448 OSAtomicDecrement32Barrier(&(node
->pinned_to_depot
));
4450 small_meta_header_set_is_free(meta_headers
, index
, msize
);
4451 small_free_list_add_ptr(szone
, small_mag_ptr
, ptr
, msize
);
4454 #if !TARGET_OS_EMBEDDED
4455 if (0 < bytes_used
|| 0 < node
->pinned_to_depot
) {
4456 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
4457 so as to avoid thrashing between the Depot's free list and a magazines's free list
4458 with detach_region/reattach_region */
4460 /* Depot'd region is just now empty. Consider return to OS. */
4461 region_t r_dealloc
= small_free_try_depot_unmap_no_lock(szone
, small_mag_ptr
, node
);
4462 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4464 deallocate_pages(szone
, r_dealloc
, SMALL_REGION_SIZE
, 0);
4465 return FALSE
; // Caller need not unlock
4470 return TRUE
; // Caller must do SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr)
4473 // Allocates from the last region or a freshly allocated region
4475 small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
4476 msize_t msize
, void *aligned_address
)
4480 // Before anything we transform the mag_bytes_free_at_end or mag_bytes_free_at_start - if any - to a regular free block
4481 /* FIXME: last_block needs to be coalesced with previous entry if free, <rdar://5462322> */
4482 if (small_mag_ptr
->mag_bytes_free_at_end
|| small_mag_ptr
->mag_bytes_free_at_start
)
4483 small_finalize_region(szone
, small_mag_ptr
);
4485 // Here find the only place in smallville that (infrequently) takes the small_regions_lock.
4486 // Only one thread at a time should be permitted to assess the density of the hash
4487 // ring and adjust if needed.
4488 // Only one thread at a time should be permitted to insert its new region on
4490 // It is safe for all other threads to read the hash ring (hashed_regions) and
4491 // the associated sizes (num_regions_allocated and num_small_regions).
4493 LOCK(szone
->small_regions_lock
);
4494 // Check to see if the hash ring of small regions needs to grow. Try to
4495 // avoid the hash ring becoming too dense.
4496 if (szone
->small_region_generation
->num_regions_allocated
< (2 * szone
->num_small_regions
)) {
4497 region_t
*new_regions
;
4499 size_t new_shift
= szone
->small_region_generation
->num_regions_allocated_shift
; // In/Out parameter
4500 new_regions
= hash_regions_grow_no_lock(szone
, szone
->small_region_generation
->hashed_regions
,
4501 szone
->small_region_generation
->num_regions_allocated
,
4504 // Do not deallocate the current hashed_regions allocation since someone
4505 // may be iterating it. Instead, just leak it.
4507 // Prepare to advance to the "next generation" of the hash ring.
4508 szone
->small_region_generation
->nextgen
->hashed_regions
= new_regions
;
4509 szone
->small_region_generation
->nextgen
->num_regions_allocated
= new_size
;
4510 szone
->small_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
4512 // Throw the switch to atomically advance to the next generation.
4513 szone
->small_region_generation
= szone
->small_region_generation
->nextgen
;
4514 // Ensure everyone sees the advance.
4517 // Tag the region at "aligned_address" as belonging to us,
4518 // and so put it under the protection of the magazine lock we are holding.
4519 // Do this before advertising "aligned_address" on the hash ring(!)
4520 MAGAZINE_INDEX_FOR_SMALL_REGION(aligned_address
) = mag_index
;
4522 // Insert the new region into the hash ring, and update malloc statistics
4523 hash_region_insert_no_lock(szone
->small_region_generation
->hashed_regions
,
4524 szone
->small_region_generation
->num_regions_allocated
,
4525 szone
->small_region_generation
->num_regions_allocated_shift
,
4528 szone
->num_small_regions
++;
4530 UNLOCK(szone
->small_regions_lock
);
4532 small_mag_ptr
->mag_last_region
= aligned_address
;
4533 BYTES_USED_FOR_SMALL_REGION(aligned_address
) = SMALL_BYTES_FOR_MSIZE(msize
);
4535 int offset_msize
= malloc_entropy
[1] & SMALL_ENTROPY_MASK
;
4537 if (getenv("MallocASLRForce")) offset_msize
= strtol(getenv("MallocASLRForce"), NULL
, 0) & SMALL_ENTROPY_MASK
;
4538 if (getenv("MallocASLRPrint")) malloc_printf("Region: %p offset: %d\n", aligned_address
, offset_msize
);
4541 int offset_msize
= 0;
4543 ptr
= (void *)((uintptr_t) aligned_address
+ SMALL_BYTES_FOR_MSIZE(offset_msize
));
4544 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), offset_msize
, msize
);
4545 small_mag_ptr
->mag_num_objects
++;
4546 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(msize
);
4547 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4549 // add a big free block at the end
4550 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), offset_msize
+ msize
, NUM_SMALL_BLOCKS
- msize
- offset_msize
);
4551 small_mag_ptr
->mag_bytes_free_at_end
= SMALL_BYTES_FOR_MSIZE(NUM_SMALL_BLOCKS
- msize
- offset_msize
);
4554 // add a big free block at the start
4555 small_mag_ptr
->mag_bytes_free_at_start
= SMALL_BYTES_FOR_MSIZE(offset_msize
);
4557 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), 0, offset_msize
);
4560 small_mag_ptr
->mag_bytes_free_at_start
= 0;
4563 // connect to magazine as last node
4564 recirc_list_splice_last(szone
, small_mag_ptr
, REGION_TRAILER_FOR_SMALL_REGION(aligned_address
));
4569 static INLINE
void *
4570 small_try_shrink_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_good_size
)
4572 msize_t new_msize
= SMALL_MSIZE_FOR_BYTES(new_good_size
);
4573 msize_t mshrinkage
= SMALL_MSIZE_FOR_BYTES(old_size
) - new_msize
;
4576 void *q
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
));
4577 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4578 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4579 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4581 // Mark q as block header and in-use, thus creating two blocks.
4582 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), new_msize
);
4583 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mshrinkage
);
4584 small_mag_ptr
->mag_num_objects
++;
4586 SZONE_MAGAZINE_PTR_UNLOCK(szone
,small_mag_ptr
);
4587 szone_free(szone
, q
); // avoid inlining free_small(szone, q, ...);
4593 static INLINE boolean_t
4594 small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
4596 // returns 1 on success
4597 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4599 msize_t old_msize
, new_msize
;
4600 unsigned next_index
;
4602 msize_t next_msize_and_free
;
4604 msize_t next_msize
, leftover_msize
;
4607 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4608 old_msize
= SMALL_MSIZE_FOR_BYTES(old_size
);
4609 new_msize
= SMALL_MSIZE_FOR_BYTES(new_size
+ SMALL_QUANTUM
- 1);
4610 next_index
= index
+ old_msize
;
4612 if (next_index
>= NUM_SMALL_BLOCKS
) {
4615 next_block
= (char *)ptr
+ old_size
;
4618 if ((uintptr_t)next_block
& (SMALL_QUANTUM
- 1)) {
4619 szone_error(szone
, 1, "internal invariant broken in realloc(next_block)", next_block
, NULL
);
4621 if (meta_headers
[index
] != old_msize
)
4622 malloc_printf("*** small_try_realloc_in_place incorrect old %d %d\n",
4623 meta_headers
[index
], old_msize
);
4626 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4627 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4628 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4631 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
4634 next_msize_and_free
= meta_headers
[next_index
];
4635 is_free
= next_msize_and_free
& SMALL_IS_FREE
;
4637 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4638 return 0; // next_block is in use;
4640 next_msize
= next_msize_and_free
& ~ SMALL_IS_FREE
;
4641 if (old_msize
+ next_msize
< new_msize
) {
4642 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4643 return 0; // even with next block, not enough
4646 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
4649 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4650 small_meta_header_set_middle(meta_headers
, next_index
);
4651 leftover_msize
= old_msize
+ next_msize
- new_msize
;
4652 if (leftover_msize
) {
4653 /* there's some left, so put the remainder back */
4654 leftover
= (unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
);
4656 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover
, leftover_msize
);
4659 if (SMALL_BYTES_FOR_MSIZE(new_msize
) > szone
->large_threshold
) {
4660 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize
);
4663 small_meta_header_set_in_use(meta_headers
, index
, new_msize
);
4665 if (LOG(szone
,ptr
)) {
4666 malloc_printf("in small_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, *SMALL_METADATA_FOR_PTR(ptr
));
4669 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4671 // Update this region's bytes in use count
4672 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4673 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4674 node
->bytes_used
= bytes_used
;
4676 // Emptiness discriminant
4677 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4678 /* After this reallocation the region is still sparse, so it must have been even more so before
4679 the reallocation. That implies the region is already correctly marked. Do nothing. */
4681 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
4682 recirculation candidates list. */
4683 node
->recirc_suitable
= FALSE
;
4686 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4687 CHECK(szone
, __PRETTY_FUNCTION__
);
4692 small_check_region(szone_t
*szone
, region_t region
)
4694 unsigned char *ptr
= SMALL_REGION_ADDRESS(region
);
4695 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4696 unsigned char *region_end
= SMALL_REGION_END(region
);
4697 msize_t prev_free
= 0;
4699 msize_t msize_and_free
;
4701 free_list_t
*free_head
;
4702 void *previous
, *next
;
4704 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4705 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4708 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4710 if (region
== small_mag_ptr
->mag_last_region
) {
4711 ptr
+= small_mag_ptr
->mag_bytes_free_at_start
;
4712 region_end
-= small_mag_ptr
->mag_bytes_free_at_end
;
4715 while (ptr
< region_end
) {
4716 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4717 msize_and_free
= meta_headers
[index
];
4718 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4720 msize
= msize_and_free
;
4722 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
4723 ptr
, szone
->num_small_regions
, region_end
);
4726 #if !RELAXED_INVARIANT_CHECKS
4727 if (SMALL_BYTES_FOR_MSIZE(msize
) > szone
->large_threshold
) {
4728 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
4729 ptr
, msize_and_free
);
4732 #endif // RELAXED_INVARIANT_CHECKS
4733 ptr
+= SMALL_BYTES_FOR_MSIZE(msize
);
4737 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4738 free_head
= (free_list_t
*)ptr
;
4739 follower
= (msize_t
*)FOLLOWING_SMALL_PTR(ptr
, msize
);
4741 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr
, msize
);
4744 #if !RELAXED_INVARIANT_CHECKS
4746 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr
);
4750 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
4751 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
4752 if (previous
&& !SMALL_PTR_IS_FREE(previous
)) {
4753 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
4754 ptr
, free_head
->previous
);
4757 if (next
&& !SMALL_PTR_IS_FREE(next
)) {
4758 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr
);
4761 if (SMALL_PREVIOUS_MSIZE(follower
) != msize
) {
4762 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
4763 "(end marker incorrect) should be %d; in fact %d\n",
4764 ptr
, follower
, SMALL_REGION_ADDRESS(region
), region_end
, msize
, SMALL_PREVIOUS_MSIZE(follower
));
4767 ptr
= (unsigned char *)follower
;
4768 prev_free
= SMALL_IS_FREE
;
4774 static kern_return_t
4775 small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
4776 memory_reader_t reader
, vm_range_recorder_t recorder
)
4781 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4786 vm_range_t admin_range
;
4787 vm_range_t ptr_range
;
4788 unsigned char *mapped_region
;
4789 msize_t
*block_header
;
4790 unsigned block_index
;
4791 unsigned block_limit
;
4792 msize_t msize_and_free
;
4794 magazine_t
*small_mag_base
= NULL
;
4796 region_hash_generation_t
*srg_ptr
;
4797 err
= reader(task
, (vm_address_t
)szone
->small_region_generation
, sizeof(region_hash_generation_t
), (void **)&srg_ptr
);
4798 if (err
) return err
;
4800 num_regions
= srg_ptr
->num_regions_allocated
;
4801 err
= reader(task
, (vm_address_t
)srg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
4802 if (err
) return err
;
4804 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4805 // Map in all active magazines. Do this outside the iteration over regions.
4806 err
= reader(task
, (vm_address_t
)(szone
->small_magazines
),
4807 szone
->num_small_magazines
*sizeof(magazine_t
),(void **)&small_mag_base
);
4808 if (err
) return err
;
4811 for (index
= 0; index
< num_regions
; ++index
) {
4812 region
= regions
[index
];
4813 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
4814 range
.address
= (vm_address_t
)SMALL_REGION_ADDRESS(region
);
4815 range
.size
= SMALL_REGION_SIZE
;
4816 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4817 admin_range
.address
= range
.address
+ SMALL_METADATA_START
;
4818 admin_range
.size
= SMALL_METADATA_SIZE
;
4819 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
4821 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
4822 ptr_range
.address
= range
.address
;
4823 ptr_range
.size
= NUM_SMALL_BLOCKS
* SMALL_QUANTUM
;
4824 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
4826 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4827 void *mag_last_free
;
4828 vm_address_t mag_last_free_ptr
= 0;
4829 msize_t mag_last_free_msize
= 0;
4831 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
4835 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(mapped_region
);
4836 magazine_t
*small_mag_ptr
= small_mag_base
+ mag_index
;
4838 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4839 mag_last_free
= small_mag_ptr
->mag_last_free
;
4840 if (mag_last_free
) {
4841 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4842 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4845 for (mag_index
= 0; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4846 if ((void *)range
.address
== (small_mag_base
+ mag_index
)->mag_last_free_rgn
) {
4847 mag_last_free
= (small_mag_base
+ mag_index
)->mag_last_free
;
4848 if (mag_last_free
) {
4849 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4850 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4856 block_header
= (msize_t
*)(mapped_region
+ SMALL_METADATA_START
+ sizeof(region_trailer_t
));
4858 block_limit
= NUM_SMALL_BLOCKS
;
4859 if (region
== small_mag_ptr
->mag_last_region
) {
4860 block_index
+= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_start
);
4861 block_limit
-= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
4863 while (block_index
< block_limit
) {
4864 msize_and_free
= block_header
[block_index
];
4865 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4866 if (! (msize_and_free
& SMALL_IS_FREE
) &&
4867 range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
) != mag_last_free_ptr
) {
4869 buffer
[count
].address
= range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
);
4870 buffer
[count
].size
= SMALL_BYTES_FOR_MSIZE(msize
);
4872 if (count
>= MAX_RECORDER_BUFFER
) {
4873 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4879 return KERN_FAILURE
; // Somethings amiss. Avoid looping at this block_index.
4881 block_index
+= msize
;
4884 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4894 small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4898 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4899 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4900 free_list_t
**the_slot
= free_list
+ slot
;
4902 free_list_t
**limit
;
4904 msize_t leftover_msize
;
4905 free_list_t
*leftover_ptr
;
4907 // Assumes we've locked the region
4908 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4910 // Look for an exact match by checking the freelist for this msize.
4914 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4916 next
->previous
= ptr
->previous
;
4918 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4922 goto return_small_alloc
;
4925 // Mask off the bits representing slots holding free blocks smaller than
4926 // the size we need. If there are no larger free blocks, try allocating
4927 // from the free space at the end of the small region.
4928 if (szone
->is_largemem
) {
4929 // BITMAPN_CTZ implementation
4930 unsigned idx
= slot
>> 5;
4932 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4933 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4934 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4939 // Check for fallthrough: No bits set in bitmap
4940 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4941 goto try_small_from_end
;
4943 // Start looking at the first set bit, plus 32 bits for every word of
4944 // zeroes or entries that were too small.
4945 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4947 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4949 goto try_small_from_end
;
4951 slot
= BITMAP32_CTZ((&bitmap
));
4953 // FIXME: Explain use of - 1 here, last slot has special meaning
4954 limit
= free_list
+ szone
->num_small_slots
- 1;
4957 if (free_list
< limit
) {
4961 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4964 next
->previous
= ptr
->previous
;
4966 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4968 this_msize
= SMALL_PTR_SIZE(ptr
);
4969 goto add_leftover_and_proceed
;
4972 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4976 // We are now looking at the last slot, which contains blocks equal to, or
4977 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4978 // If the last freelist is not empty, and the head contains a block that is
4979 // larger than our request, then the remainder is put back on the free list.
4983 this_msize
= SMALL_PTR_SIZE(ptr
);
4984 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4985 if (this_msize
- msize
>= szone
->num_small_slots
) {
4986 // the leftover will go back to the free list, so we optimize by
4987 // modifying the free list rather than a pop and push of the head
4988 leftover_msize
= this_msize
- msize
;
4989 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4990 *limit
= leftover_ptr
;
4992 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
4994 leftover_ptr
->previous
= ptr
->previous
;
4995 leftover_ptr
->next
= ptr
->next
;
4996 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(leftover_ptr
),
4997 SMALL_META_INDEX_FOR_PTR(leftover_ptr
), leftover_msize
);
4998 // Store msize at the end of the block denoted by "leftover_ptr" (i.e. at a negative offset from follower)
4999 SMALL_PREVIOUS_MSIZE(FOLLOWING_SMALL_PTR(leftover_ptr
, leftover_msize
)) = leftover_msize
; // Access is safe
5001 if (LOG(szone
,ptr
)) {
5002 malloc_printf("in small_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr
, msize
, this_msize
);
5006 goto return_small_alloc
;
5009 next
->previous
= ptr
->previous
;
5012 goto add_leftover_and_proceed
;
5016 // Let's see if we can use small_mag_ptr->mag_bytes_free_at_end
5017 if (small_mag_ptr
->mag_bytes_free_at_end
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
5018 ptr
= (free_list_t
*)(SMALL_REGION_END(small_mag_ptr
->mag_last_region
) -
5019 small_mag_ptr
->mag_bytes_free_at_end
);
5020 small_mag_ptr
->mag_bytes_free_at_end
-= SMALL_BYTES_FOR_MSIZE(msize
);
5021 if (small_mag_ptr
->mag_bytes_free_at_end
) {
5022 // let's mark this block as in use to serve as boundary
5023 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
),
5024 SMALL_META_INDEX_FOR_PTR((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
)),
5025 SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
));
5028 goto return_small_alloc
;
5031 // Try from start if nothing left at end
5032 if (small_mag_ptr
->mag_bytes_free_at_start
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
5033 ptr
= (free_list_t
*)(SMALL_REGION_ADDRESS(small_mag_ptr
->mag_last_region
) +
5034 small_mag_ptr
->mag_bytes_free_at_start
- SMALL_BYTES_FOR_MSIZE(msize
));
5035 small_mag_ptr
->mag_bytes_free_at_start
-= SMALL_BYTES_FOR_MSIZE(msize
);
5036 if (small_mag_ptr
->mag_bytes_free_at_start
) {
5037 // let's mark this block as in use to serve as boundary
5038 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), 0, SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_start
));
5041 goto return_small_alloc
;
5046 add_leftover_and_proceed
:
5047 if (this_msize
> msize
) {
5048 leftover_msize
= this_msize
- msize
;
5049 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
5051 if (LOG(szone
,ptr
)) {
5052 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
5055 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover_ptr
, leftover_msize
);
5060 small_mag_ptr
->mag_num_objects
++;
5061 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(this_msize
);
5063 // Update this region's bytes in use count
5064 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5065 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(this_msize
);
5066 node
->bytes_used
= bytes_used
;
5068 // Emptiness discriminant
5069 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
5070 /* After this allocation the region is still sparse, so it must have been even more so before
5071 the allocation. That implies the region is already correctly marked. Do nothing. */
5073 /* Region has crossed threshold from sparsity to density. Mark in not "suitable" on the
5074 recirculation candidates list. */
5075 node
->recirc_suitable
= FALSE
;
5078 if (LOG(szone
,ptr
)) {
5079 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
5082 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), this_msize
);
5085 #undef DENSITY_THRESHOLD
5088 static INLINE
void *
5089 small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
5092 mag_index_t mag_index
= mag_get_thread_index(szone
);
5093 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5095 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5098 ptr
= (void *)small_mag_ptr
->mag_last_free
;
5100 if ((((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) == msize
) {
5102 small_mag_ptr
->mag_last_free
= NULL
;
5103 small_mag_ptr
->mag_last_free_rgn
= NULL
;
5104 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5105 CHECK(szone
, __PRETTY_FUNCTION__
);
5106 ptr
= (void *)((uintptr_t)ptr
& ~ (SMALL_QUANTUM
- 1));
5107 if (cleared_requested
) {
5108 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
5112 #endif /* SMALL_CACHE */
5115 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
5117 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5118 CHECK(szone
, __PRETTY_FUNCTION__
);
5119 if (cleared_requested
) {
5120 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
5125 if (small_get_region_from_depot(szone
, small_mag_ptr
, mag_index
, msize
)) {
5126 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
5128 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5129 CHECK(szone
, __PRETTY_FUNCTION__
);
5130 if (cleared_requested
) {
5131 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
5137 // The magazine is exhausted. A new region (heap) must be allocated to satisfy this call to malloc().
5138 // The allocation, an mmap() system call, will be performed outside the magazine spin locks by the first
5139 // thread that suffers the exhaustion. That thread sets "alloc_underway" and enters a critical section.
5140 // Threads arriving here later are excluded from the critical section, yield the CPU, and then retry the
5141 // allocation. After some time the magazine is resupplied, the original thread leaves with its allocation,
5142 // and retry-ing threads succeed in the code just above.
5143 if (!small_mag_ptr
->alloc_underway
) {
5146 // time to create a new region (do this outside the magazine lock)
5147 small_mag_ptr
->alloc_underway
= TRUE
;
5149 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5150 fresh_region
= allocate_pages_securely(szone
, SMALL_REGION_SIZE
, SMALL_BLOCKS_ALIGN
, VM_MEMORY_MALLOC_SMALL
);
5151 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5153 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
, fresh_region
, SMALL_REGION_SIZE
); // DTrace USDT Probe
5155 if (!fresh_region
) { // out of memory!
5156 small_mag_ptr
->alloc_underway
= FALSE
;
5158 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5162 ptr
= small_malloc_from_region_no_lock(szone
, small_mag_ptr
, mag_index
, msize
, fresh_region
);
5164 // we don't clear because this freshly allocated space is pristine
5165 small_mag_ptr
->alloc_underway
= FALSE
;
5167 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5168 CHECK(szone
, __PRETTY_FUNCTION__
);
5171 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5173 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5179 static NOINLINE
void
5180 free_small_botch(szone_t
*szone
, free_list_t
*ptr
)
5182 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5183 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5184 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5185 szone_error(szone
, 1, "double free", ptr
, NULL
);
5189 free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
)
5192 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5193 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5195 // ptr is known to be in small_region
5197 msize
= SMALL_MSIZE_FOR_BYTES(known_size
+ SMALL_QUANTUM
- 1);
5199 msize
= SMALL_PTR_SIZE(ptr
);
5200 if (SMALL_PTR_IS_FREE(ptr
)) {
5201 free_small_botch(szone
, ptr
);
5206 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5209 // Depot does not participate in SMALL_CACHE since it can't be directly malloc()'d
5210 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
5212 void *ptr2
= small_mag_ptr
->mag_last_free
; // Might be NULL
5213 region_t rgn2
= small_mag_ptr
->mag_last_free_rgn
;
5215 /* check that we don't already have this pointer in the cache */
5216 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (SMALL_QUANTUM
- 1))) {
5217 free_small_botch(szone
, ptr
);
5221 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
5222 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
5224 small_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
5225 small_mag_ptr
->mag_last_free_rgn
= small_region
;
5228 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5229 CHECK(szone
, __PRETTY_FUNCTION__
);
5233 msize
= (uintptr_t)ptr2
& (SMALL_QUANTUM
- 1);
5234 ptr
= (void *)(((uintptr_t)ptr2
) & ~(SMALL_QUANTUM
- 1));
5235 small_region
= rgn2
;
5237 #endif /* SMALL_CACHE */
5239 // Now in the time it took to acquire the lock, the region may have migrated
5240 // from one magazine to another. I.e. trailer->mag_index is volatile.
5241 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
5242 // is stale. If so, keep on tryin' ...
5243 region_trailer_t
*trailer
= REGION_TRAILER_FOR_SMALL_REGION(small_region
);
5244 mag_index_t refreshed_index
;
5246 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
5248 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5250 mag_index
= refreshed_index
;
5251 small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5252 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5255 if (small_free_no_lock(szone
, small_mag_ptr
, mag_index
, small_region
, ptr
, msize
))
5256 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5258 CHECK(szone
, __PRETTY_FUNCTION__
);
5262 print_small_free_list(szone_t
*szone
)
5265 _SIMPLE_STRING b
= _simple_salloc();
5266 mag_index_t mag_index
;
5269 _simple_sappend(b
, "small free sizes:\n");
5270 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
5272 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
5273 while (slot
< szone
->num_small_slots
) {
5274 ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
5276 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== szone
->num_small_slots
-1) ? ">=" : "",
5277 (slot
+ 1) * SMALL_QUANTUM
, free_list_count(szone
, ptr
));
5281 _simple_sappend(b
,"\n");
5283 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5289 print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
)
5291 unsigned counts
[1024];
5292 unsigned in_use
= 0;
5293 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(region
);
5294 uintptr_t current
= start
+ bytes_at_start
;
5295 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(region
) - bytes_at_end
;
5296 msize_t msize_and_free
;
5300 uintptr_t pgTot
= 0;
5302 if (region
== HASHRING_REGION_DEALLOCATED
) {
5303 if ((b
= _simple_salloc()) != NULL
) {
5304 _simple_sprintf(b
, "Small region [unknown address] was returned to the OS\n");
5305 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5311 memset(counts
, 0, sizeof(counts
));
5312 while (current
< limit
) {
5313 msize_and_free
= *SMALL_METADATA_FOR_PTR(current
);
5314 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
5316 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
5319 if (!(msize_and_free
& SMALL_IS_FREE
)) {
5325 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
5326 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
5329 pgTot
+= (pgHi
- pgLo
);
5332 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
5334 if ((b
= _simple_salloc()) != NULL
) {
5335 _simple_sprintf(b
, "Small region [%p-%p, %y] \t", (void *)start
, SMALL_REGION_END(region
), (int)SMALL_REGION_SIZE
);
5336 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_SMALL_REGION(region
));
5337 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_SMALL_REGION(region
));
5338 if (bytes_at_end
|| bytes_at_start
)
5339 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
+ bytes_at_start
);
5340 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_SMALL_REGION(region
)) {
5341 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
5343 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
5345 if (verbose
&& in_use
) {
5346 _simple_sappend(b
, "\n\tSizes in use: ");
5347 for (ci
= 0; ci
< 1024; ci
++)
5349 _simple_sprintf(b
, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
5351 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5357 small_free_list_check(szone_t
*szone
, grain_t slot
)
5359 mag_index_t mag_index
;
5361 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
5362 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5363 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5366 free_list_t
*ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
5367 msize_t msize_and_free
;
5368 free_list_t
*previous
= NULL
;
5371 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
5372 if (!(msize_and_free
& SMALL_IS_FREE
)) {
5373 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5374 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5377 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
5378 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5379 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5382 if (!small_region_for_ptr_no_lock(szone
, ptr
)) {
5383 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5384 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5387 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
5388 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5389 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5393 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
5397 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5402 /*******************************************************************************
5403 * Large allocator implementation
5404 ******************************************************************************/
5405 #pragma mark large allocator
5410 large_debug_print(szone_t
*szone
)
5413 large_entry_t
*range
;
5414 _SIMPLE_STRING b
= _simple_salloc();
5417 for (index
= 0, range
= szone
->large_entries
; index
< szone
->num_large_entries
; index
++, range
++)
5419 _simple_sprintf(b
, "%d: %p(%y); ", index
, range
->address
, range
->size
);
5421 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5428 * Scan the hash ring looking for an entry for the given pointer.
5430 static large_entry_t
*
5431 large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
5433 // result only valid with lock held
5434 unsigned num_large_entries
= szone
->num_large_entries
;
5435 unsigned hash_index
;
5437 large_entry_t
*range
;
5439 if (!num_large_entries
)
5442 hash_index
= ((uintptr_t)ptr
>> vm_page_shift
) % num_large_entries
;
5446 range
= szone
->large_entries
+ index
;
5447 if (range
->address
== (vm_address_t
)ptr
)
5449 if (0 == range
->address
)
5450 return NULL
; // end of chain
5452 if (index
== num_large_entries
)
5454 } while (index
!= hash_index
);
5460 large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
)
5462 unsigned num_large_entries
= szone
->num_large_entries
;
5463 unsigned hash_index
= (((uintptr_t)(range
.address
)) >> vm_page_shift
) % num_large_entries
;
5464 unsigned index
= hash_index
;
5465 large_entry_t
*entry
;
5467 // assert(szone->num_large_objects_in_use < szone->num_large_entries); /* must be called with room to spare */
5470 entry
= szone
->large_entries
+ index
;
5471 if (0 == entry
->address
) {
5473 return; // end of chain
5476 if (index
== num_large_entries
)
5478 } while (index
!= hash_index
);
5480 // assert(0); /* must not fallthrough! */
5483 // FIXME: can't we simply swap the (now empty) entry with the last entry on the collision chain for this hash slot?
5485 large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
)
5487 unsigned num_large_entries
= szone
->num_large_entries
;
5488 unsigned hash_index
= entry
- szone
->large_entries
;
5489 unsigned index
= hash_index
;
5490 large_entry_t range
;
5492 // assert(entry->address == 0) /* caller must have cleared *entry */
5496 if (index
== num_large_entries
)
5498 range
= szone
->large_entries
[index
];
5499 if (0 == range
.address
)
5501 szone
->large_entries
[index
].address
= (vm_address_t
)0;
5502 szone
->large_entries
[index
].size
= 0;
5503 szone
->large_entries
[index
].did_madvise_reusable
= FALSE
;
5504 large_entry_insert_no_lock(szone
, range
); // this will reinsert in the
5506 } while (index
!= hash_index
);
5508 // assert(0); /* since entry->address == 0, must not fallthrough! */
5511 // FIXME: num should probably be a size_t, since you can theoretically allocate
5512 // more than 2^32-1 large_threshold objects in 64 bit.
5513 static INLINE large_entry_t
*
5514 large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
)
5516 size_t size
= num
* sizeof(large_entry_t
);
5518 // Note that we allocate memory (via a system call) under a spin lock
5519 // That is certainly evil, however it's very rare in the lifetime of a process
5520 // The alternative would slow down the normal case
5521 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC_LARGE
);
5525 large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
)
5527 size_t size
= num
* sizeof(large_entry_t
);
5529 range_to_deallocate
->address
= (vm_address_t
)entries
;
5530 range_to_deallocate
->size
= round_page(size
);
5533 static large_entry_t
*
5534 large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
)
5536 // sets range_to_deallocate
5537 unsigned old_num_entries
= szone
->num_large_entries
;
5538 large_entry_t
*old_entries
= szone
->large_entries
;
5539 // always an odd number for good hashing
5540 unsigned new_num_entries
= (old_num_entries
) ? old_num_entries
* 2 + 1 :
5541 ((vm_page_size
/ sizeof(large_entry_t
)) - 1);
5542 large_entry_t
*new_entries
= large_entries_alloc_no_lock(szone
, new_num_entries
);
5543 unsigned index
= old_num_entries
;
5544 large_entry_t oldRange
;
5546 // if the allocation of new entries failed, bail
5547 if (new_entries
== NULL
)
5550 szone
->num_large_entries
= new_num_entries
;
5551 szone
->large_entries
= new_entries
;
5553 /* rehash entries into the new list */
5555 oldRange
= old_entries
[index
];
5556 if (oldRange
.address
) {
5557 large_entry_insert_no_lock(szone
, oldRange
);
5562 large_entries_free_no_lock(szone
, old_entries
, old_num_entries
, range_to_deallocate
);
5564 range_to_deallocate
->address
= (vm_address_t
)0;
5565 range_to_deallocate
->size
= 0;
5571 // frees the specific entry in the size table
5572 // returns a range to truly deallocate
5574 large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
)
5578 range
.address
= entry
->address
;
5579 range
.size
= entry
->size
;
5581 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
5582 protect((void *)range
.address
, range
.size
, PROT_READ
| PROT_WRITE
, szone
->debug_flags
);
5583 range
.address
-= vm_page_size
;
5584 range
.size
+= 2 * vm_page_size
;
5589 entry
->did_madvise_reusable
= FALSE
;
5590 large_entries_rehash_after_entry_no_lock(szone
, entry
);
5593 if (large_entry_for_pointer_no_lock(szone
, (void *)range
.address
)) {
5594 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
5595 range
.address
, szone
->num_large_entries
);
5596 large_debug_print(szone
);
5603 static NOINLINE kern_return_t
5604 large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
,
5605 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
5608 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
5610 large_entry_t
*entries
;
5613 large_entry_t entry
;
5615 err
= reader(task
, large_entries_address
, sizeof(large_entry_t
) * num_entries
, (void **)&entries
);
5619 index
= num_entries
;
5620 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
5621 range
.address
= large_entries_address
;
5622 range
.size
= round_page(num_entries
* sizeof(large_entry_t
));
5623 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &range
, 1);
5625 if (type_mask
& (MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
)) {
5627 entry
= entries
[index
];
5628 if (entry
.address
) {
5629 range
.address
= entry
.address
;
5630 range
.size
= entry
.size
;
5631 buffer
[count
++] = range
;
5632 if (count
>= MAX_RECORDER_BUFFER
) {
5633 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
5641 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
5648 large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
,
5649 boolean_t cleared_requested
)
5652 vm_range_t range_to_deallocate
;
5654 large_entry_t large_entry
;
5657 num_pages
= 1; // minimal allocation size for this szone
5658 size
= (size_t)num_pages
<< vm_page_shift
;
5659 range_to_deallocate
.size
= 0;
5660 range_to_deallocate
.address
= 0;
5663 if (size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
) { // Look for a large_entry_t on the death-row cache?
5666 int i
, best
= -1, idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5667 size_t best_size
= SIZE_T_MAX
;
5669 while (1) { // Scan large_entry_cache for best fit, starting with most recent entry
5670 size_t this_size
= szone
->large_entry_cache
[idx
].size
;
5671 addr
= (void *)szone
->large_entry_cache
[idx
].address
;
5673 if (0 == alignment
|| 0 == (((uintptr_t) addr
) & (((uintptr_t) 1 << alignment
) - 1))) {
5674 if (size
== this_size
) { // size match!
5676 best_size
= this_size
;
5680 if (size
<= this_size
&& this_size
< best_size
) { // improved fit?
5682 best_size
= this_size
;
5686 if (idx
== stop_idx
) // exhausted live ring?
5690 idx
--; // bump idx down
5692 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5695 if (best
> -1 && (best_size
- size
) < size
) { //limit fragmentation to 50%
5696 addr
= (void *)szone
->large_entry_cache
[best
].address
;
5697 boolean_t was_madvised_reusable
= szone
->large_entry_cache
[best
].did_madvise_reusable
;
5699 // Compact live ring to fill entry now vacated at large_entry_cache[best]
5700 // while preserving time-order
5701 if (szone
->large_entry_cache_oldest
< szone
->large_entry_cache_newest
) {
5703 // Ring hasn't wrapped. Fill in from right.
5704 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5705 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5707 szone
->large_entry_cache_newest
--; // Pull in right endpoint.
5709 } else if (szone
->large_entry_cache_newest
< szone
->large_entry_cache_oldest
) {
5711 // Ring has wrapped. Arrange to fill in from the contiguous side.
5712 if (best
<= szone
->large_entry_cache_newest
) {
5714 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5715 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5717 if (0 < szone
->large_entry_cache_newest
)
5718 szone
->large_entry_cache_newest
--;
5720 szone
->large_entry_cache_newest
= LARGE_ENTRY_CACHE_SIZE
- 1;
5723 for ( i
= best
; i
> szone
->large_entry_cache_oldest
; --i
)
5724 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
- 1];
5726 if (szone
->large_entry_cache_oldest
< LARGE_ENTRY_CACHE_SIZE
- 1)
5727 szone
->large_entry_cache_oldest
++;
5729 szone
->large_entry_cache_oldest
= 0;
5733 // By trichotomy, large_entry_cache_newest == large_entry_cache_oldest.
5734 // That implies best == large_entry_cache_newest == large_entry_cache_oldest
5735 // and the ring is now empty.
5736 szone
->large_entry_cache
[best
].address
= 0;
5737 szone
->large_entry_cache
[best
].size
= 0;
5738 szone
->large_entry_cache
[best
].did_madvise_reusable
= FALSE
;
5741 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5742 // density of hash table too high; grow table
5743 // we do that under lock to avoid a race
5744 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5745 if (entries
== NULL
) {
5746 SZONE_UNLOCK(szone
);
5751 large_entry
.address
= (vm_address_t
)addr
;
5752 large_entry
.size
= best_size
;
5753 large_entry
.did_madvise_reusable
= FALSE
;
5754 large_entry_insert_no_lock(szone
, large_entry
);
5756 szone
->num_large_objects_in_use
++;
5757 szone
->num_bytes_in_large_objects
+= best_size
;
5758 if (!was_madvised_reusable
)
5759 szone
->large_entry_cache_reserve_bytes
-= best_size
;
5761 szone
->large_entry_cache_bytes
-= best_size
;
5763 if (szone
->flotsam_enabled
&& szone
->large_entry_cache_bytes
< SZONE_FLOTSAM_THRESHOLD_LOW
) {
5764 szone
->flotsam_enabled
= FALSE
;
5767 SZONE_UNLOCK(szone
);
5769 if (range_to_deallocate
.size
) {
5770 // we deallocate outside the lock
5771 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5774 // Perform the madvise() outside the lock.
5775 // Typically the madvise() is successful and we'll quickly return from this routine.
5776 // In the unusual case of failure, reacquire the lock to unwind.
5777 #if TARGET_OS_EMBEDDED
5778 // Ok to do this madvise on embedded because we won't call MADV_FREE_REUSABLE on a large
5779 // cache block twice without MADV_FREE_REUSE in between.
5781 if (was_madvised_reusable
&& -1 == madvise(addr
, size
, MADV_FREE_REUSE
)) {
5782 /* -1 return: VM map entry change makes this unfit for reuse. */
5784 szone_error(szone
, 0, "large_malloc madvise(..., MADV_FREE_REUSE) failed",
5785 addr
, "length=%d\n", size
);
5789 szone
->num_large_objects_in_use
--;
5790 szone
->num_bytes_in_large_objects
-= large_entry
.size
;
5792 // Re-acquire "entry" after interval just above where we let go the lock.
5793 large_entry_t
*entry
= large_entry_for_pointer_no_lock(szone
, addr
);
5794 if (NULL
== entry
) {
5795 szone_error(szone
, 1, "entry for pointer being discarded from death-row vanished", addr
, NULL
);
5796 SZONE_UNLOCK(szone
);
5799 range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5800 SZONE_UNLOCK(szone
);
5802 if (range_to_deallocate
.size
) {
5803 // we deallocate outside the lock
5804 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5807 /* Fall through to allocate_pages() afresh. */
5809 if (cleared_requested
) {
5810 memset(addr
, 0, size
);
5816 SZONE_UNLOCK(szone
);
5820 range_to_deallocate
.size
= 0;
5821 range_to_deallocate
.address
= 0;
5822 #endif /* LARGE_CACHE */
5824 addr
= allocate_pages(szone
, size
, alignment
, szone
->debug_flags
, VM_MEMORY_MALLOC_LARGE
);
5830 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5831 // density of hash table too high; grow table
5832 // we do that under lock to avoid a race
5833 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5834 if (entries
== NULL
) {
5835 SZONE_UNLOCK(szone
);
5840 large_entry
.address
= (vm_address_t
)addr
;
5841 large_entry
.size
= size
;
5842 large_entry
.did_madvise_reusable
= FALSE
;
5843 large_entry_insert_no_lock(szone
, large_entry
);
5845 szone
->num_large_objects_in_use
++;
5846 szone
->num_bytes_in_large_objects
+= size
;
5847 SZONE_UNLOCK(szone
);
5849 if (range_to_deallocate
.size
) {
5850 // we deallocate outside the lock
5851 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5856 static NOINLINE
void
5857 free_large(szone_t
*szone
, void *ptr
)
5859 // We have established ptr is page-aligned and neither tiny nor small
5860 large_entry_t
*entry
;
5861 vm_range_t vm_range_to_deallocate
;
5864 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5867 #ifndef MADV_CAN_REUSE
5868 #define MADV_CAN_REUSE 9 /* per Francois, for testing until xnu is resubmitted to B&I */
5870 if (entry
->size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
&&
5871 -1 != madvise((void *)(entry
->address
), entry
->size
, MADV_CAN_REUSE
)) { // Put the large_entry_t on the death-row cache?
5872 int idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5873 large_entry_t this_entry
= *entry
; // Make a local copy, "entry" is volatile when lock is let go.
5874 boolean_t reusable
= TRUE
;
5875 boolean_t should_madvise
= szone
->large_entry_cache_reserve_bytes
+ this_entry
.size
> szone
->large_entry_cache_reserve_limit
;
5878 // [Note that repeated entries in death-row risk vending the same entry subsequently
5879 // to two different malloc() calls. By checking here the (illegal) double free
5880 // is accommodated, matching the behavior of the previous implementation.]
5881 while (1) { // Scan large_entry_cache starting with most recent entry
5882 if (szone
->large_entry_cache
[idx
].address
== entry
->address
) {
5883 szone_error(szone
, 1, "pointer being freed already on death-row", ptr
, NULL
);
5884 SZONE_UNLOCK(szone
);
5888 if (idx
== stop_idx
) // exhausted live ring?
5892 idx
--; // bump idx down
5894 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5897 SZONE_UNLOCK(szone
);
5899 if (szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) { // Are we a purgable zone?
5900 int state
= VM_PURGABLE_NONVOLATILE
; // restore to default condition
5902 if (KERN_SUCCESS
!= vm_purgable_control(mach_task_self(), this_entry
.address
, VM_PURGABLE_SET_STATE
, &state
)) {
5903 malloc_printf("*** can't vm_purgable_control(..., VM_PURGABLE_SET_STATE) for large freed block at %p\n",
5904 this_entry
.address
);
5909 if (szone
->large_legacy_reset_mprotect
) { // Linked for Leopard?
5910 // Accomodate Leopard apps that (illegally) mprotect() their own guard pages on large malloc'd allocations
5911 int err
= mprotect((void *)(this_entry
.address
), this_entry
.size
, PROT_READ
| PROT_WRITE
);
5913 malloc_printf("*** can't reset protection for large freed block at %p\n", this_entry
.address
);
5918 // madvise(..., MADV_REUSABLE) death-row arrivals if hoarding would exceed large_entry_cache_reserve_limit
5919 if (should_madvise
) {
5920 // Issue madvise to avoid paging out the dirtied free()'d pages in "entry"
5921 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)0, (void *)(this_entry
.address
), this_entry
.size
); // DTrace USDT Probe
5923 #if TARGET_OS_EMBEDDED
5924 // Ok to do this madvise on embedded because we won't call MADV_FREE_REUSABLE on a large
5925 // cache block twice without MADV_FREE_REUSE in between.
5927 if (-1 == madvise((void *)(this_entry
.address
), this_entry
.size
, MADV_FREE_REUSABLE
)) {
5928 /* -1 return: VM map entry change makes this unfit for reuse. */
5930 szone_error(szone
, 0, "free_large madvise(..., MADV_FREE_REUSABLE) failed",
5931 (void *)this_entry
.address
, "length=%d\n", this_entry
.size
);
5939 // Re-acquire "entry" after interval just above where we let go the lock.
5940 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5941 if (NULL
== entry
) {
5942 szone_error(szone
, 1, "entry for pointer being freed from death-row vanished", ptr
, NULL
);
5943 SZONE_UNLOCK(szone
);
5947 // Add "entry" to death-row ring
5949 int idx
= szone
->large_entry_cache_newest
; // Most recently occupied
5953 if (szone
->large_entry_cache_newest
== szone
->large_entry_cache_oldest
&&
5954 0 == szone
->large_entry_cache
[idx
].address
) {
5955 // Ring is empty, idx is good as it stands
5959 // Extend the queue to the "right" by bumping up large_entry_cache_newest
5960 if (idx
== LARGE_ENTRY_CACHE_SIZE
- 1)
5961 idx
= 0; // Wrap index
5963 idx
++; // Bump index
5965 if (idx
== szone
->large_entry_cache_oldest
) { // Fully occupied
5966 // Drop this entry from the cache and deallocate the VM
5967 addr
= szone
->large_entry_cache
[idx
].address
;
5968 adjsize
= szone
->large_entry_cache
[idx
].size
;
5969 szone
->large_entry_cache_bytes
-= adjsize
;
5970 if (!szone
->large_entry_cache
[idx
].did_madvise_reusable
)
5971 szone
->large_entry_cache_reserve_bytes
-= adjsize
;
5973 // Using an unoccupied cache slot
5979 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
))
5980 memset((void *)(entry
->address
), 0x55, entry
->size
);
5982 entry
->did_madvise_reusable
= should_madvise
; // Was madvise()'d above?
5983 if (!should_madvise
) // Entered on death-row without madvise() => up the hoard total
5984 szone
->large_entry_cache_reserve_bytes
+= entry
->size
;
5986 szone
->large_entry_cache_bytes
+= entry
->size
;
5988 if (!szone
->flotsam_enabled
&& szone
->large_entry_cache_bytes
> SZONE_FLOTSAM_THRESHOLD_HIGH
) {
5989 szone
->flotsam_enabled
= TRUE
;
5992 szone
->large_entry_cache
[idx
] = *entry
;
5993 szone
->large_entry_cache_newest
= idx
;
5995 szone
->num_large_objects_in_use
--;
5996 szone
->num_bytes_in_large_objects
-= entry
->size
;
5998 (void)large_entry_free_no_lock(szone
, entry
);
6001 SZONE_UNLOCK(szone
);
6005 // Fall through to drop large_entry_cache_oldest from the cache,
6006 // and then deallocate its pages.
6008 // Trim the queue on the "left" by bumping up large_entry_cache_oldest
6009 if (szone
->large_entry_cache_oldest
== LARGE_ENTRY_CACHE_SIZE
- 1)
6010 szone
->large_entry_cache_oldest
= 0;
6012 szone
->large_entry_cache_oldest
++;
6014 // we deallocate_pages, including guard pages, outside the lock
6015 SZONE_UNLOCK(szone
);
6016 deallocate_pages(szone
, (void *)addr
, (size_t)adjsize
, 0);
6019 /* fall through to discard an allocation that is not reusable */
6022 #endif /* LARGE_CACHE */
6024 szone
->num_large_objects_in_use
--;
6025 szone
->num_bytes_in_large_objects
-= entry
->size
;
6027 vm_range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
6030 large_debug_print(szone
);
6032 szone_error(szone
, 1, "pointer being freed was not allocated", ptr
, NULL
);
6033 SZONE_UNLOCK(szone
);
6036 SZONE_UNLOCK(szone
); // we release the lock asap
6037 CHECK(szone
, __PRETTY_FUNCTION__
);
6039 // we deallocate_pages, including guard pages, outside the lock
6040 if (vm_range_to_deallocate
.address
) {
6042 // FIXME: large_entry_for_pointer_no_lock() needs the lock held ...
6043 if (large_entry_for_pointer_no_lock(szone
, (void *)vm_range_to_deallocate
.address
)) {
6044 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n",
6045 vm_range_to_deallocate
.address
, szone
->num_large_entries
);
6046 large_debug_print(szone
);
6050 deallocate_pages(szone
, (void *)vm_range_to_deallocate
.address
, (size_t)vm_range_to_deallocate
.size
, 0);
6054 static INLINE
void *
6055 large_try_shrink_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_good_size
)
6057 size_t shrinkage
= old_size
- new_good_size
;
6061 /* contract existing large entry */
6062 large_entry_t
*large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6064 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
6065 SZONE_UNLOCK(szone
);
6069 large_entry
->address
= (vm_address_t
)ptr
;
6070 large_entry
->size
= new_good_size
;
6071 szone
->num_bytes_in_large_objects
-= shrinkage
;
6072 SZONE_UNLOCK(szone
); // we release the lock asap
6074 deallocate_pages(szone
, (void *)((uintptr_t)ptr
+ new_good_size
), shrinkage
, 0);
6080 large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
6082 vm_address_t addr
= (vm_address_t
)ptr
+ old_size
;
6083 large_entry_t
*large_entry
;
6087 large_entry
= large_entry_for_pointer_no_lock(szone
, (void *)addr
);
6088 SZONE_UNLOCK(szone
);
6090 if (large_entry
) { // check if "addr = ptr + old_size" is already spoken for
6091 return 0; // large pointer already exists in table - extension is not going to work
6094 new_size
= round_page(new_size
);
6096 * Ask for allocation at a specific address, and mark as realloc
6097 * to request coalescing with previous realloc'ed extensions.
6099 err
= vm_allocate(mach_task_self(), &addr
, new_size
- old_size
, VM_MAKE_TAG(VM_MEMORY_REALLOC
));
6100 if (err
!= KERN_SUCCESS
) {
6105 /* extend existing large entry */
6106 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6108 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
6109 SZONE_UNLOCK(szone
);
6110 return 0; // Bail, leaking "addr"
6113 large_entry
->address
= (vm_address_t
)ptr
;
6114 large_entry
->size
= new_size
;
6115 szone
->num_bytes_in_large_objects
+= new_size
- old_size
;
6116 SZONE_UNLOCK(szone
); // we release the lock asap
6121 /********************* Zone call backs ************************/
6123 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
6125 static NOINLINE
void
6126 szone_free(szone_t
*szone
, void *ptr
)
6128 region_t tiny_region
;
6129 region_t small_region
;
6132 if (LOG(szone
, ptr
))
6133 malloc_printf("in szone_free with %p\n", ptr
);
6138 * Try to free to a tiny region.
6140 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
6141 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
6144 if ((tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
6145 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
6146 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
6149 free_tiny(szone
, ptr
, tiny_region
, 0);
6154 * Try to free to a small region.
6156 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
6157 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
6160 if ((small_region
= small_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
6161 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
6162 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
6165 free_small(szone
, ptr
, small_region
, 0);
6169 /* check that it's a legal large allocation */
6170 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
6171 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
6174 free_large(szone
, ptr
);
6177 static NOINLINE
void
6178 szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
6181 if (LOG(szone
, ptr
))
6182 malloc_printf("in szone_free_definite_size with %p\n", ptr
);
6185 szone_error(szone
, 1, "pointer of size zero being freed", ptr
, NULL
);
6194 * Try to free to a tiny region.
6196 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
6197 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
6200 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6201 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
6202 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
6205 free_tiny(szone
, ptr
, TINY_REGION_FOR_PTR(ptr
), size
);
6210 * Try to free to a small region.
6212 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
6213 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
6216 if (size
<= szone
->large_threshold
) {
6217 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
6218 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
6221 free_small(szone
, ptr
, SMALL_REGION_FOR_PTR(ptr
), size
);
6225 /* check that it's a legal large allocation */
6226 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
6227 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
6230 free_large(szone
, ptr
);
6233 static NOINLINE
void *
6234 szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
)
6239 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6241 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6244 ptr
= tiny_malloc_should_clear(szone
, msize
, cleared_requested
);
6245 } else if (size
<= szone
->large_threshold
) {
6247 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6250 ptr
= small_malloc_should_clear(szone
, msize
, cleared_requested
);
6253 size_t num_pages
= round_page(size
) >> vm_page_shift
;
6254 if (num_pages
== 0) /* Overflowed */
6257 ptr
= large_malloc(szone
, num_pages
, 0, cleared_requested
);
6260 if (LOG(szone
, ptr
))
6261 malloc_printf("szone_malloc returned %p\n", ptr
);
6264 * If requested, scribble on allocated memory.
6266 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && ptr
&& !cleared_requested
&& size
)
6267 memset(ptr
, 0xaa, size
);
6272 static NOINLINE
void *
6273 szone_malloc(szone_t
*szone
, size_t size
) {
6274 return szone_malloc_should_clear(szone
, size
, 0);
6277 static NOINLINE
void *
6278 szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
6280 size_t total_bytes
= num_items
* size
;
6282 // Check for overflow of integer multiplication
6283 if (num_items
> 1) {
6284 #if __LP64__ /* size_t is uint64_t */
6285 if ((num_items
| size
) & 0xffffffff00000000ul
) {
6286 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
6287 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
6288 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
6291 #else /* size_t is uint32_t */
6292 if ((num_items
| size
) & 0xffff0000ul
) {
6293 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
6294 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
6295 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
6301 return szone_malloc_should_clear(szone
, total_bytes
, 1);
6304 static NOINLINE
void *
6305 szone_valloc(szone_t
*szone
, size_t size
)
6309 if (size
<= szone
->large_threshold
) {
6310 ptr
= szone_memalign(szone
, vm_page_size
, size
);
6314 num_pages
= round_page(size
) >> vm_page_shift
;
6315 ptr
= large_malloc(szone
, num_pages
, 0, 0);
6319 if (LOG(szone
, ptr
))
6320 malloc_printf("szone_valloc returned %p\n", ptr
);
6325 /* Isolate PIC-base load (for __is_threaded) here. */
6326 static NOINLINE
size_t
6327 szone_size_try_large(szone_t
*szone
, const void *ptr
)
6330 large_entry_t
*entry
;
6333 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6337 SZONE_UNLOCK(szone
);
6339 if (LOG(szone
, ptr
)) {
6340 malloc_printf("szone_size for %p returned %d\n", ptr
, (unsigned)size
);
6346 static NOINLINE
size_t
6347 szone_size(szone_t
*szone
, const void *ptr
)
6350 msize_t msize
, msize_and_free
;
6355 if (LOG(szone
, ptr
)) {
6356 malloc_printf("in szone_size for %p (szone=%p)\n", ptr
, szone
);
6361 * Look for it in a tiny region.
6363 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1))
6365 if (tiny_region_for_ptr_no_lock(szone
, ptr
)) {
6366 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
6368 msize
= get_tiny_meta_header(ptr
, &is_free
);
6373 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
6374 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
6375 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6377 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
6380 for (mag_index
= 0; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6381 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6383 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
6389 return TINY_BYTES_FOR_MSIZE(msize
);
6393 * Look for it in a small region.
6395 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1))
6397 if (small_region_for_ptr_no_lock(szone
, ptr
)) {
6398 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
)
6400 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
6401 if (msize_and_free
& SMALL_IS_FREE
)
6405 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
6406 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
6407 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
6409 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
6412 for (mag_index
= 0; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6413 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
6415 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
6421 return SMALL_BYTES_FOR_MSIZE(msize_and_free
);
6425 * If not page-aligned, it cannot have come from a large allocation.
6427 if ((uintptr_t)ptr
& (vm_page_size
- 1))
6431 * Look for it in a large entry.
6433 return szone_size_try_large(szone
, ptr
);
6436 static NOINLINE
void *
6437 szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
6439 size_t old_size
, new_good_size
, valid_size
;
6443 if (LOG(szone
, ptr
)) {
6444 malloc_printf("in szone_realloc for %p, %d\n", ptr
, (unsigned)new_size
);
6448 // If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size.
6449 return szone_malloc(szone
, new_size
);
6450 } else if (0 == new_size
) {
6451 // If size is 0 and ptr is not a null pointer, the object pointed to is freed.
6452 szone_free(szone
, ptr
);
6453 // If size is 0, either a null pointer or a unique pointer that can be successfully passed
6454 // to free() shall be returned.
6455 return szone_malloc(szone
, 1);
6458 old_size
= szone_size(szone
, ptr
);
6460 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
6464 new_good_size
= szone_good_size(szone
, new_size
);
6465 if (new_good_size
== old_size
) { // Existing allocation is best fit evar?
6470 * If the new size suits the tiny allocator and the pointer being resized
6471 * belongs to a tiny region, try to reallocate in-place.
6473 if (new_good_size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6474 if (old_size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6475 if (new_good_size
<= (old_size
>> 1)) {
6477 * Serious shrinkage (more than half). free() the excess.
6479 return tiny_try_shrink_in_place(szone
, ptr
, old_size
, new_good_size
);
6480 } else if (new_good_size
<= old_size
) {
6482 * new_good_size smaller than old_size but not by much (less than half).
6483 * Avoid thrashing at the expense of some wasted storage.
6486 } else if (tiny_try_realloc_in_place(szone
, ptr
, old_size
, new_good_size
)) { // try to grow the allocation
6492 * Else if the new size suits the small allocator and the pointer being resized
6493 * belongs to a small region, and we're not protecting the small allocations
6494 * try to reallocate in-place.
6496 } else if (new_good_size
<= szone
->large_threshold
) {
6497 if ((NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
< old_size
&& old_size
<= szone
->large_threshold
) {
6498 if (new_good_size
<= (old_size
>> 1)) {
6499 return small_try_shrink_in_place(szone
, ptr
, old_size
, new_good_size
);
6500 } else if (new_good_size
<= old_size
) {
6502 } else if (small_try_realloc_in_place(szone
, ptr
, old_size
, new_good_size
)) {
6507 * Else if the allocation's a large allocation, try to reallocate in-place there.
6509 } else if (!(szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) && // purgeable needs fresh allocation
6510 (old_size
> szone
->large_threshold
) &&
6511 (new_good_size
> szone
->large_threshold
)) {
6512 if (new_good_size
<= (old_size
>> 1)) {
6513 return large_try_shrink_in_place(szone
, ptr
, old_size
, new_good_size
);
6514 } else if (new_good_size
<= old_size
) {
6516 } else if (large_try_realloc_in_place(szone
, ptr
, old_size
, new_good_size
)) {
6522 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
6524 if (new_good_size
<= (old_size
>> 1)) {
6525 /* Serious shrinkage (more than half). FALL THROUGH to alloc/copy/free. */
6526 } else if (new_good_size
<= old_size
) {
6530 new_ptr
= szone_malloc(szone
, new_size
);
6531 if (new_ptr
== NULL
)
6535 * If the allocation's large enough, try to copy using VM. If that fails, or
6536 * if it's too small, just copy by hand.
6538 valid_size
= MIN(old_size
, new_size
);
6539 if ((valid_size
< szone
->vm_copy_threshold
) ||
6540 vm_copy(mach_task_self(), (vm_address_t
)ptr
, valid_size
, (vm_address_t
)new_ptr
))
6541 memcpy(new_ptr
, ptr
, valid_size
);
6542 szone_free(szone
, ptr
);
6545 if (LOG(szone
, ptr
)) {
6546 malloc_printf("szone_realloc returned %p for %d\n", new_ptr
, (unsigned)new_size
);
6552 static NOINLINE
void *
6553 szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
6555 if ((size
+ alignment
) < size
) // size_t arithmetic wrapped!
6558 // alignment is gauranteed a power of 2 at least as large as sizeof(void *), hence non-zero.
6559 // Since size + alignment didn't wrap, 0 <= size + alignment - 1 < size + alignment
6560 size_t span
= size
+ alignment
- 1;
6562 if (alignment
<= TINY_QUANTUM
) {
6563 return szone_malloc(szone
, size
); // Trivially satisfied by tiny, small, or large
6565 } else if (span
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6566 msize_t mspan
= TINY_MSIZE_FOR_BYTES(span
+ TINY_QUANTUM
- 1);
6567 void *p
= szone_malloc(szone
, span
); // avoids inlining tiny_malloc_should_clear(szone, mspan, 0);
6572 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
6573 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
6575 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6576 msize_t mpad
= TINY_MSIZE_FOR_BYTES(pad
+ TINY_QUANTUM
- 1);
6577 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
6580 void *q
= (void *)(((uintptr_t) p
) + pad
);
6582 // Mark q as a block header and in-use, thus creating two blocks.
6583 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6584 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
6585 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
6586 set_tiny_meta_header_in_use(q
, msize
);
6587 tiny_mag_ptr
->mag_num_objects
++;
6589 // set_tiny_meta_header_in_use() "reaffirms" the block_header on the *following* block, so
6590 // now set its in_use bit as well. But only if its within the original allocation made above.
6592 BITARRAY_SET(TINY_INUSE_FOR_HEADER(TINY_BLOCK_HEADER_FOR_PTR(q
)), TINY_INDEX_FOR_PTR(q
) + msize
);
6593 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6595 // Give up mpad blocks beginning at p to the tiny free list
6596 // region_t r = TINY_REGION_FOR_PTR(p);
6597 szone_free(szone
, p
); // avoids inlining free_tiny(szone, p, &r);
6599 p
= q
; // advance p to the desired alignment
6603 void *q
= (void *)(((uintptr_t) p
) + TINY_BYTES_FOR_MSIZE(msize
));
6604 // Mark q as block header and in-use, thus creating two blocks.
6605 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6606 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
6607 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
6608 set_tiny_meta_header_in_use(q
, mwaste
);
6609 tiny_mag_ptr
->mag_num_objects
++;
6610 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6612 // Give up mwaste blocks beginning at q to the tiny free list
6613 // region_t r = TINY_REGION_FOR_PTR(q);
6614 szone_free(szone
, q
); // avoids inlining free_tiny(szone, q, &r);
6617 return p
; // p has the desired size and alignment, and can later be free()'d
6619 } else if ((NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
< size
&& alignment
<= SMALL_QUANTUM
) {
6620 return szone_malloc(szone
, size
); // Trivially satisfied by small or large
6622 } else if (span
<= szone
->large_threshold
) {
6624 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6625 size
= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
+ TINY_QUANTUM
; // ensure block allocated by small does not have a tiny-possible size
6626 span
= size
+ alignment
- 1;
6629 msize_t mspan
= SMALL_MSIZE_FOR_BYTES(span
+ SMALL_QUANTUM
- 1);
6630 void *p
= szone_malloc(szone
, span
); // avoid inlining small_malloc_should_clear(szone, mspan, 0);
6635 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
6636 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
6638 msize_t msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6639 msize_t mpad
= SMALL_MSIZE_FOR_BYTES(pad
+ SMALL_QUANTUM
- 1);
6640 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
6643 void *q
= (void *)(((uintptr_t) p
) + pad
);
6645 // Mark q as block header and in-use, thus creating two blocks.
6646 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6647 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
6648 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
6649 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), mpad
);
6650 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), msize
+ mwaste
);
6651 small_mag_ptr
->mag_num_objects
++;
6652 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6654 // Give up mpad blocks beginning at p to the small free list
6655 // region_t r = SMALL_REGION_FOR_PTR(p);
6656 szone_free(szone
, p
); // avoid inlining free_small(szone, p, &r);
6658 p
= q
; // advance p to the desired alignment
6661 void *q
= (void *)(((uintptr_t) p
) + SMALL_BYTES_FOR_MSIZE(msize
));
6662 // Mark q as block header and in-use, thus creating two blocks.
6663 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6664 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
6665 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
6666 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), msize
);
6667 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mwaste
);
6668 small_mag_ptr
->mag_num_objects
++;
6669 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6671 // Give up mwaste blocks beginning at q to the small free list
6672 // region_t r = SMALL_REGION_FOR_PTR(q);
6673 szone_free(szone
, q
); // avoid inlining free_small(szone, q, &r);
6676 return p
; // p has the desired size and alignment, and can later be free()'d
6678 } else if (szone
->large_threshold
< size
&& alignment
<= vm_page_size
) {
6679 return szone_malloc(szone
, size
); // Trivially satisfied by large
6682 // ensure block allocated by large does not have a small-possible size
6683 size_t num_pages
= round_page(MAX(szone
->large_threshold
+ 1, size
)) >> vm_page_shift
;
6686 if (num_pages
== 0) /* Overflowed */
6689 p
= large_malloc(szone
, num_pages
, MAX(vm_page_shift
, __builtin_ctz(alignment
)), 0);
6696 // given a size, returns the number of pointers allocated capable of holding
6697 // that size, up to the limit specified by the 'count' argument. These pointers
6698 // are stored in the 'results' array, which must be allocated by the caller.
6699 // may return zero, since this function is only a best attempt at allocating
6700 // the pointers. clients should be prepared to call malloc for any additional
6701 // blocks they need.
6702 static NOINLINE
unsigned
6703 szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
6705 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6707 mag_index_t mag_index
= mag_get_thread_index(szone
);
6708 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6710 // only bother implementing this for tiny
6711 if (size
> (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
)
6713 // make sure to return objects at least one quantum in size
6717 CHECK(szone
, __PRETTY_FUNCTION__
);
6719 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
6720 // the caller has done so.
6721 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
6723 // with the zone locked, allocate objects from the free list until all
6724 // sufficiently large objects have been exhausted, or we have met our quota
6725 // of objects to allocate.
6726 while (found
< count
) {
6727 void *ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
6734 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6738 /* Try caching the tiny_region and checking if the next ptr hits there. */
6739 static NOINLINE
void
6740 szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
6744 region_t tiny_region
= NULL
;
6747 magazine_t
*tiny_mag_ptr
= NULL
;
6748 mag_index_t mag_index
= -1;
6750 // frees all the pointers in to_be_freed
6751 // note that to_be_freed may be overwritten during the process
6755 CHECK(szone
, __PRETTY_FUNCTION__
);
6756 while (cc
< count
) {
6757 ptr
= to_be_freed
[cc
];
6759 if (NULL
== tiny_region
|| tiny_region
!= TINY_REGION_FOR_PTR(ptr
)) { // region same as last iteration?
6760 if (tiny_mag_ptr
) { // non-NULL iff magazine lock taken
6761 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6762 tiny_mag_ptr
= NULL
;
6765 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
6768 tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6769 REGION_TRAILER_FOR_TINY_REGION(tiny_region
),
6770 MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
));
6771 mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
6775 // this is a tiny pointer
6776 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
6777 break; // pointer to metadata; let the standard free deal with it
6778 msize
= get_tiny_meta_header(ptr
, &is_free
);
6780 break; // a double free; let the standard free deal with it
6782 if (!tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
)) {
6783 // Arrange to re-acquire magazine lock
6784 tiny_mag_ptr
= NULL
;
6787 to_be_freed
[cc
] = NULL
;
6789 // No region in this zone claims ptr; let the standard free deal with it
6797 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6798 tiny_mag_ptr
= NULL
;
6801 CHECK(szone
, __PRETTY_FUNCTION__
);
6803 ptr
= to_be_freed
[count
];
6805 szone_free(szone
, ptr
);
6809 // FIXME: Suppose one of the locks is held?
6811 szone_destroy(szone_t
*szone
)
6814 large_entry_t
*large
;
6815 vm_range_t range_to_deallocate
;
6820 /* disable any memory pressure responder */
6821 szone
->flotsam_enabled
= FALSE
;
6823 // stack allocated copy of the death-row cache
6824 int idx
= szone
->large_entry_cache_oldest
, idx_max
= szone
->large_entry_cache_newest
;
6825 large_entry_t local_entry_cache
[LARGE_ENTRY_CACHE_SIZE
];
6827 memcpy((void *)local_entry_cache
, (void *)szone
->large_entry_cache
, sizeof(local_entry_cache
));
6829 szone
->large_entry_cache_oldest
= szone
->large_entry_cache_newest
= 0;
6830 szone
->large_entry_cache
[0].address
= 0x0;
6831 szone
->large_entry_cache
[0].size
= 0;
6832 szone
->large_entry_cache_bytes
= 0;
6833 szone
->large_entry_cache_reserve_bytes
= 0;
6835 SZONE_UNLOCK(szone
);
6837 // deallocate the death-row cache outside the zone lock
6838 while (idx
!= idx_max
) {
6839 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
6840 if (++idx
== LARGE_ENTRY_CACHE_SIZE
) idx
= 0;
6842 if (0 != local_entry_cache
[idx
].address
&& 0 != local_entry_cache
[idx
].size
) {
6843 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
6847 /* destroy large entries */
6848 index
= szone
->num_large_entries
;
6850 large
= szone
->large_entries
+ index
;
6851 if (large
->address
) {
6852 // we deallocate_pages, including guard pages
6853 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6856 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6857 if (range_to_deallocate
.size
)
6858 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6860 /* destroy tiny regions */
6861 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
)
6862 if ((HASHRING_OPEN_ENTRY
!= szone
->tiny_region_generation
->hashed_regions
[index
]) &&
6863 (HASHRING_REGION_DEALLOCATED
!= szone
->tiny_region_generation
->hashed_regions
[index
]))
6864 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
[index
], TINY_REGION_SIZE
, 0);
6866 /* destroy small regions */
6867 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
)
6868 if ((HASHRING_OPEN_ENTRY
!= szone
->small_region_generation
->hashed_regions
[index
]) &&
6869 (HASHRING_REGION_DEALLOCATED
!= szone
->small_region_generation
->hashed_regions
[index
]))
6870 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
[index
], SMALL_REGION_SIZE
, 0);
6872 /* destroy region hash rings, if any */
6873 if (szone
->tiny_region_generation
->hashed_regions
!= szone
->initial_tiny_regions
) {
6874 size_t size
= round_page(szone
->tiny_region_generation
->num_regions_allocated
* sizeof(region_t
));
6875 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
, size
, 0);
6877 if (szone
->small_region_generation
->hashed_regions
!= szone
->initial_small_regions
) {
6878 size_t size
= round_page(szone
->small_region_generation
->num_regions_allocated
* sizeof(region_t
));
6879 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
, size
, 0);
6882 /* Now destroy the separate szone region */
6883 if (szone
->cpu_id_key
!= (pthread_key_t
) -1)
6884 (void)pthread_key_delete(szone
->cpu_id_key
);
6885 deallocate_pages(szone
, (void *)&(szone
->tiny_magazines
[-1]), TINY_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6886 deallocate_pages(szone
, (void *)&(szone
->small_magazines
[-1]), SMALL_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6887 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, 0);
6890 static NOINLINE
size_t
6891 szone_good_size(szone_t
*szone
, size_t size
)
6895 // Find a good size for this tiny allocation.
6896 if (size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6897 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6900 return TINY_BYTES_FOR_MSIZE(msize
);
6903 // Find a good size for this small allocation.
6904 if (size
<= szone
->large_threshold
) {
6905 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6908 return SMALL_BYTES_FOR_MSIZE(msize
);
6911 // Check for integer overflow on the size, since unlike the two cases above,
6912 // there is no upper bound on allocation size at this point.
6913 if (size
> round_page(size
))
6914 return (size_t)(-1LL);
6917 // It is not acceptable to see a size of zero here, since that means we
6918 // failed to catch a request for zero bytes in the tiny check, or the size
6919 // overflowed to zero during some arithmetic.
6921 malloc_printf("szone_good_size() invariant broken %y\n", size
);
6923 return round_page(size
);
6926 unsigned szone_check_counter
= 0;
6927 unsigned szone_check_start
= 0;
6928 unsigned szone_check_modulo
= 1;
6930 static NOINLINE boolean_t
6931 szone_check_all(szone_t
*szone
, const char *function
)
6935 /* check tiny regions - chould check region count */
6936 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6937 region_t tiny
= szone
->tiny_region_generation
->hashed_regions
[index
];
6939 if (HASHRING_REGION_DEALLOCATED
== tiny
)
6943 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6944 REGION_TRAILER_FOR_TINY_REGION(tiny
), MAGAZINE_INDEX_FOR_TINY_REGION(tiny
));
6946 if (!tiny_check_region(szone
, tiny
)) {
6947 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6948 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6949 szone_error(szone
, 1, "check: tiny region incorrect", NULL
,
6950 "*** tiny region %ld incorrect szone_check_all(%s) counter=%d\n",
6951 index
, function
, szone_check_counter
);
6954 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6957 /* check tiny free lists */
6958 for (index
= 0; index
< NUM_TINY_SLOTS
; ++index
) {
6959 if (!tiny_free_list_check(szone
, index
)) {
6960 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6961 szone_error(szone
, 1, "check: tiny free list incorrect", NULL
,
6962 "*** tiny free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6963 index
, function
, szone_check_counter
);
6968 /* check small regions - could check region count */
6969 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6970 region_t small
= szone
->small_region_generation
->hashed_regions
[index
];
6972 if (HASHRING_REGION_DEALLOCATED
== small
)
6976 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6977 REGION_TRAILER_FOR_SMALL_REGION(small
), MAGAZINE_INDEX_FOR_SMALL_REGION(small
));
6979 if (!small_check_region(szone
, small
)) {
6980 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6981 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6982 szone_error(szone
, 1, "check: small region incorrect", NULL
,
6983 "*** small region %ld incorrect szone_check_all(%s) counter=%d\n",
6984 index
, function
, szone_check_counter
);
6987 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6990 /* check small free lists */
6991 for (index
= 0; index
< szone
->num_small_slots
; ++index
) {
6992 if (!small_free_list_check(szone
, index
)) {
6993 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6994 szone_error(szone
, 1, "check: small free list incorrect", NULL
,
6995 "*** small free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6996 index
, function
, szone_check_counter
);
7005 szone_check(szone_t
*szone
)
7007 if ((++szone_check_counter
% 10000) == 0)
7008 _malloc_printf(ASL_LEVEL_NOTICE
, "at szone_check counter=%d\n", szone_check_counter
);
7010 if (szone_check_counter
< szone_check_start
)
7013 if (szone_check_counter
% szone_check_modulo
)
7016 return szone_check_all(szone
, "");
7019 static kern_return_t
7020 szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
7021 memory_reader_t reader
, vm_range_recorder_t recorder
)
7026 if (!reader
) reader
= _szone_default_reader
;
7028 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
7029 if (err
) return err
;
7031 err
= tiny_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
7032 if (err
) return err
;
7034 err
= small_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
7035 if (err
) return err
;
7037 err
= large_in_use_enumerator(task
, context
, type_mask
,
7038 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
7042 // Following method is deprecated: use scalable_zone_statistics instead
7044 scalable_zone_info(malloc_zone_t
*zone
, unsigned *info_to_fill
, unsigned count
)
7046 szone_t
*szone
= (void *)zone
;
7049 // We do not lock to facilitate debug
7054 mag_index_t mag_index
;
7056 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
7057 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
;
7058 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
7059 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
7060 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
7066 for (t
= 0, u
= 0, mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
7067 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
;
7068 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
7069 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
7070 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
7076 info
[8] = szone
->num_large_objects_in_use
;
7077 info
[9] = szone
->num_bytes_in_large_objects
;
7079 info
[10] = 0; // DEPRECATED szone->num_huge_entries;
7080 info
[11] = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7082 info
[12] = szone
->debug_flags
;
7084 info
[0] = info
[4] + info
[6] + info
[8] + info
[10];
7085 info
[1] = info
[5] + info
[7] + info
[9] + info
[11];
7087 info
[3] = (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
7088 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ info
[9] + info
[11];
7090 info
[2] = info
[3] - s
;
7091 memcpy(info_to_fill
, info
, sizeof(unsigned)*count
);
7094 // FIXME: consistent picture requires locking!
7095 static NOINLINE
void
7096 szone_print(szone_t
*szone
, boolean_t verbose
)
7102 scalable_zone_info((void *)szone
, info
, 13);
7103 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7104 "Scalable zone %p: inUse=%u(%y) touched=%y allocated=%y flags=%d\n",
7105 szone
, info
[0], info
[1], info
[2], info
[3], info
[12]);
7106 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7107 "\ttiny=%u(%y) small=%u(%y) large=%u(%y) huge=%u(%y)\n",
7108 info
[4], info
[5], info
[6], info
[7], info
[8], info
[9], info
[10], info
[11]);
7110 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7111 "%lu tiny regions:\n", szone
->num_tiny_regions
);
7112 if (szone
->num_tiny_regions_dealloc
)
7113 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7114 "[%lu tiny regions have been vm_deallocate'd]\n", szone
->num_tiny_regions_dealloc
);
7115 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
7116 region
= szone
->tiny_region_generation
->hashed_regions
[index
];
7117 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
7118 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
7119 print_tiny_region(verbose
, region
,
7120 (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
7121 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
: 0,
7122 (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
7123 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
7127 print_tiny_free_list(szone
);
7129 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7130 "%lu small regions:\n", szone
->num_small_regions
);
7131 if (szone
->num_small_regions_dealloc
)
7132 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7133 "[%lu small regions have been vm_deallocate'd]\n", szone
->num_small_regions_dealloc
);
7134 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
7135 region
= szone
->small_region_generation
->hashed_regions
[index
];
7136 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
7137 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(region
);
7138 print_small_region(szone
, verbose
, region
,
7139 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
7140 szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
: 0,
7141 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
7142 szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
7146 print_small_free_list(szone
);
7150 szone_log(malloc_zone_t
*zone
, void *log_address
)
7152 szone_t
*szone
= (szone_t
*)zone
;
7154 szone
->log_address
= log_address
;
7158 szone_force_lock(szone_t
*szone
)
7162 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
7163 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7165 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
])));
7167 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
7168 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[i
])));
7170 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
])));
7176 szone_force_unlock(szone_t
*szone
)
7180 SZONE_UNLOCK(szone
);
7182 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
7183 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
7186 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
7187 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7192 szone_locked(szone_t
*szone
)
7197 tookLock
= SZONE_TRY_LOCK(szone
);
7200 SZONE_UNLOCK(szone
);
7202 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
7203 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->small_magazines
[i
])));
7206 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
7209 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
7210 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7213 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7219 szone_pressure_relief(szone_t
*szone
, size_t goal
)
7222 if (!szone
->flotsam_enabled
)
7227 // stack allocated copy of the death-row cache
7228 int idx
= szone
->large_entry_cache_oldest
, idx_max
= szone
->large_entry_cache_newest
;
7229 large_entry_t local_entry_cache
[LARGE_ENTRY_CACHE_SIZE
];
7231 memcpy((void *)local_entry_cache
, (void *)szone
->large_entry_cache
, sizeof(local_entry_cache
));
7233 szone
->large_entry_cache_oldest
= szone
->large_entry_cache_newest
= 0;
7234 szone
->large_entry_cache
[0].address
= 0x0;
7235 szone
->large_entry_cache
[0].size
= 0;
7236 szone
->large_entry_cache_bytes
= 0;
7237 szone
->large_entry_cache_reserve_bytes
= 0;
7239 szone
->flotsam_enabled
= FALSE
;
7241 SZONE_UNLOCK(szone
);
7243 // deallocate the death-row cache outside the zone lock
7245 while (idx
!= idx_max
) {
7246 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
7247 total
+= local_entry_cache
[idx
].size
;
7248 if (++idx
== LARGE_ENTRY_CACHE_SIZE
) idx
= 0;
7250 if (0 != local_entry_cache
[idx
].address
&& 0 != local_entry_cache
[idx
].size
) {
7251 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
7252 total
+= local_entry_cache
[idx
].size
;
7254 MAGMALLOC_PRESSURERELIEF((void *)szone
, goal
, total
); // DTrace USDT Probe
7262 scalable_zone_statistics(malloc_zone_t
*zone
, malloc_statistics_t
*stats
, unsigned subzone
)
7264 szone_t
*szone
= (szone_t
*)zone
;
7272 mag_index_t mag_index
;
7274 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
7275 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
;
7276 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
7277 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
7278 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
7281 stats
->blocks_in_use
= t
;
7282 stats
->size_in_use
= u
;
7283 stats
->size_allocated
= (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
;
7284 stats
->max_size_in_use
= stats
->size_allocated
- s
;
7292 mag_index_t mag_index
;
7294 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
7295 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
;
7296 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
7297 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
7298 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
7301 stats
->blocks_in_use
= t
;
7302 stats
->size_in_use
= u
;
7303 stats
->size_allocated
= (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
;
7304 stats
->max_size_in_use
= stats
->size_allocated
- s
;
7308 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
7309 stats
->size_in_use
= szone
->num_bytes_in_large_objects
;
7310 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
7313 stats
->blocks_in_use
= 0; // DEPRECATED szone->num_huge_entries;
7314 stats
->size_in_use
= 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7315 stats
->max_size_in_use
= stats
->size_allocated
= 0;
7322 szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
7329 mag_index_t mag_index
;
7331 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
7332 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
;
7333 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
7334 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
7335 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
7338 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
7339 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
;
7340 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
7341 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
7342 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
7345 large
= szone
->num_bytes_in_large_objects
+ 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7347 stats
->blocks_in_use
= t
+ szone
->num_large_objects_in_use
+ 0; // DEPRECATED szone->num_huge_entries;
7348 stats
->size_in_use
= u
+ large
;
7349 stats
->max_size_in_use
= stats
->size_allocated
=
7350 (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
7351 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ large
;
7352 // Now we account for the untouched areas
7353 stats
->max_size_in_use
-= s
;
7357 legacy_zeroing_large_malloc(szone_t
*szone
, size_t size
) {
7358 if (size
> LARGE_THRESHOLD
) // Leopard and earlier returned a ZFOD range, so ...
7359 return szone_calloc(szone
, 1, size
); // Clear to zero always, ham-handedly touching in each page
7361 return szone_malloc(szone
, size
);
7365 legacy_zeroing_large_valloc(szone_t
*szone
, size_t size
) {
7366 void *p
= szone_valloc(szone
, size
);
7368 // Leopard and earlier returned a ZFOD range, so ...
7369 memset(p
, 0, size
); // Clear to zero always, ham-handedly touching in each page
7373 void zeroify_scalable_zone(malloc_zone_t
*zone
)
7375 szone_t
*szone
= (szone_t
*)zone
;
7378 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
| PROT_WRITE
);
7379 szone
->basic_zone
.malloc
= (void *)legacy_zeroing_large_malloc
;
7380 szone
->basic_zone
.valloc
= (void *)legacy_zeroing_large_valloc
;
7381 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
);
7385 static const struct malloc_introspection_t szone_introspect
= {
7386 (void *)szone_ptr_in_use_enumerator
,
7387 (void *)szone_good_size
,
7388 (void *)szone_check
,
7389 (void *)szone_print
,
7391 (void *)szone_force_lock
,
7392 (void *)szone_force_unlock
,
7393 (void *)szone_statistics
,
7394 (void *)szone_locked
,
7395 NULL
, NULL
, NULL
, NULL
, /* Zone enumeration version 7 and forward. */
7396 }; // marked as const to spare the DATA section
7399 create_scalable_zone(size_t initial_size
, unsigned debug_flags
)
7402 uint64_t hw_memsize
= 0;
7405 * Sanity-check our build-time assumptions about the size of a page.
7406 * Since we have sized various things assuming the default page size,
7407 * attempting to determine it dynamically is not useful.
7409 if ((vm_page_size
!= _vm_page_size
) || (vm_page_shift
!= _vm_page_shift
)) {
7410 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
7414 #if defined(__i386__) || defined(__x86_64__)
7415 if (_COMM_PAGE_VERSION_REQD
> (*((short *) _COMM_PAGE_VERSION
))) { // _COMM_PAGE_CPU_NUMBER must be present at runtime
7416 malloc_printf("*** ERROR - comm page version mismatch.\n");
7421 /* get memory for the zone. */
7422 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, 0, VM_MEMORY_MALLOC
);
7426 /* set up the szone structure */
7428 #warning CHECK_REGIONS enabled
7429 debug_flags
|= CHECK_REGIONS
;
7432 #warning LOG enabled
7433 szone
->log_address
= ~0;
7435 szone
->trg
[0].nextgen
= &(szone
->trg
[1]);
7436 szone
->trg
[1].nextgen
= &(szone
->trg
[0]);
7437 szone
->tiny_region_generation
= &(szone
->trg
[0]);
7439 szone
->tiny_region_generation
->hashed_regions
= szone
->initial_tiny_regions
;
7440 szone
->tiny_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
7441 szone
->tiny_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
7443 szone
->srg
[0].nextgen
= &(szone
->srg
[1]);
7444 szone
->srg
[1].nextgen
= &(szone
->srg
[0]);
7445 szone
->small_region_generation
= &(szone
->srg
[0]);
7447 szone
->small_region_generation
->hashed_regions
= szone
->initial_small_regions
;
7448 szone
->small_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
7449 szone
->small_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
7453 * Initialize variables that size the free list for SMALL allocations based
7454 * upon the amount of memory in the system. Switch to a larger number of
7455 * free list entries at 1GB.
7457 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7458 if ((hw_memsize
= *(uint64_t *)(uintptr_t)_COMM_PAGE_MEMORY_SIZE
) >= (1ULL << 30))
7460 size_t uint64_t_size
= sizeof(hw_memsize
);
7462 if (0 == sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0) &&
7463 hw_memsize
>= (1ULL << 30))
7466 szone
->is_largemem
= 1;
7467 szone
->num_small_slots
= NUM_SMALL_SLOTS_LARGEMEM
;
7468 szone
->large_threshold
= LARGE_THRESHOLD_LARGEMEM
;
7469 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD_LARGEMEM
;
7471 szone
->is_largemem
= 0;
7472 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
7473 szone
->large_threshold
= LARGE_THRESHOLD
;
7474 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7477 szone
->large_entry_cache_reserve_limit
=
7478 hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
7480 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7481 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
7482 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
7483 szone
->large_legacy_reset_mprotect
= TRUE
;
7485 szone
->large_legacy_reset_mprotect
= FALSE
;
7488 // Initialize the security token.
7489 szone
->cookie
= (uintptr_t)malloc_entropy
[0];
7492 #if __i386__ || __LP64__ || TARGET_OS_EMBEDDED
7494 uintptr_t stackbase
= 0x8fe00000;
7495 int entropic_bits
= 3;
7497 uintptr_t stackbase
= USRSTACK64
;
7498 int entropic_bits
= 16;
7500 uintptr_t stackbase
= USRSTACK
;
7501 int entropic_bits
= 3;
7503 if (0 != _dyld_get_image_slide((const struct mach_header
*)_NSGetMachExecuteHeader())) {
7504 if (0 == entropic_address
) {
7505 uintptr_t t
= stackbase
- MAXSSIZ
- ((uintptr_t) (malloc_entropy
[1] & ((1 << entropic_bits
) - 1)) << SMALL_BLOCKS_ALIGN
);
7506 (void)__sync_bool_compare_and_swap(&entropic_limit
, 0, t
); // Just one initialization please
7507 (void)__sync_bool_compare_and_swap(&entropic_address
, 0, t
- ENTROPIC_KABILLION
); // Just one initialization please
7509 debug_flags
&= ~DISABLE_ASLR
;
7511 debug_flags
|= DISABLE_ASLR
;
7515 debug_flags
|= DISABLE_ASLR
;
7518 szone
->basic_zone
.version
= 8;
7519 szone
->basic_zone
.size
= (void *)szone_size
;
7520 szone
->basic_zone
.malloc
= (void *)szone_malloc
;
7521 szone
->basic_zone
.calloc
= (void *)szone_calloc
;
7522 szone
->basic_zone
.valloc
= (void *)szone_valloc
;
7523 szone
->basic_zone
.free
= (void *)szone_free
;
7524 szone
->basic_zone
.realloc
= (void *)szone_realloc
;
7525 szone
->basic_zone
.destroy
= (void *)szone_destroy
;
7526 szone
->basic_zone
.batch_malloc
= (void *)szone_batch_malloc
;
7527 szone
->basic_zone
.batch_free
= (void *)szone_batch_free
;
7528 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
7529 szone
->basic_zone
.memalign
= (void *)szone_memalign
;
7530 szone
->basic_zone
.free_definite_size
= (void *)szone_free_definite_size
;
7531 szone
->basic_zone
.pressure_relief
= (void *)szone_pressure_relief
;
7533 szone
->basic_zone
.reserved1
= 0; /* Set to zero once and for all as required by CFAllocator. */
7534 szone
->basic_zone
.reserved2
= 0; /* Set to zero once and for all as required by CFAllocator. */
7535 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
); /* Prevent overwriting the function pointers in basic_zone. */
7537 szone
->debug_flags
= debug_flags
;
7538 LOCK_INIT(szone
->large_szone_lock
);
7540 #if defined(__ppc__) || defined(__ppc64__)
7542 * In the interest of compatibility for PPC applications executing via Rosetta,
7543 * arrange to zero-fill allocations as occurred by side effect in Leopard and earlier.
7545 zeroify_scalable_zone((malloc_zone_t
*)szone
);
7548 #if defined(__i386__) || defined(__x86_64__)
7549 szone
->cpu_id_key
= (pthread_key_t
) -1; // Unused. _COMM_PAGE_CPU_NUMBER preferred.
7552 if ((err
= pthread_key_create(&(szone
->cpu_id_key
), NULL
))) {
7553 malloc_printf("*** ERROR -pthread_key_create failure err=%d.\n", err
);
7554 szone
->cpu_id_key
= (pthread_key_t
) -1;
7558 // Query the number of configured processors.
7559 // Uniprocessor case gets just one tiny and one small magazine (whose index is zero). This gives
7560 // the same behavior as the original scalable malloc. MP gets per-CPU magazines
7561 // that scale (way) better.
7562 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7563 int nproc
= *(uint8_t *)(uintptr_t)_COMM_PAGE_NCPUS
;
7565 int nproc
= sysconf(_SC_NPROCESSORS_CONF
);
7567 szone
->num_tiny_magazines
= (nproc
> 1) ? MIN(nproc
, TINY_MAX_MAGAZINES
) : 1;
7569 // FIXME vm_allocate() based on number of configured CPUs
7570 magazine_t
*tiny_magazines
= allocate_pages(NULL
, TINY_MAGAZINE_PAGED_SIZE
, 0,
7571 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
7572 if (NULL
== tiny_magazines
)
7575 szone
->tiny_magazines
= &(tiny_magazines
[1]); // szone->tiny_magazines[-1] is the Depot
7577 // The magazines are indexed in [0 .. (num_tiny_magazines - 1)]
7578 // Find the smallest power of 2 that exceeds (num_tiny_magazines - 1)
7579 szone
->num_tiny_magazines_mask_shift
= 0;
7581 while( i
<= (szone
->num_tiny_magazines
- 1) ) {
7582 szone
->num_tiny_magazines_mask_shift
++;
7586 // Now if i <= TINY_MAX_MAGAZINES we'll never access tiny_magazines[] out of bounds.
7587 if (i
> TINY_MAX_MAGAZINES
) {
7588 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
7592 // Reduce i by 1 to obtain a mask covering [0 .. (num_tiny_magazines - 1)]
7593 szone
->num_tiny_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
7594 #if TARGET_OS_EMBEDDED
7595 szone
->last_tiny_advise
= 0;
7598 // Init the tiny_magazine locks
7599 LOCK_INIT(szone
->tiny_regions_lock
);
7600 LOCK_INIT(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
7601 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
7602 LOCK_INIT(szone
->tiny_magazines
[i
].magazine_lock
);
7605 szone
->num_small_magazines
= (nproc
> 1) ? MIN(nproc
, SMALL_MAX_MAGAZINES
) : 1;
7607 // FIXME vm_allocate() based on number of configured CPUs
7608 magazine_t
*small_magazines
= allocate_pages(NULL
, SMALL_MAGAZINE_PAGED_SIZE
, 0,
7609 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
7610 if (NULL
== small_magazines
)
7613 szone
->small_magazines
= &(small_magazines
[1]); // szone->small_magazines[-1] is the Depot
7615 // The magazines are indexed in [0 .. (num_small_magazines - 1)]
7616 // Find the smallest power of 2 that exceeds (num_small_magazines - 1)
7617 szone
->num_small_magazines_mask_shift
= 0;
7618 while( i
<= (szone
->num_small_magazines
- 1) ) {
7619 szone
->num_small_magazines_mask_shift
++;
7623 // Now if i <= SMALL_MAX_MAGAZINES we'll never access small_magazines[] out of bounds.
7624 if (i
> SMALL_MAX_MAGAZINES
) {
7625 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
7629 // Reduce i by 1 to obtain a mask covering [0 .. (num_small_magazines - 1)]
7630 szone
->num_small_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
7631 #if TARGET_OS_EMBEDDED
7632 szone
->last_small_advise
= 0;
7635 // Init the small_magazine locks
7636 LOCK_INIT(szone
->small_regions_lock
);
7637 LOCK_INIT(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
7638 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
7639 LOCK_INIT(szone
->small_magazines
[i
].magazine_lock
);
7642 CHECK(szone
, __PRETTY_FUNCTION__
);
7643 return (malloc_zone_t
*)szone
;
7647 // purgeable zones have their own "large" allocation pool, but share "tiny" and "large"
7648 // heaps with a helper_zone identified in the call to create_purgeable_zone()
7651 purgeable_size(szone_t
*szone
, const void *ptr
)
7653 // Only claim our large allocations, leave the shared tiny/small for the helper zone to claim.
7654 return szone_size_try_large(szone
, ptr
);
7658 purgeable_malloc(szone_t
*szone
, size_t size
) {
7659 if (size
<= szone
->large_threshold
)
7660 return szone_malloc(szone
->helper_zone
, size
);
7662 return szone_malloc(szone
, size
);
7666 purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
7668 size_t total_bytes
= num_items
* size
;
7670 // Check for overflow of integer multiplication
7671 if (num_items
> 1) {
7672 #if __LP64__ /* size_t is uint64_t */
7673 if ((num_items
| size
) & 0xffffffff00000000ul
) {
7674 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
7675 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
7676 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
7679 #else /* size_t is uint32_t */
7680 if ((num_items
| size
) & 0xffff0000ul
) {
7681 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
7682 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
7683 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
7689 if (total_bytes
<= szone
->large_threshold
)
7690 return szone_calloc(szone
->helper_zone
, 1, total_bytes
);
7692 return szone_calloc(szone
, 1, total_bytes
);
7696 purgeable_valloc(szone_t
*szone
, size_t size
)
7698 if (size
<= szone
->large_threshold
)
7699 return szone_valloc(szone
->helper_zone
, size
);
7701 return szone_valloc(szone
, size
);
7705 purgeable_free(szone_t
*szone
, void *ptr
)
7707 large_entry_t
*entry
;
7710 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
7711 SZONE_UNLOCK(szone
);
7713 return free_large(szone
, ptr
);
7715 return szone_free(szone
->helper_zone
, ptr
);
7720 purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
7722 if (size
<= szone
->large_threshold
)
7723 return szone_free_definite_size(szone
->helper_zone
, ptr
, size
);
7725 return szone_free_definite_size(szone
, ptr
, size
);
7729 purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
7734 // If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size.
7735 return purgeable_malloc(szone
, new_size
);
7736 } else if (0 == new_size
) {
7737 // If size is 0 and ptr is not a null pointer, the object pointed to is freed.
7738 purgeable_free(szone
, ptr
);
7739 // If size is 0, either a null pointer or a unique pointer that can be successfully passed
7740 // to free() shall be returned.
7741 return purgeable_malloc(szone
, 1);
7744 old_size
= purgeable_size(szone
, ptr
); // Now ptr can be safely size()'d
7746 old_size
= szone_size(szone
->helper_zone
, ptr
);
7749 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
7753 // Distinguish 4 cases: {oldsize, newsize} x { <= , > large_threshold }
7754 // and deal with the allocation crossing from the purgeable zone to the helper zone and vice versa.
7755 if (old_size
<= szone
->large_threshold
) {
7756 if (new_size
<= szone
->large_threshold
)
7757 return szone_realloc(szone
->helper_zone
, ptr
, new_size
);
7759 // allocation crosses from helper to purgeable zone
7760 void * new_ptr
= purgeable_malloc(szone
, new_size
);
7762 memcpy(new_ptr
, ptr
, old_size
);
7763 szone_free_definite_size(szone
->helper_zone
, ptr
, old_size
);
7765 return new_ptr
; // in state VM_PURGABLE_NONVOLATILE
7768 if (new_size
<= szone
->large_threshold
) {
7769 // allocation crosses from purgeable to helper zone
7770 void * new_ptr
= szone_malloc(szone
->helper_zone
, new_size
);
7772 memcpy(new_ptr
, ptr
, new_size
);
7773 purgeable_free_definite_size(szone
, ptr
, old_size
);
7777 void * new_ptr
= purgeable_malloc(szone
, new_size
);
7779 memcpy(new_ptr
, ptr
, MIN(old_size
, new_size
));
7780 purgeable_free_definite_size(szone
, ptr
, old_size
);
7782 return new_ptr
; // in state VM_PURGABLE_NONVOLATILE
7789 purgeable_destroy(szone_t
*szone
)
7791 /* destroy large entries */
7792 size_t index
= szone
->num_large_entries
;
7793 large_entry_t
*large
;
7794 vm_range_t range_to_deallocate
;
7797 large
= szone
->large_entries
+ index
;
7798 if (large
->address
) {
7799 // we deallocate_pages, including guard pages
7800 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
7803 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
7804 if (range_to_deallocate
.size
)
7805 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
7807 /* Now destroy the separate szone region */
7808 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, 0);
7812 purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
7814 return szone_batch_malloc(szone
->helper_zone
, size
, results
, count
);
7818 purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
7820 return szone_batch_free(szone
->helper_zone
, to_be_freed
, count
);
7824 purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
7826 if (size
<= szone
->large_threshold
)
7827 return szone_memalign(szone
->helper_zone
, alignment
, size
);
7829 return szone_memalign(szone
, alignment
, size
);
7832 static kern_return_t
7833 purgeable_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
7834 memory_reader_t reader
, vm_range_recorder_t recorder
)
7839 if (!reader
) reader
= _szone_default_reader
;
7841 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
7842 if (err
) return err
;
7844 err
= large_in_use_enumerator(task
, context
, type_mask
,
7845 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
7850 purgeable_good_size(szone_t
*szone
, size_t size
)
7852 if (size
<= szone
->large_threshold
)
7853 return szone_good_size(szone
->helper_zone
, size
);
7855 return szone_good_size(szone
, size
);
7859 purgeable_check(szone_t
*szone
)
7865 purgeable_print(szone_t
*szone
, boolean_t verbose
)
7867 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7868 "Scalable zone %p: inUse=%u(%y) flags=%d\n",
7869 szone
, szone
->num_large_objects_in_use
, szone
->num_bytes_in_large_objects
, szone
->debug_flags
);
7873 purgeable_log(malloc_zone_t
*zone
, void *log_address
)
7875 szone_t
*szone
= (szone_t
*)zone
;
7877 szone
->log_address
= log_address
;
7881 purgeable_force_lock(szone_t
*szone
)
7887 purgeable_force_unlock(szone_t
*szone
)
7889 SZONE_UNLOCK(szone
);
7893 purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
7895 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
7896 stats
->size_in_use
= stats
->max_size_in_use
= stats
->size_allocated
= szone
->num_bytes_in_large_objects
;
7900 purgeable_locked(szone_t
*szone
)
7904 tookLock
= SZONE_TRY_LOCK(szone
);
7907 SZONE_UNLOCK(szone
);
7912 purgeable_pressure_relief(szone_t
*szone
, size_t goal
)
7914 return szone_pressure_relief(szone
, goal
) + szone_pressure_relief(szone
->helper_zone
, goal
);
7917 static const struct malloc_introspection_t purgeable_introspect
= {
7918 (void *)purgeable_ptr_in_use_enumerator
,
7919 (void *)purgeable_good_size
,
7920 (void *)purgeable_check
,
7921 (void *)purgeable_print
,
7923 (void *)purgeable_force_lock
,
7924 (void *)purgeable_force_unlock
,
7925 (void *)purgeable_statistics
,
7926 (void *)purgeable_locked
,
7927 NULL
, NULL
, NULL
, NULL
, /* Zone enumeration version 7 and forward. */
7928 }; // marked as const to spare the DATA section
7930 __private_extern__ malloc_zone_t
*
7931 create_purgeable_zone(size_t initial_size
, malloc_zone_t
*malloc_default_zone
, unsigned debug_flags
)
7934 uint64_t hw_memsize
= 0;
7936 /* get memory for the zone. */
7937 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, 0, VM_MEMORY_MALLOC
);
7941 /* set up the szone structure */
7943 #warning LOG enabled
7944 szone
->log_address
= ~0;
7947 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7948 hw_memsize
= *(uint64_t *)(uintptr_t)_COMM_PAGE_MEMORY_SIZE
;
7950 size_t uint64_t_size
= sizeof(hw_memsize
);
7952 sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0);
7954 /* Purgeable zone does not participate in the adaptive "largemem" sizing. */
7955 szone
->is_largemem
= 0;
7956 szone
->large_threshold
= LARGE_THRESHOLD
;
7957 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7960 szone
->large_entry_cache_reserve_limit
=
7961 hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
7963 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7964 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
7965 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
7966 szone
->large_legacy_reset_mprotect
= TRUE
;
7968 szone
->large_legacy_reset_mprotect
= FALSE
;
7971 szone
->basic_zone
.version
= 8;
7972 szone
->basic_zone
.size
= (void *)purgeable_size
;
7973 szone
->basic_zone
.malloc
= (void *)purgeable_malloc
;
7974 szone
->basic_zone
.calloc
= (void *)purgeable_calloc
;
7975 szone
->basic_zone
.valloc
= (void *)purgeable_valloc
;
7976 szone
->basic_zone
.free
= (void *)purgeable_free
;
7977 szone
->basic_zone
.realloc
= (void *)purgeable_realloc
;
7978 szone
->basic_zone
.destroy
= (void *)purgeable_destroy
;
7979 szone
->basic_zone
.batch_malloc
= (void *)purgeable_batch_malloc
;
7980 szone
->basic_zone
.batch_free
= (void *)purgeable_batch_free
;
7981 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&purgeable_introspect
;
7982 szone
->basic_zone
.memalign
= (void *)purgeable_memalign
;
7983 szone
->basic_zone
.free_definite_size
= (void *)purgeable_free_definite_size
;
7984 szone
->basic_zone
.pressure_relief
= (void *)purgeable_pressure_relief
;
7986 szone
->basic_zone
.reserved1
= 0; /* Set to zero once and for all as required by CFAllocator. */
7987 szone
->basic_zone
.reserved2
= 0; /* Set to zero once and for all as required by CFAllocator. */
7988 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
); /* Prevent overwriting the function pointers in basic_zone. */
7990 szone
->debug_flags
= debug_flags
| SCALABLE_MALLOC_PURGEABLE
;
7992 /* Purgeable zone does not support SCALABLE_MALLOC_ADD_GUARD_PAGES. */
7993 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
7994 _malloc_printf(ASL_LEVEL_INFO
, "purgeable zone does not support guard pages\n");
7995 szone
->debug_flags
&= ~SCALABLE_MALLOC_ADD_GUARD_PAGES
;
7998 LOCK_INIT(szone
->large_szone_lock
);
8000 szone
->helper_zone
= (struct szone_s
*)malloc_default_zone
;
8002 CHECK(szone
, __PRETTY_FUNCTION__
);
8003 return (malloc_zone_t
*)szone
;
8007 * For use by CheckFix: create a new zone whose behavior is, apart from
8008 * the use of death-row and per-CPU magazines, that of Leopard.
8010 static NOINLINE
void *
8011 legacy_valloc(szone_t
*szone
, size_t size
)
8016 num_pages
= round_page(size
) >> vm_page_shift
;
8017 ptr
= large_malloc(szone
, num_pages
, 0, TRUE
);
8019 if (LOG(szone
, ptr
))
8020 malloc_printf("legacy_valloc returned %p\n", ptr
);
8025 __private_extern__ malloc_zone_t
*
8026 create_legacy_scalable_zone(size_t initial_size
, unsigned debug_flags
)
8028 malloc_zone_t
*mzone
= create_scalable_zone(initial_size
, debug_flags
);
8029 szone_t
*szone
= (szone_t
*)mzone
;
8034 szone
->is_largemem
= 0;
8035 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
8036 szone
->large_threshold
= LARGE_THRESHOLD
;
8037 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
8039 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
| PROT_WRITE
);
8040 szone
->basic_zone
.valloc
= (void *)legacy_valloc
;
8041 szone
->basic_zone
.free_definite_size
= NULL
;
8042 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
);
8047 /********* Support code for emacs unexec ************/
8049 /* History of freezedry version numbers:
8051 * 1) Old malloc (before the scalable malloc implementation in this file
8053 * 2) Original freezedrying code for scalable malloc. This code was apparently
8054 * based on the old freezedrying code and was fundamentally flawed in its
8055 * assumption that tracking allocated memory regions was adequate to fake
8056 * operations on freezedried memory. This doesn't work, since scalable
8057 * malloc does not store flags in front of large page-aligned allocations.
8058 * 3) Original szone-based freezedrying code.
8059 * 4) Fresher malloc with tiny zone
8060 * 5) 32/64bit compatible malloc
8061 * 6) Metadata within 1MB and 8MB region for tiny and small
8063 * No version backward compatibility is provided, but the version number does
8064 * make it possible for malloc_jumpstart() to return an error if the application
8065 * was freezedried with an older version of malloc.
8067 #define MALLOC_FREEZEDRY_VERSION 6
8076 frozen_malloc(szone_t
*zone
, size_t new_size
)
8078 return malloc(new_size
);
8082 frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
)
8084 return calloc(num_items
, size
);
8088 frozen_valloc(szone_t
*zone
, size_t new_size
)
8090 return valloc(new_size
);
8094 frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
)
8096 size_t old_size
= szone_size(zone
, ptr
);
8099 if (new_size
<= old_size
) {
8102 new_ptr
= malloc(new_size
);
8104 memcpy(new_ptr
, ptr
, old_size
);
8110 frozen_free(szone_t
*zone
, void *ptr
)
8115 frozen_destroy(szone_t
*zone
)
8119 /********* Pseudo-private API for emacs unexec ************/
8122 * malloc_freezedry() records all of the szones in use, so that they can be
8123 * partially reconstituted by malloc_jumpstart(). Due to the differences
8124 * between reconstituted memory regions and those created by the szone code,
8125 * care is taken not to reallocate from the freezedried memory, except in the
8126 * case of a non-growing realloc().
8128 * Due to the flexibility provided by the zone registration mechanism, it is
8129 * impossible to implement generic freezedrying for any zone type. This code
8130 * only handles applications that use the szone allocator, so malloc_freezedry()
8131 * returns 0 (error) if any non-szone zones are encountered.
8135 malloc_freezedry(void)
8137 extern unsigned malloc_num_zones
;
8138 extern malloc_zone_t
**malloc_zones
;
8139 malloc_frozen
*data
;
8142 /* Allocate space in which to store the freezedry state. */
8143 data
= (malloc_frozen
*) malloc(sizeof(malloc_frozen
));
8145 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
8146 data
->version
= MALLOC_FREEZEDRY_VERSION
;
8148 /* Allocate the array of szone pointers. */
8149 data
->nszones
= malloc_num_zones
;
8150 data
->szones
= (szone_t
*) calloc(malloc_num_zones
, sizeof(szone_t
));
8153 * Fill in the array of szone structures. They are copied rather than
8154 * referenced, since the originals are likely to be clobbered during malloc
8157 for (i
= 0; i
< malloc_num_zones
; i
++) {
8158 if (strcmp(malloc_zones
[i
]->zone_name
, "DefaultMallocZone")) {
8159 /* Unknown zone type. */
8164 memcpy(&data
->szones
[i
], malloc_zones
[i
], sizeof(szone_t
));
8167 return((uintptr_t)data
);
8171 malloc_jumpstart(uintptr_t cookie
)
8173 malloc_frozen
*data
= (malloc_frozen
*)cookie
;
8176 if (data
->version
!= MALLOC_FREEZEDRY_VERSION
) {
8177 /* Unsupported freezedry version. */
8181 for (i
= 0; i
< data
->nszones
; i
++) {
8182 /* Set function pointers. Even the functions that stay the same must be
8183 * set, since there are no guarantees that they will be mapped to the
8184 * same addresses. */
8185 data
->szones
[i
].basic_zone
.size
= (void *) szone_size
;
8186 data
->szones
[i
].basic_zone
.malloc
= (void *) frozen_malloc
;
8187 data
->szones
[i
].basic_zone
.calloc
= (void *) frozen_calloc
;
8188 data
->szones
[i
].basic_zone
.valloc
= (void *) frozen_valloc
;
8189 data
->szones
[i
].basic_zone
.free
= (void *) frozen_free
;
8190 data
->szones
[i
].basic_zone
.realloc
= (void *) frozen_realloc
;
8191 data
->szones
[i
].basic_zone
.destroy
= (void *) frozen_destroy
;
8192 data
->szones
[i
].basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
8194 /* Register the freezedried zone. */
8195 malloc_zone_register(&data
->szones
[i
].basic_zone
);