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
85 #warning DEBUG_MALLOC ENABLED
88 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) \
90 if (__is_threaded && TRY_LOCK(mag_ptr->magazine_lock)) { \
91 malloc_printf("*** magazine_lock was not set %p in %s\n", \
92 mag_ptr->magazine_lock, fun); \
96 # define INLINE __inline__
97 # define ALWAYSINLINE __attribute__((always_inline))
98 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) {}
101 # define NOINLINE __attribute__((noinline))
103 #if defined(__i386__) || defined(__x86_64__)
104 #define CACHE_ALIGN __attribute__ ((aligned (128) )) /* Future-proofing at 128B */
105 #elif defined(__ppc__) || defined(__ppc64__)
106 #define CACHE_ALIGN __attribute__ ((aligned (128) ))
112 #define ASLR_INTERNAL 1
116 * Access to global variables is slow, so optimise our handling of vm_page_size
119 #define _vm_page_size vm_page_size /* to get to the originals */
120 #define _vm_page_shift vm_page_shift
121 #define vm_page_size 4096 /* our normal working sizes */
122 #define vm_page_shift 12
125 * msize - a type to refer to the number of quanta of a tiny or small
126 * allocation. A tiny block with an msize of 3 would be 3 << SHIFT_TINY_QUANTUM
129 typedef unsigned short msize_t
;
141 typedef unsigned int grain_t
; // N.B. wide enough to index all free slots
143 typedef int mag_index_t
;
145 #define CHECK_REGIONS (1 << 31)
146 #define DISABLE_ASLR (1 << 30)
148 #define MAX_RECORDER_BUFFER 256
150 /********************* DEFINITIONS for tiny ************************/
153 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the
154 * szone's hashed_regions pointer.
156 * Each region is laid out as a heap, followed by a header block, all within
157 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header
158 * is 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
160 * The header block is arranged as in struct tiny_region defined just below, and
161 * consists of two bitfields (or bit arrays) interleaved 32 bits by 32 bits.
163 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding
164 * TINY_QUANTUM block within the heap.
166 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
167 * that the corresponding quantum is the first quantum in a block (either in use or free). The
168 * in-use bit is set for the header if the block has been handed out (allocated). If the header
169 * bit is not set, the in-use bit is invalid.
171 * The szone maintains an array of NUM_TINY_SLOTS freelists, each of which is used to hold
172 * free objects of the corresponding quantum size.
174 * A free block is laid out depending on its size, in order to fit all free
175 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
176 * not store their size in the block, instead relying on the header information
177 * to determine their size. Blocks of two or more quanta have room to store
178 * their size in the block, and store it both after the 'next' pointer, and in
179 * the last 2 bytes of the block.
182 * Offset (32-bit mode) (64-bit mode)
188 * Offset (32-bit mode) (64-bit mode)
191 * 0x8 0x10 : size (in quantum counts)
192 * end - 2 end - 2 : size (in quantum counts)
195 * All fields are pointer-sized, except for the size which is an unsigned short.
199 #define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
200 #define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
202 #define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
205 #define NUM_TINY_SLOTS 64 // number of slots for free-lists
207 #define NUM_TINY_SLOTS 32 // number of slots for free-lists
210 #define NUM_TINY_BLOCKS 64520
211 #define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
212 #define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
213 #define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM) // 20
215 #define TINY_ENTROPY_BITS 15
216 #define TINY_ENTROPY_MASK ((1 << TINY_ENTROPY_BITS) - 1)
219 * Avoid having so much entropy that the end of a valid tiny allocation
220 * might overrun the end of the tiny region.
222 #if TINY_ENTROPY_MASK + NUM_TINY_SLOTS > NUM_TINY_BLOCKS
223 #error Too many entropy bits for tiny region requested
227 * Enough room for the data, followed by the bit arrays (2-bits per block)
228 * plus rounding to the nearest page.
230 #define CEIL_NUM_TINY_BLOCKS_WORDS (((NUM_TINY_BLOCKS + 31) & ~31) >> 5)
231 #define TINY_METADATA_SIZE (sizeof(region_trailer_t) + sizeof(tiny_header_inuse_pair_t) * CEIL_NUM_TINY_BLOCKS_WORDS)
232 #define TINY_REGION_SIZE \
233 ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
235 #define TINY_METADATA_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
238 * Beginning and end pointers for a region's heap.
240 #define TINY_REGION_ADDRESS(region) ((void *)(region))
241 #define TINY_REGION_END(region) ((void *)(((uintptr_t)(region)) + (NUM_TINY_BLOCKS * TINY_QUANTUM)))
244 * Locate the heap base for a pointer known to be within a tiny region.
246 #define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
249 * Convert between byte and msize units.
251 #define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
252 #define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
255 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
257 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
259 #define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
262 * Layout of a tiny region
264 typedef uint32_t tiny_block_t
[4]; // assert(TINY_QUANTUM == sizeof(tiny_block_t))
266 typedef struct tiny_header_inuse_pair
270 } tiny_header_inuse_pair_t
;
272 typedef struct region_trailer
274 struct region_trailer
*prev
;
275 struct region_trailer
*next
;
276 boolean_t recirc_suitable
;
277 boolean_t failedREUSE
;
278 volatile int pinned_to_depot
;
280 mag_index_t mag_index
;
283 typedef struct tiny_region
285 tiny_block_t blocks
[NUM_TINY_BLOCKS
];
287 region_trailer_t trailer
;
289 // The interleaved bit arrays comprising the header and inuse bitfields.
290 // The unused bits of each component in the last pair will be initialized to sentinel values.
291 tiny_header_inuse_pair_t pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
];
293 uint8_t pad
[TINY_REGION_SIZE
- (NUM_TINY_BLOCKS
* sizeof(tiny_block_t
)) - TINY_METADATA_SIZE
];
297 * Per-region meta data for tiny allocator
299 #define REGION_TRAILER_FOR_TINY_REGION(r) (&(((tiny_region_t)(r))->trailer))
300 #define MAGAZINE_INDEX_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->mag_index)
301 #define BYTES_USED_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->bytes_used)
304 * Locate the block header for a pointer known to be within a tiny region.
306 #define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)&(((tiny_region_t)TINY_REGION_FOR_PTR(_p))->pairs))
309 * Locate the inuse map for a given block header pointer.
311 #define TINY_INUSE_FOR_HEADER(_h) ((void *)&(((tiny_header_inuse_pair_t *)(_h))->inuse))
314 * Compute the bitmap index for a pointer known to be within a tiny region.
316 #define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
318 #define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
321 #warning TINY_CACHE turned off
324 #define TINY_REGION_PAYLOAD_BYTES (NUM_TINY_BLOCKS * TINY_QUANTUM)
326 /********************* DEFINITIONS for small ************************/
329 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's hashed_regions
332 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
333 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
334 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
337 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
338 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
340 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
341 * of the corresponding quantum size.
343 * A free block is laid out as:
345 * Offset (32-bit mode) (64-bit mode)
348 * 0x8 0x10 : size (in quantum counts)
349 * end - 2 end - 2 : size (in quantum counts)
352 * All fields are pointer-sized, except for the size which is an unsigned short.
356 #define SMALL_IS_FREE (1 << 15)
358 #define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
359 #define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
361 #define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
364 * The number of slots in the free-list for small blocks. To avoid going to
365 * vm system as often on large memory machines, increase the number of free list
366 * spots above some amount of RAM installed in the system.
368 #define NUM_SMALL_SLOTS 32
369 #define NUM_SMALL_SLOTS_LARGEMEM 256
370 #define SMALL_BITMAP_WORDS 8
373 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
374 * convention msize=0 => msize = (1<<15)
376 #define NUM_SMALL_BLOCKS 16320
377 #define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
378 #define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
379 #define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
381 #define SMALL_ENTROPY_BITS 13
382 #define SMALL_ENTROPY_MASK ((1 << SMALL_ENTROPY_BITS) - 1)
385 * Avoid having so much entropy that the end of a valid small allocation
386 * might overrun the end of the small region.
388 #if SMALL_ENTROPY_MASK + NUM_SMALL_SLOTS > NUM_SMALL_BLOCKS
389 #error Too many entropy bits for small region requested
392 #define SMALL_METADATA_SIZE (sizeof(region_trailer_t) + NUM_SMALL_BLOCKS * sizeof(msize_t))
393 #define SMALL_REGION_SIZE \
394 ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
396 #define SMALL_METADATA_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
399 * Beginning and end pointers for a region's heap.
401 #define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
402 #define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
405 * Locate the heap base for a pointer known to be within a small region.
407 #define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
410 * Convert between byte and msize units.
412 #define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
413 #define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
415 #define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
418 * Layout of a small region
420 typedef uint32_t small_block_t
[SMALL_QUANTUM
/sizeof(uint32_t)];
422 typedef struct small_region
424 small_block_t blocks
[NUM_SMALL_BLOCKS
];
426 region_trailer_t trailer
;
428 msize_t small_meta_words
[NUM_SMALL_BLOCKS
];
430 uint8_t pad
[SMALL_REGION_SIZE
- (NUM_SMALL_BLOCKS
* sizeof(small_block_t
)) - SMALL_METADATA_SIZE
];
434 * Per-region meta data for small allocator
436 #define REGION_TRAILER_FOR_SMALL_REGION(r) (&(((small_region_t)(r))->trailer))
437 #define MAGAZINE_INDEX_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->mag_index)
438 #define BYTES_USED_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->bytes_used)
441 * Locate the metadata base for a pointer known to be within a small region.
443 #define SMALL_META_HEADER_FOR_PTR(_p) (((small_region_t)SMALL_REGION_FOR_PTR(_p))->small_meta_words)
446 * Compute the metadata index for a pointer known to be within a small region.
448 #define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
451 * Find the metadata word for a pointer known to be within a small region.
453 #define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
456 * Determine whether a pointer known to be within a small region points to memory which is free.
458 #define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
461 * Extract the msize value for a pointer known to be within a small region.
463 #define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
465 #define SMALL_CACHE 1
467 #warning SMALL_CACHE turned off
470 #define SMALL_REGION_PAYLOAD_BYTES (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
472 /************************* DEFINITIONS for large ****************************/
474 #define LARGE_THRESHOLD (15 * 1024) // strictly above this use "large"
475 #define LARGE_THRESHOLD_LARGEMEM (127 * 1024)
477 #if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
478 #error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
481 #if (LARGE_THRESHOLD_LARGEMEM > NUM_SMALL_SLOTS_LARGEMEM * SMALL_QUANTUM)
482 #error LARGE_THRESHOLD_LARGEMEM should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
486 * When all memory is touched after a copy, vm_copy() is always a lose
487 * But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages
490 * This must be larger than LARGE_THRESHOLD
492 #define VM_COPY_THRESHOLD (40 * 1024)
493 #define VM_COPY_THRESHOLD_LARGEMEM (128 * 1024)
496 vm_address_t address
;
498 boolean_t did_madvise_reusable
;
501 #if !TARGET_OS_EMBEDDED
502 #define LARGE_CACHE 1
504 #define LARGE_CACHE 0
507 #warning LARGE_CACHE turned off
509 #if defined(__LP64__)
510 #define LARGE_ENTRY_CACHE_SIZE 16
511 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x80000000) /* 2Gb */
513 #define LARGE_ENTRY_CACHE_SIZE 8
514 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x02000000) /* 32Mb */
516 #define LARGE_CACHE_SIZE_ENTRY_LIMIT (LARGE_CACHE_SIZE_LIMIT/LARGE_ENTRY_CACHE_SIZE)
518 #define SZONE_FLOTSAM_THRESHOLD_LOW (1024 * 512)
519 #define SZONE_FLOTSAM_THRESHOLD_HIGH (1024 * 1024)
521 /*******************************************************************************
522 * Definitions for region hash
523 ******************************************************************************/
525 typedef void * region_t
;
526 typedef region_t
* rgnhdl_t
; /* A pointer into hashed_regions array. */
528 #define INITIAL_NUM_REGIONS_SHIFT 6 // log2(INITIAL_NUM_REGIONS)
529 #define INITIAL_NUM_REGIONS (1 << INITIAL_NUM_REGIONS_SHIFT) // Must be a power of 2!
530 #define HASHRING_OPEN_ENTRY ((region_t) 0) // Initial value and sentinel marking end of collision chain
531 #define HASHRING_REGION_DEALLOCATED ((region_t)-1) // Region at this slot reclaimed by OS
532 #define HASH_BLOCKS_ALIGN TINY_BLOCKS_ALIGN // MIN( TINY_BLOCKS_ALIGN, SMALL_BLOCKS_ALIGN, ... )
534 typedef struct region_hash_generation
{
535 size_t num_regions_allocated
;
536 size_t num_regions_allocated_shift
; // log2(num_regions_allocated)
537 region_t
*hashed_regions
; // hashed by location
538 struct region_hash_generation
*nextgen
;
539 } region_hash_generation_t
;
541 /*******************************************************************************
542 * Per-processor magazine for tiny and small allocators
543 ******************************************************************************/
545 typedef struct { // vm_allocate()'d, so the array of magazines is page-aligned to begin with.
546 // Take magazine_lock first, Depot lock when needed for recirc, then szone->{tiny,small}_regions_lock when needed for alloc
547 pthread_lock_t magazine_lock CACHE_ALIGN
;
548 // Protection for the crtical section that does allocate_pages outside the magazine_lock
549 volatile boolean_t alloc_underway
;
551 // One element deep "death row", optimizes malloc/free/malloc for identical size.
552 void *mag_last_free
; // low SHIFT_{TINY,SMALL}_QUANTUM bits indicate the msize
553 region_t mag_last_free_rgn
; // holds the region for mag_last_free
555 free_list_t
*mag_free_list
[256]; // assert( 256 >= MAX( NUM_TINY_SLOTS, NUM_SMALL_SLOTS_LARGEMEM ))
556 unsigned mag_bitmap
[8]; // assert( sizeof(mag_bitmap) << 3 >= sizeof(mag_free_list)/sizeof(free_list_t) )
558 // the first and last free region in the last block are treated as big blocks in use that are not accounted for
559 size_t mag_bytes_free_at_end
;
560 size_t mag_bytes_free_at_start
;
561 region_t mag_last_region
; // Valid iff mag_bytes_free_at_end || mag_bytes_free_at_start > 0
564 unsigned mag_num_objects
;
565 size_t mag_num_bytes_in_objects
;
566 size_t num_bytes_in_magazine
;
568 // recirculation list -- invariant: all regions owned by this magazine that meet the emptiness criteria
569 // are located nearer to the head of the list than any region that doesn't satisfy that criteria.
570 // Doubly linked list for efficient extraction.
571 unsigned recirculation_entries
;
572 region_trailer_t
*firstNode
;
573 region_trailer_t
*lastNode
;
576 uint64_t pad
[48]; // So sizeof(magazine_t) is 2560 bytes. FIXME: assert this at compile time
578 uint32_t pad
[12]; // So sizeof(magazine_t) is 1280 bytes. FIXME: assert this at compile time
582 #define TINY_MAX_MAGAZINES 32 /* MUST BE A POWER OF 2! */
583 #define TINY_MAGAZINE_PAGED_SIZE \
584 (((sizeof(magazine_t) * (TINY_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
585 ~ (vm_page_size - 1)) /* + 1 for the Depot */
587 #define SMALL_MAX_MAGAZINES 32 /* MUST BE A POWER OF 2! */
588 #define SMALL_MAGAZINE_PAGED_SIZE \
589 (((sizeof(magazine_t) * (SMALL_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
590 ~ (vm_page_size - 1)) /* + 1 for the Depot */
592 #define DEPOT_MAGAZINE_INDEX -1
594 /****************************** zone itself ***********************************/
597 * Note that objects whose adddress are held in pointers here must be pursued
598 * individually in the {tiny,small}_in_use_enumeration() routines. See for
599 * example the treatment of region_hash_generation and tiny_magazines below.
602 typedef struct szone_s
{ // vm_allocate()'d, so page-aligned to begin with.
603 malloc_zone_t basic_zone
; // first page will be given read-only protection
604 uint8_t pad
[vm_page_size
- sizeof(malloc_zone_t
)];
606 pthread_key_t cpu_id_key
; // remainder of structure is R/W (contains no function pointers)
607 unsigned debug_flags
;
610 /* Regions for tiny objects */
611 pthread_lock_t tiny_regions_lock CACHE_ALIGN
;
612 size_t num_tiny_regions
;
613 size_t num_tiny_regions_dealloc
;
614 region_hash_generation_t
*tiny_region_generation
;
615 region_hash_generation_t trg
[2];
617 int num_tiny_magazines
;
618 unsigned num_tiny_magazines_mask
;
619 int num_tiny_magazines_mask_shift
;
620 magazine_t
*tiny_magazines
; // array of per-processor magazines
622 #if TARGET_OS_EMBEDDED
623 uintptr_t last_tiny_advise
;
626 /* Regions for small objects */
627 pthread_lock_t small_regions_lock CACHE_ALIGN
;
628 size_t num_small_regions
;
629 size_t num_small_regions_dealloc
;
630 region_hash_generation_t
*small_region_generation
;
631 region_hash_generation_t srg
[2];
633 unsigned num_small_slots
; // determined by physmem size
635 int num_small_magazines
;
636 unsigned num_small_magazines_mask
;
637 int num_small_magazines_mask_shift
;
638 magazine_t
*small_magazines
; // array of per-processor magazines
640 #if TARGET_OS_EMBEDDED
641 uintptr_t last_small_advise
;
644 /* large objects: all the rest */
645 pthread_lock_t large_szone_lock CACHE_ALIGN
; // One customer at a time for large
646 unsigned num_large_objects_in_use
;
647 unsigned num_large_entries
;
648 large_entry_t
*large_entries
; // hashed by location; null entries don't count
649 size_t num_bytes_in_large_objects
;
652 int large_entry_cache_oldest
;
653 int large_entry_cache_newest
;
654 large_entry_t large_entry_cache
[LARGE_ENTRY_CACHE_SIZE
]; // "death row" for large malloc/free
655 boolean_t large_legacy_reset_mprotect
;
656 size_t large_entry_cache_reserve_bytes
;
657 size_t large_entry_cache_reserve_limit
;
658 size_t large_entry_cache_bytes
; // total size of death row, bytes
661 /* flag and limits pertaining to altered malloc behavior for systems with
662 large amounts of physical memory */
663 unsigned is_largemem
;
664 unsigned large_threshold
;
665 unsigned vm_copy_threshold
;
667 /* security cookie */
670 /* Initial region list */
671 region_t initial_tiny_regions
[INITIAL_NUM_REGIONS
];
672 region_t initial_small_regions
[INITIAL_NUM_REGIONS
];
674 /* The purgeable zone constructed by create_purgeable_zone() would like to hand off tiny and small
675 * allocations to the default scalable zone. Record the latter as the "helper" zone here. */
676 struct szone_s
*helper_zone
;
678 boolean_t flotsam_enabled
;
681 #define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
683 #if DEBUG_MALLOC || DEBUG_CLIENT
684 static void szone_sleep(void);
686 __private_extern__
void malloc_error_break(void);
688 // msg prints after fmt, ...
689 static NOINLINE
void szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
692 static void protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
);
693 static void *allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
,
695 static void *allocate_pages_securely(szone_t
*szone
, size_t size
, unsigned char align
,
697 static void deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
);
698 #if TARGET_OS_EMBEDDED
699 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
, uintptr_t *last
);
701 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
);
703 static kern_return_t
_szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
);
705 static INLINE mag_index_t
mag_get_thread_index(szone_t
*szone
) ALWAYSINLINE
;
706 static magazine_t
*mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
,
707 mag_index_t mag_index
);
709 static INLINE rgnhdl_t
hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
)
711 static void hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
);
712 static region_t
*hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
);
713 static region_t
*hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
,
714 size_t *mutable_shift
, size_t *new_size
);
716 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
) ALWAYSINLINE
;
717 static INLINE
uintptr_t free_list_checksum_ptr(szone_t
*szone
, void *p
) ALWAYSINLINE
;
718 static INLINE
void *free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
) ALWAYSINLINE
;
719 static unsigned free_list_count(szone_t
*szone
, free_list_t
*ptr
);
721 static INLINE
void recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
722 static INLINE
void recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
723 static INLINE
void recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
725 static INLINE
void BITARRAY_SET(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
726 static INLINE
void BITARRAY_CLR(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
727 static INLINE boolean_t
BITARRAY_BIT(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
729 static msize_t
get_tiny_free_size(const void *ptr
);
730 static msize_t
get_tiny_previous_free_msize(const void *ptr
);
731 static INLINE msize_t
get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
) ALWAYSINLINE
;
732 static INLINE
void set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
733 static INLINE
void set_tiny_meta_header_in_use_1(const void *ptr
) ALWAYSINLINE
;
734 static INLINE
void set_tiny_meta_header_middle(const void *ptr
) ALWAYSINLINE
;
735 static INLINE
void set_tiny_meta_header_free(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
736 static INLINE boolean_t
tiny_meta_header_is_free(const void *ptr
) ALWAYSINLINE
;
737 static INLINE
void *tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
) ALWAYSINLINE
;
739 static void tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
740 static void tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
741 static INLINE region_t
tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
743 static void tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
);
744 static int tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
745 static size_t tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
746 static void tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
747 static region_t
tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
748 static boolean_t
tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
);
749 static region_t
tiny_find_msize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
750 static boolean_t
tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
752 static INLINE boolean_t
tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
,
753 void *ptr
, msize_t msize
) ALWAYSINLINE
;
754 static void *tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
755 msize_t msize
, void *fresh_region
);
756 static boolean_t
tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
757 static boolean_t
tiny_check_region(szone_t
*szone
, region_t region
);
758 static kern_return_t
tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
759 memory_reader_t reader
, vm_range_recorder_t recorder
);
760 static void *tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
762 static INLINE
void *tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
763 static INLINE
void free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
) ALWAYSINLINE
;
764 static void print_tiny_free_list(szone_t
*szone
);
765 static void print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
);
766 static boolean_t
tiny_free_list_check(szone_t
*szone
, grain_t slot
);
768 static INLINE
void small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
) ALWAYSINLINE
;
769 static INLINE
void small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
) ALWAYSINLINE
;
770 static INLINE
void small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
) ALWAYSINLINE
;
771 static void small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
772 static void small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
773 static INLINE region_t
small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
775 static void small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
);
776 static int small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
777 static size_t small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
778 static void small_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
779 static region_t
small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
780 static boolean_t
small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
);
781 static region_t
small_find_msize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
782 static boolean_t
small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
);
783 static INLINE boolean_t
small_free_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, region_t region
,
784 void *ptr
, msize_t msize
) ALWAYSINLINE
;
785 static void *small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
786 msize_t msize
, void *fresh_region
);
787 static boolean_t
small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
788 static boolean_t
small_check_region(szone_t
*szone
, region_t region
);
789 static kern_return_t
small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
790 memory_reader_t reader
, vm_range_recorder_t recorder
);
791 static void *small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
793 static INLINE
void *small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
794 static INLINE
void free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
) ALWAYSINLINE
;
795 static void print_small_free_list(szone_t
*szone
);
796 static void print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
);
797 static boolean_t
small_free_list_check(szone_t
*szone
, grain_t grain
);
800 static void large_debug_print(szone_t
*szone
);
802 static large_entry_t
*large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
);
803 static void large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
);
804 static INLINE
void large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
) ALWAYSINLINE
;
805 static INLINE large_entry_t
*large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
) ALWAYSINLINE
;
806 static void large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
,
807 vm_range_t
*range_to_deallocate
);
808 static large_entry_t
*large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
);
809 static vm_range_t
large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
);
810 static NOINLINE kern_return_t
large_in_use_enumerator(task_t task
, void *context
,
811 unsigned type_mask
, vm_address_t large_entries_address
,
812 unsigned num_entries
, memory_reader_t reader
,
813 vm_range_recorder_t recorder
);
814 static void *large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
, boolean_t cleared_requested
);
815 static NOINLINE
void free_large(szone_t
*szone
, void *ptr
);
816 static INLINE
int large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
819 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
821 static NOINLINE
void szone_free(szone_t
*szone
, void *ptr
);
822 static NOINLINE
void *szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
);
823 static NOINLINE
void *szone_malloc(szone_t
*szone
, size_t size
);
824 static NOINLINE
void *szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
825 static NOINLINE
void *szone_valloc(szone_t
*szone
, size_t size
);
826 static NOINLINE
size_t szone_size_try_large(szone_t
*szone
, const void *ptr
);
827 static NOINLINE
size_t szone_size(szone_t
*szone
, const void *ptr
);
828 static NOINLINE
void *szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
829 static NOINLINE
void *szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
830 static NOINLINE
void szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
831 static NOINLINE
unsigned szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
832 static NOINLINE
void szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
833 static void szone_destroy(szone_t
*szone
);
834 static NOINLINE
size_t szone_good_size(szone_t
*szone
, size_t size
);
836 static NOINLINE boolean_t
szone_check_all(szone_t
*szone
, const char *function
);
837 static boolean_t
szone_check(szone_t
*szone
);
838 static kern_return_t
szone_ptr_in_use_enumerator(task_t task
, void *context
,
839 unsigned type_mask
, vm_address_t zone_address
,
840 memory_reader_t reader
, vm_range_recorder_t recorder
);
841 static NOINLINE
void szone_print(szone_t
*szone
, boolean_t verbose
);
842 static void szone_log(malloc_zone_t
*zone
, void *log_address
);
843 static void szone_force_lock(szone_t
*szone
);
844 static void szone_force_unlock(szone_t
*szone
);
845 static boolean_t
szone_locked(szone_t
*szone
);
847 static void szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
849 static void purgeable_free(szone_t
*szone
, void *ptr
);
850 static void *purgeable_malloc(szone_t
*szone
, size_t size
);
851 static void *purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
852 static void *purgeable_valloc(szone_t
*szone
, size_t size
);
853 static size_t purgeable_size(szone_t
*szone
, const void *ptr
);
854 static void *purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
855 static void *purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
856 static void purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
857 static unsigned purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
858 static void purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
859 static void purgeable_destroy(szone_t
*szone
);
860 static size_t purgeable_good_size(szone_t
*szone
, size_t size
);
862 static boolean_t
purgeable_check(szone_t
*szone
);
863 static kern_return_t
purgeable_ptr_in_use_enumerator(task_t task
, void *context
,
864 unsigned type_mask
, vm_address_t zone_address
,
865 memory_reader_t reader
, vm_range_recorder_t recorder
);
866 static void purgeable_print(szone_t
*szone
, boolean_t verbose
);
867 static void purgeable_log(malloc_zone_t
*zone
, void *log_address
);
868 static void purgeable_force_lock(szone_t
*szone
);
869 static void purgeable_force_unlock(szone_t
*szone
);
870 static boolean_t
purgeable_locked(szone_t
*szone
);
872 static void purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
874 static void *frozen_malloc(szone_t
*zone
, size_t new_size
);
875 static void *frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
);
876 static void *frozen_valloc(szone_t
*zone
, size_t new_size
);
877 static void *frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
);
878 static void frozen_free(szone_t
*zone
, void *ptr
);
879 static void frozen_destroy(szone_t
*zone
);
881 static volatile uintptr_t entropic_address
= 0;
882 static volatile uintptr_t entropic_limit
= 0;
883 #define ENTROPIC_KABILLION 0x10000000 /* 256Mb */
885 __private_extern__
uint64_t malloc_entropy
[2];
887 #define SZONE_LOCK(szone) \
889 LOCK(szone->large_szone_lock); \
892 #define SZONE_UNLOCK(szone) \
894 UNLOCK(szone->large_szone_lock); \
897 #define SZONE_TRY_LOCK(szone) \
898 TRY_LOCK(szone->large_szone_lock);
900 #define SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr) \
902 LOCK(mag_ptr->magazine_lock); \
905 #define SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr) \
907 UNLOCK(mag_ptr->magazine_lock); \
910 #define SZONE_MAGAZINE_PTR_TRY_LOCK(szone, mag_ptr) \
911 TRY_LOCK(mag_ptr->magazine_lock);
914 # define LOG(szone,ptr) \
915 (szone->log_address && (((uintptr_t)szone->log_address == -1) || \
916 (szone->log_address == (void *)(ptr))))
918 # define LOG(szone,ptr) 0
921 #if DEBUG_MALLOC || DEBUG_CLIENT
922 # define CHECK(szone,fun) \
923 if ((szone)->debug_flags & CHECK_REGIONS) \
924 szone_check_all(szone, fun)
926 # define CHECK(szone,fun) \
930 /********************* VERY LOW LEVEL UTILITIES ************************/
932 #if DEBUG_MALLOC || DEBUG_CLIENT
937 if (getenv("MallocErrorSleep")) {
938 _malloc_printf(ASL_LEVEL_NOTICE
, "*** sleeping to help debug\n");
939 sleep(3600); // to help debug
944 // msg prints after fmt, ...
946 szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
949 _SIMPLE_STRING b
= _simple_salloc();
951 if (szone
) SZONE_UNLOCK(szone
); // FIXME: unlock magazine and region locks?
955 _simple_vsprintf(b
, fmt
, ap
);
959 _simple_sprintf(b
, "*** error for object %p: %s\n", ptr
, msg
);
961 _simple_sprintf(b
, "*** error: %s\n", msg
);
963 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b
));
966 * Should only get here if vm_allocate() can't get a single page of
967 * memory, implying _simple_asl_log() would also fail. So we just
968 * print to the file descriptor.
972 _malloc_vprintf(MALLOC_PRINTF_NOLOG
, fmt
, ap
);
976 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error for object %p: %s\n", ptr
, msg
);
978 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error: %s\n", msg
);
980 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** set a breakpoint in malloc_error_break to debug\n");
982 malloc_error_break();
984 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 OSAtomicIncrement32Barrier(&(node
->pinned_to_depot
));
2634 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2635 #if TARGET_OS_EMBEDDED
2636 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
), &szone
->last_tiny_advise
);
2638 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
));
2640 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
2641 OSAtomicDecrement32Barrier(&(node
->pinned_to_depot
));
2644 #if !TARGET_OS_EMBEDDED
2645 if (0 < bytes_used
|| 0 < node
->pinned_to_depot
) {
2646 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
2647 so as to avoid thrashing between the Depot's free list and a magazines's free list
2648 with detach_region/reattach_region */
2650 /* Depot'd region is just now empty. Consider return to OS. */
2651 region_t r_dealloc
= tiny_free_try_depot_unmap_no_lock(szone
, tiny_mag_ptr
, node
);
2652 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2654 deallocate_pages(szone
, r_dealloc
, TINY_REGION_SIZE
, 0);
2655 return FALSE
; // Caller need not unlock
2660 return TRUE
; // Caller must do SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr)
2663 // Allocates from the last region or a freshly allocated region
2665 tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
2666 msize_t msize
, void * aligned_address
)
2670 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at_start
2671 if (tiny_mag_ptr
->mag_bytes_free_at_end
|| tiny_mag_ptr
->mag_bytes_free_at_start
)
2672 tiny_finalize_region(szone
, tiny_mag_ptr
);
2674 // We set the unused bits of the header in the last pair to be all ones, and those of the inuse to zeroes.
2675 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].header
=
2676 (NUM_TINY_BLOCKS
& 31) ? (0xFFFFFFFFU
<< (NUM_TINY_BLOCKS
& 31)) : 0;
2677 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].inuse
= 0;
2679 // Here find the only place in tinyland that (infrequently) takes the tiny_regions_lock.
2680 // Only one thread at a time should be permitted to assess the density of the hash
2681 // ring and adjust if needed.
2682 // Only one thread at a time should be permitted to insert its new region on
2684 // It is safe for all other threads to read the hash ring (hashed_regions) and
2685 // the associated sizes (num_regions_allocated and num_tiny_regions).
2687 LOCK(szone
->tiny_regions_lock
);
2689 // Check to see if the hash ring of tiny regions needs to grow. Try to
2690 // avoid the hash ring becoming too dense.
2691 if (szone
->tiny_region_generation
->num_regions_allocated
< (2 * szone
->num_tiny_regions
)) {
2692 region_t
*new_regions
;
2694 size_t new_shift
= szone
->tiny_region_generation
->num_regions_allocated_shift
; // In/Out parameter
2695 new_regions
= hash_regions_grow_no_lock(szone
, szone
->tiny_region_generation
->hashed_regions
,
2696 szone
->tiny_region_generation
->num_regions_allocated
,
2699 // Do not deallocate the current hashed_regions allocation since someone may
2700 // be iterating it. Instead, just leak it.
2702 // Prepare to advance to the "next generation" of the hash ring.
2703 szone
->tiny_region_generation
->nextgen
->hashed_regions
= new_regions
;
2704 szone
->tiny_region_generation
->nextgen
->num_regions_allocated
= new_size
;
2705 szone
->tiny_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
2707 // Throw the switch to atomically advance to the next generation.
2708 szone
->tiny_region_generation
= szone
->tiny_region_generation
->nextgen
;
2709 // Ensure everyone sees the advance.
2712 // Tag the region at "aligned_address" as belonging to us,
2713 // and so put it under the protection of the magazine lock we are holding.
2714 // Do this before advertising "aligned_address" on the hash ring(!)
2715 MAGAZINE_INDEX_FOR_TINY_REGION(aligned_address
) = mag_index
;
2717 // Insert the new region into the hash ring, and update malloc statistics
2718 hash_region_insert_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2719 szone
->tiny_region_generation
->num_regions_allocated
,
2720 szone
->tiny_region_generation
->num_regions_allocated_shift
,
2723 szone
->num_tiny_regions
++;
2724 UNLOCK(szone
->tiny_regions_lock
);
2726 tiny_mag_ptr
->mag_last_region
= aligned_address
;
2727 BYTES_USED_FOR_TINY_REGION(aligned_address
) = TINY_BYTES_FOR_MSIZE(msize
);
2729 int offset_msize
= malloc_entropy
[0] & TINY_ENTROPY_MASK
;
2731 if (getenv("MallocASLRForce")) offset_msize
= strtol(getenv("MallocASLRForce"), NULL
, 0) & TINY_ENTROPY_MASK
;
2732 if (getenv("MallocASLRPrint")) malloc_printf("Region: %p offset: %d\n", aligned_address
, offset_msize
);
2735 int offset_msize
= 0;
2737 ptr
= (void *)((uintptr_t) aligned_address
+ TINY_BYTES_FOR_MSIZE(offset_msize
));
2738 set_tiny_meta_header_in_use(ptr
, msize
);
2739 tiny_mag_ptr
->mag_num_objects
++;
2740 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(msize
);
2741 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2743 // We put a header on the last block so that it appears in use (for coalescing, etc...)
2744 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
)));
2745 tiny_mag_ptr
->mag_bytes_free_at_end
= TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS
- msize
- offset_msize
);
2748 // Put a header on the previous block for same reason
2749 tiny_mag_ptr
->mag_bytes_free_at_start
= TINY_BYTES_FOR_MSIZE(offset_msize
);
2751 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
- TINY_QUANTUM
));
2754 tiny_mag_ptr
->mag_bytes_free_at_start
= 0;
2757 // connect to magazine as last node
2758 recirc_list_splice_last(szone
, tiny_mag_ptr
, REGION_TRAILER_FOR_TINY_REGION(aligned_address
));
2761 if (LOG(szone
,ptr
)) {
2762 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2768 static INLINE
void *
2769 tiny_try_shrink_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_good_size
)
2771 msize_t new_msize
= TINY_MSIZE_FOR_BYTES(new_good_size
);
2772 msize_t mshrinkage
= TINY_MSIZE_FOR_BYTES(old_size
) - new_msize
;
2775 void *q
= (void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(new_msize
));
2776 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2777 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2778 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2780 // Mark q as block header and in-use, thus creating two blocks.
2781 set_tiny_meta_header_in_use(q
, mshrinkage
);
2782 tiny_mag_ptr
->mag_num_objects
++;
2784 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2785 szone_free(szone
, q
); // avoid inlining free_tiny(szone, q, ...);
2790 static INLINE boolean_t
2791 tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
2793 // returns 1 on success
2796 unsigned next_index
;
2799 msize_t next_msize
, coalesced_msize
, leftover_msize
;
2802 index
= TINY_INDEX_FOR_PTR(ptr
);
2803 old_msize
= TINY_MSIZE_FOR_BYTES(old_size
);
2804 next_index
= index
+ old_msize
;
2806 if (next_index
>= NUM_TINY_BLOCKS
) {
2809 next_block
= (char *)ptr
+ old_size
;
2811 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2812 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2813 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2816 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2819 is_free
= tiny_meta_header_is_free(next_block
);
2821 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2822 return 0; // next_block is in use;
2824 next_msize
= get_tiny_free_size(next_block
);
2825 if (old_size
+ TINY_BYTES_FOR_MSIZE(next_msize
) < new_size
) {
2826 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2827 return 0; // even with next block, not enough
2830 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2833 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2834 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2835 coalesced_msize
= TINY_MSIZE_FOR_BYTES(new_size
- old_size
+ TINY_QUANTUM
- 1);
2836 leftover_msize
= next_msize
- coalesced_msize
;
2837 if (leftover_msize
) {
2838 /* there's some left, so put the remainder back */
2839 leftover
= (void *)((uintptr_t)next_block
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
));
2841 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover
, leftover_msize
);
2843 set_tiny_meta_header_in_use(ptr
, old_msize
+ coalesced_msize
);
2845 if (LOG(szone
,ptr
)) {
2846 malloc_printf("in tiny_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, old_msize
+ coalesced_msize
);
2849 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2851 // Update this region's bytes in use count
2852 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2853 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2854 node
->bytes_used
= bytes_used
;
2856 // Emptiness discriminant
2857 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2858 /* After this reallocation the region is still sparse, so it must have been even more so before
2859 the reallocation. That implies the region is already correctly marked. Do nothing. */
2861 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2862 recirculation candidates list. */
2863 node
->recirc_suitable
= FALSE
;
2866 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2867 CHECK(szone
, __PRETTY_FUNCTION__
);
2872 tiny_check_region(szone_t
*szone
, region_t region
)
2874 uintptr_t start
, ptr
, region_end
;
2875 boolean_t prev_free
= 0;
2878 free_list_t
*free_head
;
2879 void *follower
, *previous
, *next
;
2880 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
2881 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2884 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2886 /* establish region limits */
2887 start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
2889 if (region
== tiny_mag_ptr
->mag_last_region
) {
2890 ptr
+= tiny_mag_ptr
->mag_bytes_free_at_start
;
2893 * Check the leading block's integrity here also.
2895 if (tiny_mag_ptr
->mag_bytes_free_at_start
) {
2896 msize
= get_tiny_meta_header((void *)(ptr
- TINY_QUANTUM
), &is_free
);
2897 if (is_free
|| (msize
!= 1)) {
2898 malloc_printf("*** invariant broken for leader block %p - %d %d\n", ptr
- TINY_QUANTUM
, msize
, is_free
);
2902 region_end
= (uintptr_t)TINY_REGION_END(region
);
2905 * The last region may have a trailing chunk which has not been converted into inuse/freelist
2908 if (region
== tiny_mag_ptr
->mag_last_region
)
2909 region_end
-= tiny_mag_ptr
->mag_bytes_free_at_end
;
2912 * Scan blocks within the region.
2914 while (ptr
< region_end
) {
2916 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
2919 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2920 if (is_free
&& !msize
&& (ptr
== start
)) {
2925 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
2929 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
2936 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
2939 if (msize
> (NUM_TINY_SLOTS
- 1)) {
2940 malloc_printf("*** invariant broken for %p this tiny msize=%d - size is too large\n",
2944 /* move to next block */
2945 ptr
+= TINY_BYTES_FOR_MSIZE(msize
);
2948 * Free blocks must have been coalesced, we cannot have a free block following another
2952 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
2958 * Check the integrity of this block's entry in its freelist.
2960 free_head
= (free_list_t
*)ptr
;
2961 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
2962 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
2963 if (previous
&& !tiny_meta_header_is_free(previous
)) {
2964 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2968 if (next
&& !tiny_meta_header_is_free(next
)) {
2969 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
2974 * Check the free block's trailing size value.
2976 follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
2977 if (((uintptr_t)follower
!= region_end
) && (get_tiny_previous_free_msize(follower
) != msize
)) {
2978 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
2979 "(end marker incorrect) should be %d; in fact %d\n",
2980 ptr
, follower
, TINY_REGION_ADDRESS(region
), region_end
, msize
, get_tiny_previous_free_msize(follower
));
2983 /* move to next block */
2984 ptr
= (uintptr_t)follower
;
2988 * Ensure that we scanned the entire region
2990 if (ptr
!= region_end
) {
2991 malloc_printf("*** invariant broken for region end %p - %p\n", ptr
, region_end
);
2995 * Check the trailing block's integrity.
2997 if (region
== tiny_mag_ptr
->mag_last_region
) {
2998 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2999 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
3000 if (is_free
|| (msize
!= 1)) {
3001 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr
, msize
, is_free
);
3008 static kern_return_t
3009 tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
3010 memory_reader_t reader
, vm_range_recorder_t recorder
)
3015 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
3020 vm_range_t admin_range
;
3021 vm_range_t ptr_range
;
3022 unsigned char *mapped_region
;
3023 uint32_t *block_header
;
3025 unsigned block_index
;
3026 unsigned block_limit
;
3031 magazine_t
*tiny_mag_base
= NULL
;
3033 region_hash_generation_t
*trg_ptr
;
3034 err
= reader(task
, (vm_address_t
)szone
->tiny_region_generation
, sizeof(region_hash_generation_t
), (void **)&trg_ptr
);
3035 if (err
) return err
;
3037 num_regions
= trg_ptr
->num_regions_allocated
;
3038 err
= reader(task
, (vm_address_t
)trg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
3039 if (err
) return err
;
3041 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
3042 // Map in all active magazines. Do this outside the iteration over regions.
3043 err
= reader(task
, (vm_address_t
)(szone
->tiny_magazines
),
3044 szone
->num_tiny_magazines
*sizeof(magazine_t
),(void **)&tiny_mag_base
);
3045 if (err
) return err
;
3048 for (index
= 0; index
< num_regions
; ++index
) {
3049 region
= regions
[index
];
3050 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
3051 range
.address
= (vm_address_t
)TINY_REGION_ADDRESS(region
);
3052 range
.size
= (vm_size_t
)TINY_REGION_SIZE
;
3053 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
3054 admin_range
.address
= range
.address
+ TINY_METADATA_START
;
3055 admin_range
.size
= TINY_METADATA_SIZE
;
3056 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
3058 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
3059 ptr_range
.address
= range
.address
;
3060 ptr_range
.size
= NUM_TINY_BLOCKS
* TINY_QUANTUM
;
3061 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
3063 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
3064 void *mag_last_free
;
3065 vm_address_t mag_last_free_ptr
= 0;
3066 msize_t mag_last_free_msize
= 0;
3068 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
3072 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(mapped_region
);
3073 magazine_t
*tiny_mag_ptr
= tiny_mag_base
+ mag_index
;
3075 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3076 mag_last_free
= tiny_mag_ptr
->mag_last_free
;
3077 if (mag_last_free
) {
3078 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(TINY_QUANTUM
- 1);
3079 mag_last_free_msize
= (uintptr_t) mag_last_free
& (TINY_QUANTUM
- 1);
3082 for (mag_index
= 0; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3083 if ((void *)range
.address
== (tiny_mag_base
+ mag_index
)->mag_last_free_rgn
) {
3084 mag_last_free
= (tiny_mag_base
+ mag_index
)->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);
3093 block_header
= (uint32_t *)(mapped_region
+ TINY_METADATA_START
+ sizeof(region_trailer_t
));
3094 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
3096 block_limit
= NUM_TINY_BLOCKS
;
3097 if (region
== tiny_mag_ptr
->mag_last_region
) {
3098 block_index
+= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_start
);
3099 block_limit
-= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
3102 while (block_index
< block_limit
) {
3103 vm_size_t block_offset
= TINY_BYTES_FOR_MSIZE(block_index
);
3104 is_free
= !BITARRAY_BIT(in_use
, block_index
);
3106 mapped_ptr
= mapped_region
+ block_offset
;
3108 // mapped_region, the address at which 'range' in 'task' has been
3109 // mapped into our process, is not necessarily aligned to
3110 // TINY_BLOCKS_ALIGN.
3112 // Since the code in get_tiny_free_size() assumes the pointer came
3113 // from a properly aligned tiny region, and mapped_region is not
3114 // necessarily aligned, then do the size calculation directly.
3115 // If the next bit is set in the header bitmap, then the size is one
3116 // quantum. Otherwise, read the size field.
3117 if (!BITARRAY_BIT(block_header
, (block_index
+1)))
3118 msize
= TINY_FREE_SIZE(mapped_ptr
);
3122 } else if (range
.address
+ block_offset
!= mag_last_free_ptr
) {
3124 bit
= block_index
+ 1;
3125 while (! BITARRAY_BIT(block_header
, bit
)) {
3129 buffer
[count
].address
= range
.address
+ block_offset
;
3130 buffer
[count
].size
= TINY_BYTES_FOR_MSIZE(msize
);
3132 if (count
>= MAX_RECORDER_BUFFER
) {
3133 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
3137 // Block is not free but it matches mag_last_free_ptr so even
3138 // though it is not marked free in the bitmap, we treat it as if
3139 // it is and move on
3140 msize
= mag_last_free_msize
;
3144 return KERN_FAILURE
; // Somethings amiss. Avoid looping at this block_index.
3146 block_index
+= msize
;
3149 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
3159 tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
3163 grain_t slot
= msize
- 1;
3164 free_list_t
**free_list
= tiny_mag_ptr
->mag_free_list
;
3165 free_list_t
**the_slot
= free_list
+ slot
;
3167 free_list_t
**limit
;
3168 #if defined(__LP64__)
3173 msize_t leftover_msize
;
3174 free_list_t
*leftover_ptr
;
3176 // Assumes we've locked the region
3177 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
3179 // Look for an exact match by checking the freelist for this msize.
3183 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3185 next
->previous
= ptr
->previous
;
3187 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
3192 if (LOG(szone
, ptr
)) {
3193 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr
, this_msize
);
3196 goto return_tiny_alloc
;
3199 // Mask off the bits representing slots holding free blocks smaller than the
3200 // size we need. If there are no larger free blocks, try allocating from
3201 // the free space at the end of the tiny region.
3202 #if defined(__LP64__)
3203 bitmap
= ((uint64_t *)(tiny_mag_ptr
->mag_bitmap
))[0] & ~ ((1ULL << slot
) - 1);
3205 bitmap
= tiny_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
3208 goto try_tiny_malloc_from_end
;
3210 slot
= BITMAPV_CTZ(bitmap
);
3211 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
3214 if (free_list
< limit
) {
3217 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3220 next
->previous
= ptr
->previous
;
3222 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
3224 this_msize
= get_tiny_free_size(ptr
);
3225 goto add_leftover_and_proceed
;
3228 malloc_printf("in tiny_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
3232 // We are now looking at the last slot, which contains blocks equal to, or
3233 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
3234 // If the last freelist is not empty, and the head contains a block that is
3235 // larger than our request, then the remainder is put back on the free list.
3238 this_msize
= get_tiny_free_size(ptr
);
3239 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3240 if (this_msize
- msize
>= NUM_TINY_SLOTS
) {
3241 // the leftover will go back to the free list, so we optimize by
3242 // modifying the free list rather than a pop and push of the head
3243 leftover_msize
= this_msize
- msize
;
3244 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
3245 *limit
= leftover_ptr
;
3247 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
3249 leftover_ptr
->previous
= ptr
->previous
;
3250 leftover_ptr
->next
= ptr
->next
;
3251 set_tiny_meta_header_free(leftover_ptr
, leftover_msize
);
3253 if (LOG(szone
,ptr
)) {
3254 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n",
3255 ptr
, msize
, this_msize
);
3259 goto return_tiny_alloc
;
3262 next
->previous
= ptr
->previous
;
3265 goto add_leftover_and_proceed
;
3269 try_tiny_malloc_from_end
:
3270 // Let's see if we can use tiny_mag_ptr->mag_bytes_free_at_end
3271 if (tiny_mag_ptr
->mag_bytes_free_at_end
>= TINY_BYTES_FOR_MSIZE(msize
)) {
3272 ptr
= (free_list_t
*)((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) -
3273 tiny_mag_ptr
->mag_bytes_free_at_end
);
3274 tiny_mag_ptr
->mag_bytes_free_at_end
-= TINY_BYTES_FOR_MSIZE(msize
);
3275 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
3276 // let's add an in use block after ptr to serve as boundary
3277 set_tiny_meta_header_in_use_1((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
3281 if (LOG(szone
, ptr
)) {
3282 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr
, msize
);
3285 goto return_tiny_alloc
;
3288 // Try from start if nothing left at end
3289 if (tiny_mag_ptr
->mag_bytes_free_at_start
>= TINY_BYTES_FOR_MSIZE(msize
)) {
3290 ptr
= (free_list_t
*)(TINY_REGION_ADDRESS(tiny_mag_ptr
->mag_last_region
) +
3291 tiny_mag_ptr
->mag_bytes_free_at_start
- TINY_BYTES_FOR_MSIZE(msize
));
3292 tiny_mag_ptr
->mag_bytes_free_at_start
-= TINY_BYTES_FOR_MSIZE(msize
);
3293 if (tiny_mag_ptr
->mag_bytes_free_at_start
) {
3294 // let's add an in use block before ptr to serve as boundary
3295 set_tiny_meta_header_in_use_1((unsigned char *)ptr
- TINY_QUANTUM
);
3299 if (LOG(szone
, ptr
)) {
3300 malloc_printf("in tiny_malloc_from_free_list(), from start ptr=%p, msize=%d\n", ptr
, msize
);
3303 goto return_tiny_alloc
;
3308 add_leftover_and_proceed
:
3309 if (!this_msize
|| (this_msize
> msize
)) {
3310 leftover_msize
= this_msize
- msize
;
3311 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
3313 if (LOG(szone
,ptr
)) {
3314 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
3317 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover_ptr
, leftover_msize
);
3322 tiny_mag_ptr
->mag_num_objects
++;
3323 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(this_msize
);
3325 // Update this region's bytes in use count
3326 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3327 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(this_msize
);
3328 node
->bytes_used
= bytes_used
;
3330 // Emptiness discriminant
3331 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
3332 /* After this allocation the region is still sparse, so it must have been even more so before
3333 the allocation. That implies the region is already correctly marked. Do nothing. */
3335 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
3336 recirculation candidates list. */
3337 node
->recirc_suitable
= FALSE
;
3340 if (LOG(szone
,ptr
)) {
3341 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
3345 set_tiny_meta_header_in_use(ptr
, this_msize
);
3347 set_tiny_meta_header_in_use_1(ptr
);
3350 #undef DENSITY_THRESHOLD
3353 static INLINE
void *
3354 tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
3357 mag_index_t mag_index
= mag_get_thread_index(szone
);
3358 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3361 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
3362 szone_error(szone
, 1, "malloc called for magazine index -1", NULL
, NULL
);
3367 szone_error(szone
, 1, "invariant broken (!msize) in allocation (region)", NULL
, NULL
);
3372 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3375 ptr
= tiny_mag_ptr
->mag_last_free
;
3377 if ((((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) == msize
) {
3379 tiny_mag_ptr
->mag_last_free
= NULL
;
3380 tiny_mag_ptr
->mag_last_free_rgn
= NULL
;
3381 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3382 CHECK(szone
, __PRETTY_FUNCTION__
);
3383 ptr
= (void *)((uintptr_t)ptr
& ~ (TINY_QUANTUM
- 1));
3384 if (cleared_requested
) {
3385 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3388 if (LOG(szone
,ptr
)) {
3389 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr
, msize
);
3394 #endif /* TINY_CACHE */
3397 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3399 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3400 CHECK(szone
, __PRETTY_FUNCTION__
);
3401 if (cleared_requested
) {
3402 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3407 if (tiny_get_region_from_depot(szone
, tiny_mag_ptr
, mag_index
, msize
)) {
3408 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3410 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3411 CHECK(szone
, __PRETTY_FUNCTION__
);
3412 if (cleared_requested
) {
3413 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3419 // The magazine is exhausted. A new region (heap) must be allocated to satisfy this call to malloc().
3420 // The allocation, an mmap() system call, will be performed outside the magazine spin locks by the first
3421 // thread that suffers the exhaustion. That thread sets "alloc_underway" and enters a critical section.
3422 // Threads arriving here later are excluded from the critical section, yield the CPU, and then retry the
3423 // allocation. After some time the magazine is resupplied, the original thread leaves with its allocation,
3424 // and retry-ing threads succeed in the code just above.
3425 if (!tiny_mag_ptr
->alloc_underway
) {
3428 // time to create a new region (do this outside the magazine lock)
3429 tiny_mag_ptr
->alloc_underway
= TRUE
;
3431 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3432 fresh_region
= allocate_pages_securely(szone
, TINY_REGION_SIZE
, TINY_BLOCKS_ALIGN
, VM_MEMORY_MALLOC_TINY
);
3433 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3435 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
, fresh_region
, TINY_REGION_SIZE
); // DTrace USDT Probe
3437 if (!fresh_region
) { // out of memory!
3438 tiny_mag_ptr
->alloc_underway
= FALSE
;
3440 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3444 ptr
= tiny_malloc_from_region_no_lock(szone
, tiny_mag_ptr
, mag_index
, msize
, fresh_region
);
3446 // we don't clear because this freshly allocated space is pristine
3447 tiny_mag_ptr
->alloc_underway
= FALSE
;
3449 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3450 CHECK(szone
, __PRETTY_FUNCTION__
);
3453 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3455 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3461 static NOINLINE
void
3462 free_tiny_botch(szone_t
*szone
, free_list_t
*ptr
)
3464 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3465 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3466 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3467 szone_error(szone
, 1, "double free", ptr
, NULL
);
3471 free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
)
3475 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
3476 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3478 // ptr is known to be in tiny_region
3480 msize
= TINY_MSIZE_FOR_BYTES(known_size
+ TINY_QUANTUM
- 1);
3482 msize
= get_tiny_meta_header(ptr
, &is_free
);
3484 free_tiny_botch(szone
, ptr
);
3490 malloc_printf("*** free_tiny() block in use is too large: %p\n", ptr
);
3495 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3498 // Depot does not participate in TINY_CACHE since it can't be directly malloc()'d
3499 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3500 if (msize
< TINY_QUANTUM
) { // to see if the bits fit in the last 4 bits
3501 void *ptr2
= tiny_mag_ptr
->mag_last_free
; // Might be NULL
3502 region_t rgn2
= tiny_mag_ptr
->mag_last_free_rgn
;
3504 /* check that we don't already have this pointer in the cache */
3505 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (TINY_QUANTUM
- 1))) {
3506 free_tiny_botch(szone
, ptr
);
3510 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
3511 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
3513 tiny_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
3514 tiny_mag_ptr
->mag_last_free_rgn
= tiny_region
;
3517 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3518 CHECK(szone
, __PRETTY_FUNCTION__
);
3522 msize
= (uintptr_t)ptr2
& (TINY_QUANTUM
- 1);
3523 ptr
= (void *)(((uintptr_t)ptr2
) & ~(TINY_QUANTUM
- 1));
3527 #endif /* TINY_CACHE */
3529 // Now in the time it took to acquire the lock, the region may have migrated
3530 // from one magazine to another. I.e. trailer->mag_index is volatile.
3531 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
3532 // is stale. If so, keep on tryin' ...
3533 region_trailer_t
*trailer
= REGION_TRAILER_FOR_TINY_REGION(tiny_region
);
3534 mag_index_t refreshed_index
;
3536 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
3538 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3540 mag_index
= refreshed_index
;
3541 tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3542 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3545 if (tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
))
3546 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3548 CHECK(szone
, __PRETTY_FUNCTION__
);
3552 print_tiny_free_list(szone_t
*szone
)
3555 _SIMPLE_STRING b
= _simple_salloc();
3556 mag_index_t mag_index
;
3559 _simple_sappend(b
, "tiny free sizes:\n");
3560 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3562 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
3563 while (slot
< NUM_TINY_SLOTS
) {
3564 ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3566 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== NUM_TINY_SLOTS
-1) ? ">=" : "",
3567 (slot
+1)*TINY_QUANTUM
, free_list_count(szone
, ptr
));
3571 _simple_sappend(b
,"\n");
3573 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3579 print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
)
3581 unsigned counts
[1024];
3582 unsigned in_use
= 0;
3583 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
3584 uintptr_t current
= start
+ bytes_at_end
;
3585 uintptr_t limit
= (uintptr_t)TINY_REGION_END(region
) - bytes_at_end
;
3590 uintptr_t pgTot
= 0;
3592 if (region
== HASHRING_REGION_DEALLOCATED
) {
3593 if ((b
= _simple_salloc()) != NULL
) {
3594 _simple_sprintf(b
, "Tiny region [unknown address] was returned to the OS\n");
3595 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3601 memset(counts
, 0, sizeof(counts
));
3602 while (current
< limit
) {
3603 msize
= get_tiny_meta_header((void *)current
, &is_free
);
3604 if (is_free
& !msize
&& (current
== start
)) {
3605 // first block is all free
3606 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3607 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
3610 pgTot
+= (pgHi
- pgLo
);
3615 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
3620 if (msize
> NUM_TINY_SLOTS
)
3621 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current
, msize
);
3626 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3627 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3630 pgTot
+= (pgHi
- pgLo
);
3633 current
+= TINY_BYTES_FOR_MSIZE(msize
);
3635 if ((b
= _simple_salloc()) != NULL
) {
3636 _simple_sprintf(b
, "Tiny region [%p-%p, %y] \t", (void *)start
, TINY_REGION_END(region
), (int)TINY_REGION_SIZE
);
3637 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_TINY_REGION(region
));
3638 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_TINY_REGION(region
));
3639 if (bytes_at_end
|| bytes_at_start
)
3640 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
+ bytes_at_start
);
3641 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_TINY_REGION(region
)) {
3642 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
3644 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
3646 if (verbose
&& in_use
) {
3647 _simple_sappend(b
, "\n\tSizes in use: ");
3648 for (ci
= 0; ci
< 1024; ci
++)
3650 _simple_sprintf(b
, "%d[%d] ", TINY_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
3652 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3658 tiny_free_list_check(szone_t
*szone
, grain_t slot
)
3660 mag_index_t mag_index
;
3662 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3663 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3664 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3667 free_list_t
*ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3669 free_list_t
*previous
= NULL
;
3672 is_free
= tiny_meta_header_is_free(ptr
);
3674 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3675 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3678 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
3679 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3680 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3683 if (!tiny_region_for_ptr_no_lock(szone
, ptr
)) {
3684 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3685 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3688 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
3689 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3690 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3694 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3698 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3703 /********************* SMALL FREE LIST UTILITIES ************************/
3706 * Mark a block as free. Only the first quantum of a block is marked thusly,
3707 * the remainder are marked "middle".
3710 small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
)
3712 meta_headers
[index
] = msize
| SMALL_IS_FREE
;
3716 * Mark a block as in use. Only the first quantum of a block is marked thusly,
3717 * the remainder are marked "middle".
3720 small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
)
3722 meta_headers
[index
] = msize
;
3726 * Mark a quantum as being the second or later in a block.
3729 small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
)
3731 meta_headers
[index
] = 0;
3735 * Adds an item to the proper free list, and also marks the meta-header of the
3737 * Assumes szone has been locked
3740 small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3742 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3743 free_list_t
*free_ptr
= ptr
;
3744 free_list_t
*free_head
= small_mag_ptr
->mag_free_list
[slot
];
3748 if (LOG(szone
,ptr
)) {
3749 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3751 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
3752 szone_error(szone
, 1, "small_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
3755 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), msize
);
3759 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
3760 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
3761 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
3763 if (!SMALL_PTR_IS_FREE(free_head
)) {
3764 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
3765 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
3768 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
3770 BITMAPN_SET(small_mag_ptr
->mag_bitmap
, slot
);
3772 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
3773 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
3775 small_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
3777 // Store msize at the end of the block denoted by "ptr" (i.e. at a negative offset from "follower")
3778 follower
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
3779 SMALL_PREVIOUS_MSIZE(follower
) = msize
;
3783 * Removes the item pointed to by ptr in the proper free list.
3784 * Assumes szone has been locked
3787 small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3789 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3790 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
3792 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
3793 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
3796 if (LOG(szone
,ptr
)) {
3797 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3802 // The block to remove is the head of the free list
3804 if (small_mag_ptr
->mag_free_list
[slot
] != ptr
) {
3805 szone_error(szone
, 1, "small_free_list_remove_ptr: Internal invariant broken (small_mag_ptr->mag_free_list[slot])", ptr
,
3806 "ptr=%p slot=%d msize=%d small_mag_ptr->mag_free_list[slot]=%p\n",
3807 ptr
, slot
, msize
, (void *)small_mag_ptr
->mag_free_list
[slot
]);
3811 small_mag_ptr
->mag_free_list
[slot
] = next
;
3812 if (!next
) BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
3814 // We know free_ptr is already checksummed, so we don't need to do it
3816 previous
->next
= free_ptr
->next
;
3819 // We know free_ptr is already checksummed, so we don't need to do it
3821 next
->previous
= free_ptr
->previous
;
3826 * small_region_for_ptr_no_lock - Returns the small region containing the pointer,
3827 * or NULL if not found.
3829 static INLINE region_t
3830 small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
3832 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3833 szone
->small_region_generation
->num_regions_allocated
,
3834 szone
->small_region_generation
->num_regions_allocated_shift
,
3835 SMALL_REGION_FOR_PTR(ptr
));
3840 small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
) {
3841 void *last_block
, *previous_block
;
3842 msize_t last_msize
, previous_msize
, last_index
;
3844 // It is possible that the block prior to the last block in the region has
3845 // been free'd, but was not coalesced with the free bytes at the end of the
3846 // block, since we treat the bytes at the end of the region as "in use" in
3847 // the meta headers. Attempt to coalesce the last block with the previous
3848 // block, so we don't violate the "no consecutive free blocks" invariant.
3850 // FIXME: If we could calculate the previous small free size in the same
3851 // manner as tiny_previous_preceding_free, it would eliminate the
3852 // index & previous msize checks, which are a guard against reading
3853 // bogus data out of in-use or written-on-freed memory.
3855 // FIXME: Need to investigate how much work would be required to increase
3856 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
3857 // than performing this workaround.
3859 if (small_mag_ptr
->mag_bytes_free_at_end
) {
3860 last_block
= SMALL_REGION_END(small_mag_ptr
->mag_last_region
) - small_mag_ptr
->mag_bytes_free_at_end
;
3861 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
3863 last_index
= SMALL_META_INDEX_FOR_PTR(last_block
);
3864 previous_msize
= SMALL_PREVIOUS_MSIZE(last_block
);
3866 if (last_index
&& (previous_msize
<= last_index
)) {
3867 previous_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3868 if (*SMALL_METADATA_FOR_PTR(previous_block
) == (previous_msize
| SMALL_IS_FREE
)) {
3869 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(last_block
);
3871 small_meta_header_set_middle(meta_headers
, last_index
);
3872 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous_block
, previous_msize
);
3873 last_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3874 last_msize
+= previous_msize
;
3878 // splice last_block into the free list
3879 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3880 small_mag_ptr
->mag_bytes_free_at_end
= 0;
3884 if (small_mag_ptr
->mag_bytes_free_at_start
) {
3885 last_block
= SMALL_REGION_ADDRESS(small_mag_ptr
->mag_last_region
);
3886 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_start
);
3888 void *next_block
= (void *) ((uintptr_t)last_block
+ small_mag_ptr
->mag_bytes_free_at_start
);
3889 if (SMALL_PTR_IS_FREE(next_block
)) {
3890 msize_t next_msize
= SMALL_PTR_SIZE(next_block
);
3892 small_meta_header_set_middle(SMALL_META_HEADER_FOR_PTR(next_block
), SMALL_META_INDEX_FOR_PTR(next_block
));
3893 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
3894 last_msize
+= next_msize
;
3897 // splice last_block into the free list
3898 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3899 small_mag_ptr
->mag_bytes_free_at_start
= 0;
3903 // TODO: Will we ever need to coalesce the blocks at the beginning and end when we finalize?
3905 small_mag_ptr
->mag_last_region
= NULL
;
3909 small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3910 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3911 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3912 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3913 uintptr_t current
= start
;
3914 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3915 int total_alloc
= 0;
3917 while (current
< limit
) {
3918 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3919 msize_t msize_and_free
= meta_headers
[index
];
3920 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3921 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3925 malloc_printf("*** small_free_detach_region error with %p: msize=%d is_free =%d\n",
3926 (void *)current
, msize
, is_free
);
3931 small_free_list_remove_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3935 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3941 small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3942 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3943 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3944 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3945 uintptr_t current
= start
;
3946 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3947 size_t total_alloc
= 0;
3949 while (current
< limit
) {
3950 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3951 msize_t msize_and_free
= meta_headers
[index
];
3952 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3953 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3957 malloc_printf("*** small_free_reattach_region error with %p: msize=%d is_free =%d\n",
3958 (void *)current
, msize
, is_free
);
3963 small_free_list_add_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3965 total_alloc
+= SMALL_BYTES_FOR_MSIZE(msize
);
3967 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3973 uint16_t pnum
, size
;
3976 static void NOINLINE
/* want private stack frame for automatic array */
3977 small_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
3978 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3979 uintptr_t current
= start
;
3980 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3981 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(start
);
3982 small_pg_pair_t advisory
[((SMALL_REGION_PAYLOAD_BYTES
+ vm_page_size
- 1) >> vm_page_shift
) >> 1]; // 4096bytes stack allocated
3985 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
3987 while (current
< limit
) {
3988 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3989 msize_t msize_and_free
= meta_headers
[index
];
3990 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3991 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3993 if (is_free
&& !msize
&& (current
== start
)) {
3995 // first block is all free
3996 malloc_printf("*** small_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
3997 (void *)current
, msize
, is_free
);
3999 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
4000 uintptr_t pgHi
= trunc_page(start
+ SMALL_REGION_SIZE
- sizeof(msize_t
));
4003 advisory
[advisories
].pnum
= (pgLo
- start
) >> vm_page_shift
;
4004 advisory
[advisories
].size
= (pgHi
- pgLo
) >> vm_page_shift
;
4011 malloc_printf("*** small_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
4012 (void *)current
, msize
, is_free
);
4017 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
4018 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
4021 advisory
[advisories
].pnum
= (pgLo
- start
) >> vm_page_shift
;
4022 advisory
[advisories
].size
= (pgHi
- pgLo
) >> vm_page_shift
;
4026 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
4029 if (advisories
> 0) {
4032 OSAtomicIncrement32Barrier(&(REGION_TRAILER_FOR_SMALL_REGION(r
)->pinned_to_depot
));
4033 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
);
4034 for (i
= 0; i
< advisories
; ++i
) {
4035 uintptr_t addr
= (advisory
[i
].pnum
<< vm_page_shift
) + start
;
4036 size_t size
= advisory
[i
].size
<< vm_page_shift
;
4038 #if TARGET_OS_EMBEDDED
4039 madvise_free_range(szone
, r
, addr
, addr
+ size
, NULL
);
4041 madvise_free_range(szone
, r
, addr
, addr
+ size
);
4044 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
4045 OSAtomicDecrement32Barrier(&(REGION_TRAILER_FOR_SMALL_REGION(r
)->pinned_to_depot
));
4050 small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
4052 if (0 < node
->bytes_used
||
4053 0 < node
->pinned_to_depot
||
4054 depot_ptr
->recirculation_entries
< (szone
->num_small_magazines
* 2)) {
4058 // disconnect first node from Depot
4059 recirc_list_extract(szone
, depot_ptr
, node
);
4061 // Iterate the region pulling its free entries off the (locked) Depot's free list
4062 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
4063 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
4065 if (0 == objects_in_use
) {
4066 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
4067 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
4068 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
4069 szone
->small_region_generation
->num_regions_allocated
,
4070 szone
->small_region_generation
->num_regions_allocated_shift
, sparse_region
);
4071 if (NULL
== pSlot
) {
4072 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock hash lookup failed:", NULL
, "%p\n", sparse_region
);
4075 *pSlot
= HASHRING_REGION_DEALLOCATED
;
4076 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
4077 __sync_fetch_and_add( &(szone
->num_small_regions_dealloc
), 1); // Atomically increment num_small_regions_dealloc
4079 // Caller will transfer ownership of the region back to the OS with no locks held
4080 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
, SMALL_REGION_SIZE
); // DTrace USDT Probe
4081 return sparse_region
;
4084 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
4090 small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
4092 // The entire magazine crossed the "emptiness threshold". Transfer a region
4093 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
4094 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
4095 region_trailer_t
*node
= small_mag_ptr
->firstNode
;
4097 while (node
&& !node
->recirc_suitable
) {
4103 malloc_printf("*** small_free_do_recirc_to_depot end of list\n");
4105 return TRUE
; // Caller must SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4108 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
4110 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at start
4111 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
)) {
4112 small_finalize_region(szone
, small_mag_ptr
);
4115 // disconnect "suitable" node from magazine
4116 recirc_list_extract(szone
, small_mag_ptr
, node
);
4118 // Iterate the region pulling its free entries off its (locked) magazine's free list
4119 int objects_in_use
= small_free_detach_region(szone
, small_mag_ptr
, sparse_region
);
4120 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
4122 // hand over the region to the (locked) Depot
4123 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
4124 // this will cause small_free_list_add_ptr called by small_free_reattach_region to use
4125 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
4126 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
4127 node
->pinned_to_depot
= 0;
4129 // Iterate the region putting its free entries on Depot's free list
4130 size_t bytes_inplay
= small_free_reattach_region(szone
, depot_ptr
, sparse_region
);
4132 small_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
4133 small_mag_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
4134 small_mag_ptr
->mag_num_objects
-= objects_in_use
;
4136 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
); // Unlock the originating magazine
4138 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
4139 depot_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4140 depot_ptr
->mag_num_objects
+= objects_in_use
;
4142 // connect to Depot as last node
4143 recirc_list_splice_last(szone
, depot_ptr
, node
);
4145 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (void *)sparse_region
, SMALL_REGION_SIZE
,
4146 (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
4148 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
4149 small_free_scan_madvise_free(szone
, depot_ptr
, sparse_region
);
4151 // If the region is entirely empty vm_deallocate() it outside the depot lock
4152 region_t r_dealloc
= small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
4153 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4155 deallocate_pages(szone
, r_dealloc
, SMALL_REGION_SIZE
, 0);
4156 return FALSE
; // Caller need not unlock the originating magazine
4160 small_find_msize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4163 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4164 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4165 free_list_t
**the_slot
= free_list
+ slot
;
4166 free_list_t
**limit
;
4169 // Assumes we've locked the magazine
4170 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4172 // Look for an exact match by checking the freelist for this msize.
4175 return SMALL_REGION_FOR_PTR(ptr
);
4177 // Mask off the bits representing slots holding free blocks smaller than
4178 // the size we need.
4179 if (szone
->is_largemem
) {
4180 // BITMAPN_CTZ implementation
4181 unsigned idx
= slot
>> 5;
4183 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4184 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4185 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4190 // Check for fallthrough: No bits set in bitmap
4191 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4194 // Start looking at the first set bit, plus 32 bits for every word of
4195 // zeroes or entries that were too small.
4196 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4198 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4202 slot
= BITMAP32_CTZ((&bitmap
));
4204 limit
= free_list
+ szone
->num_small_slots
- 1;
4207 if (free_list
< limit
) {
4210 return SMALL_REGION_FOR_PTR(ptr
);
4212 /* Shouldn't happen. Fall through to look at last slot. */
4214 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4219 // We are now looking at the last slot, which contains blocks equal to, or
4220 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4223 return SMALL_REGION_FOR_PTR(ptr
);
4229 small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4231 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
4233 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
4234 if (szone
->num_small_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
4238 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
4239 szone_error(szone
, 1, "small_get_region_from_depot called for magazine index -1", NULL
, NULL
);
4244 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
4246 // Appropriate a Depot'd region that can satisfy requested msize.
4247 region_trailer_t
*node
;
4248 region_t sparse_region
;
4251 sparse_region
= small_find_msize_region(szone
, depot_ptr
, DEPOT_MAGAZINE_INDEX
, msize
);
4252 if (NULL
== sparse_region
) { // Depot empty?
4253 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4257 node
= REGION_TRAILER_FOR_SMALL_REGION(sparse_region
);
4258 if (0 >= node
->pinned_to_depot
)
4261 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4263 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
4266 // disconnect node from Depot
4267 recirc_list_extract(szone
, depot_ptr
, node
);
4269 // Iterate the region pulling its free entries off the (locked) Depot's free list
4270 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
4272 // Transfer ownership of the region
4273 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = mag_index
;
4274 node
->pinned_to_depot
= 0;
4276 // Iterate the region putting its free entries on its new (locked) magazine's free list
4277 size_t bytes_inplay
= small_free_reattach_region(szone
, small_mag_ptr
, sparse_region
);
4279 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
4280 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
4281 depot_ptr
->mag_num_objects
-= objects_in_use
;
4283 small_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
4284 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4285 small_mag_ptr
->mag_num_objects
+= objects_in_use
;
4287 // connect to magazine as first node
4288 recirc_list_splice_first(szone
, small_mag_ptr
, node
);
4290 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
4292 // madvise() outside the Depot lock
4293 #if TARGET_OS_EMBEDDED
4294 if (node
->failedREUSE
) {
4296 if (node
->failedREUSE
||
4297 -1 == madvise((void *)sparse_region
, SMALL_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
4299 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
4301 szone_error(szone
, 0, "small_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed",
4302 sparse_region
, "length=%d\n", SMALL_REGION_PAYLOAD_BYTES
);
4304 node
->failedREUSE
= TRUE
;
4307 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (void *)sparse_region
, SMALL_REGION_SIZE
,
4308 (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
4313 #define K 1.5 // headroom measured in number of 8Mb regions
4314 #define DENSITY_THRESHOLD(a) \
4315 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
4317 static INLINE boolean_t
4318 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
)
4320 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4321 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4322 void *original_ptr
= ptr
;
4323 size_t original_size
= SMALL_BYTES_FOR_MSIZE(msize
);
4324 unsigned char *next_block
= ((unsigned char *)ptr
+ original_size
);
4325 msize_t next_index
= index
+ msize
;
4326 msize_t previous_msize
, next_msize
;
4330 if (LOG(szone
,ptr
)) {
4331 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
4334 szone_error(szone
, 1, "trying to free small block that is too small", ptr
,
4335 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
4339 // We try to coalesce this block with the preceeding one
4340 if (index
&& (SMALL_PREVIOUS_MSIZE(ptr
) <= index
)) {
4341 previous_msize
= SMALL_PREVIOUS_MSIZE(ptr
);
4342 if (meta_headers
[index
- previous_msize
] == (previous_msize
| SMALL_IS_FREE
)) {
4343 previous
= (void *)((uintptr_t)ptr
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
4344 // previous is really to be coalesced
4346 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
4347 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
4350 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous
, previous_msize
);
4351 small_meta_header_set_middle(meta_headers
, index
);
4353 msize
+= previous_msize
;
4354 index
-= previous_msize
;
4357 // We try to coalesce with the next block
4358 if ((next_block
< SMALL_REGION_END(region
)) && (meta_headers
[next_index
] & SMALL_IS_FREE
)) {
4359 // next block is free, we coalesce
4360 next_msize
= meta_headers
[next_index
] & ~ SMALL_IS_FREE
;
4363 malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n",
4364 ptr
, msize
, next_block
, next_msize
);
4366 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4367 small_meta_header_set_middle(meta_headers
, next_index
);
4368 msize
+= next_msize
;
4370 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) {
4372 szone_error(szone
, 1, "incorrect size information - block header was damaged", ptr
, NULL
);
4374 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
4377 small_free_list_add_ptr(szone
, small_mag_ptr
, ptr
, msize
);
4378 small_mag_ptr
->mag_num_objects
--;
4379 // we use original_size and not msize to avoid double counting the coalesced blocks
4380 small_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
4382 // Update this region's bytes in use count
4383 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
4384 size_t bytes_used
= node
->bytes_used
- original_size
;
4385 node
->bytes_used
= bytes_used
;
4387 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
4388 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
4389 if (szone
->num_small_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
4391 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4392 // Emptiness discriminant
4393 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4394 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
4395 recirculation candidates list. */
4396 node
->recirc_suitable
= TRUE
;
4398 /* After this free, we've found the region is still dense, so it must have been even more so before
4399 the free. That implies the region is already correctly marked. Do nothing. */
4402 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
4403 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
4404 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
4406 size_t a
= small_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
4407 size_t u
= small_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
4409 if (a
- u
> ((3 * SMALL_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
)) {
4410 return small_free_do_recirc_to_depot(szone
, small_mag_ptr
, mag_index
);
4415 // Freed to Depot. N.B. Lock on small_magazines[DEPOT_MAGAZINE_INDEX] is already held
4416 // Calcuate the first page in the coalesced block that would be safe to mark MADV_FREE
4417 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
4418 uintptr_t round_safe
= round_page(safe_ptr
);
4420 // Calcuate the last page in the coalesced block that would be safe to mark MADV_FREE
4421 uintptr_t safe_extent
= (uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
4422 uintptr_t trunc_extent
= trunc_page(safe_extent
);
4424 // The newly freed block may complete a span of bytes that cover one or more pages. Mark the span with MADV_FREE.
4425 if (round_safe
< trunc_extent
) { // Safe area covers a page (perhaps many)
4426 uintptr_t lo
= trunc_page((uintptr_t)original_ptr
);
4427 uintptr_t hi
= round_page((uintptr_t)original_ptr
+ original_size
);
4429 OSAtomicIncrement32Barrier(&(node
->pinned_to_depot
));
4430 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4431 #if TARGET_OS_EMBEDDED
4432 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
), &szone
->last_small_advise
);
4434 madvise_free_range(szone
, region
, MAX(round_safe
, lo
), MIN(trunc_extent
, hi
));
4436 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4437 OSAtomicDecrement32Barrier(&(node
->pinned_to_depot
));
4440 #if !TARGET_OS_EMBEDDED
4441 if (0 < bytes_used
|| 0 < node
->pinned_to_depot
) {
4442 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
4443 so as to avoid thrashing between the Depot's free list and a magazines's free list
4444 with detach_region/reattach_region */
4446 /* Depot'd region is just now empty. Consider return to OS. */
4447 region_t r_dealloc
= small_free_try_depot_unmap_no_lock(szone
, small_mag_ptr
, node
);
4448 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4450 deallocate_pages(szone
, r_dealloc
, SMALL_REGION_SIZE
, 0);
4451 return FALSE
; // Caller need not unlock
4456 return TRUE
; // Caller must do SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr)
4459 // Allocates from the last region or a freshly allocated region
4461 small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
4462 msize_t msize
, void *aligned_address
)
4466 // Before anything we transform the mag_bytes_free_at_end or mag_bytes_free_at_start - if any - to a regular free block
4467 /* FIXME: last_block needs to be coalesced with previous entry if free, <rdar://5462322> */
4468 if (small_mag_ptr
->mag_bytes_free_at_end
|| small_mag_ptr
->mag_bytes_free_at_start
)
4469 small_finalize_region(szone
, small_mag_ptr
);
4471 // Here find the only place in smallville that (infrequently) takes the small_regions_lock.
4472 // Only one thread at a time should be permitted to assess the density of the hash
4473 // ring and adjust if needed.
4474 // Only one thread at a time should be permitted to insert its new region on
4476 // It is safe for all other threads to read the hash ring (hashed_regions) and
4477 // the associated sizes (num_regions_allocated and num_small_regions).
4479 LOCK(szone
->small_regions_lock
);
4480 // Check to see if the hash ring of small regions needs to grow. Try to
4481 // avoid the hash ring becoming too dense.
4482 if (szone
->small_region_generation
->num_regions_allocated
< (2 * szone
->num_small_regions
)) {
4483 region_t
*new_regions
;
4485 size_t new_shift
= szone
->small_region_generation
->num_regions_allocated_shift
; // In/Out parameter
4486 new_regions
= hash_regions_grow_no_lock(szone
, szone
->small_region_generation
->hashed_regions
,
4487 szone
->small_region_generation
->num_regions_allocated
,
4490 // Do not deallocate the current hashed_regions allocation since someone
4491 // may be iterating it. Instead, just leak it.
4493 // Prepare to advance to the "next generation" of the hash ring.
4494 szone
->small_region_generation
->nextgen
->hashed_regions
= new_regions
;
4495 szone
->small_region_generation
->nextgen
->num_regions_allocated
= new_size
;
4496 szone
->small_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
4498 // Throw the switch to atomically advance to the next generation.
4499 szone
->small_region_generation
= szone
->small_region_generation
->nextgen
;
4500 // Ensure everyone sees the advance.
4503 // Tag the region at "aligned_address" as belonging to us,
4504 // and so put it under the protection of the magazine lock we are holding.
4505 // Do this before advertising "aligned_address" on the hash ring(!)
4506 MAGAZINE_INDEX_FOR_SMALL_REGION(aligned_address
) = mag_index
;
4508 // Insert the new region into the hash ring, and update malloc statistics
4509 hash_region_insert_no_lock(szone
->small_region_generation
->hashed_regions
,
4510 szone
->small_region_generation
->num_regions_allocated
,
4511 szone
->small_region_generation
->num_regions_allocated_shift
,
4514 szone
->num_small_regions
++;
4516 UNLOCK(szone
->small_regions_lock
);
4518 small_mag_ptr
->mag_last_region
= aligned_address
;
4519 BYTES_USED_FOR_SMALL_REGION(aligned_address
) = SMALL_BYTES_FOR_MSIZE(msize
);
4521 int offset_msize
= malloc_entropy
[1] & SMALL_ENTROPY_MASK
;
4523 if (getenv("MallocASLRForce")) offset_msize
= strtol(getenv("MallocASLRForce"), NULL
, 0) & SMALL_ENTROPY_MASK
;
4524 if (getenv("MallocASLRPrint")) malloc_printf("Region: %p offset: %d\n", aligned_address
, offset_msize
);
4527 int offset_msize
= 0;
4529 ptr
= (void *)((uintptr_t) aligned_address
+ SMALL_BYTES_FOR_MSIZE(offset_msize
));
4530 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), offset_msize
, msize
);
4531 small_mag_ptr
->mag_num_objects
++;
4532 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(msize
);
4533 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4535 // add a big free block at the end
4536 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), offset_msize
+ msize
, NUM_SMALL_BLOCKS
- msize
- offset_msize
);
4537 small_mag_ptr
->mag_bytes_free_at_end
= SMALL_BYTES_FOR_MSIZE(NUM_SMALL_BLOCKS
- msize
- offset_msize
);
4540 // add a big free block at the start
4541 small_mag_ptr
->mag_bytes_free_at_start
= SMALL_BYTES_FOR_MSIZE(offset_msize
);
4543 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), 0, offset_msize
);
4546 small_mag_ptr
->mag_bytes_free_at_start
= 0;
4549 // connect to magazine as last node
4550 recirc_list_splice_last(szone
, small_mag_ptr
, REGION_TRAILER_FOR_SMALL_REGION(aligned_address
));
4555 static INLINE
void *
4556 small_try_shrink_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_good_size
)
4558 msize_t new_msize
= SMALL_MSIZE_FOR_BYTES(new_good_size
);
4559 msize_t mshrinkage
= SMALL_MSIZE_FOR_BYTES(old_size
) - new_msize
;
4562 void *q
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
));
4563 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4564 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4565 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4567 // Mark q as block header and in-use, thus creating two blocks.
4568 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), new_msize
);
4569 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mshrinkage
);
4570 small_mag_ptr
->mag_num_objects
++;
4572 SZONE_MAGAZINE_PTR_UNLOCK(szone
,small_mag_ptr
);
4573 szone_free(szone
, q
); // avoid inlining free_small(szone, q, ...);
4579 static INLINE boolean_t
4580 small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
4582 // returns 1 on success
4583 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4585 msize_t old_msize
, new_msize
;
4586 unsigned next_index
;
4588 msize_t next_msize_and_free
;
4590 msize_t next_msize
, leftover_msize
;
4593 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4594 old_msize
= SMALL_MSIZE_FOR_BYTES(old_size
);
4595 new_msize
= SMALL_MSIZE_FOR_BYTES(new_size
+ SMALL_QUANTUM
- 1);
4596 next_index
= index
+ old_msize
;
4598 if (next_index
>= NUM_SMALL_BLOCKS
) {
4601 next_block
= (char *)ptr
+ old_size
;
4604 if ((uintptr_t)next_block
& (SMALL_QUANTUM
- 1)) {
4605 szone_error(szone
, 1, "internal invariant broken in realloc(next_block)", next_block
, NULL
);
4607 if (meta_headers
[index
] != old_msize
)
4608 malloc_printf("*** small_try_realloc_in_place incorrect old %d %d\n",
4609 meta_headers
[index
], old_msize
);
4612 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4613 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4614 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4617 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
4620 next_msize_and_free
= meta_headers
[next_index
];
4621 is_free
= next_msize_and_free
& SMALL_IS_FREE
;
4623 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4624 return 0; // next_block is in use;
4626 next_msize
= next_msize_and_free
& ~ SMALL_IS_FREE
;
4627 if (old_msize
+ next_msize
< new_msize
) {
4628 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4629 return 0; // even with next block, not enough
4632 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
4635 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4636 small_meta_header_set_middle(meta_headers
, next_index
);
4637 leftover_msize
= old_msize
+ next_msize
- new_msize
;
4638 if (leftover_msize
) {
4639 /* there's some left, so put the remainder back */
4640 leftover
= (unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
);
4642 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover
, leftover_msize
);
4645 if (SMALL_BYTES_FOR_MSIZE(new_msize
) > szone
->large_threshold
) {
4646 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize
);
4649 small_meta_header_set_in_use(meta_headers
, index
, new_msize
);
4651 if (LOG(szone
,ptr
)) {
4652 malloc_printf("in small_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, *SMALL_METADATA_FOR_PTR(ptr
));
4655 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4657 // Update this region's bytes in use count
4658 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4659 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4660 node
->bytes_used
= bytes_used
;
4662 // Emptiness discriminant
4663 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4664 /* After this reallocation the region is still sparse, so it must have been even more so before
4665 the reallocation. That implies the region is already correctly marked. Do nothing. */
4667 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
4668 recirculation candidates list. */
4669 node
->recirc_suitable
= FALSE
;
4672 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4673 CHECK(szone
, __PRETTY_FUNCTION__
);
4678 small_check_region(szone_t
*szone
, region_t region
)
4680 unsigned char *ptr
= SMALL_REGION_ADDRESS(region
);
4681 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4682 unsigned char *region_end
= SMALL_REGION_END(region
);
4683 msize_t prev_free
= 0;
4685 msize_t msize_and_free
;
4687 free_list_t
*free_head
;
4688 void *previous
, *next
;
4690 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4691 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4694 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4696 if (region
== small_mag_ptr
->mag_last_region
) {
4697 ptr
+= small_mag_ptr
->mag_bytes_free_at_start
;
4698 region_end
-= small_mag_ptr
->mag_bytes_free_at_end
;
4701 while (ptr
< region_end
) {
4702 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4703 msize_and_free
= meta_headers
[index
];
4704 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4706 msize
= msize_and_free
;
4708 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
4709 ptr
, szone
->num_small_regions
, region_end
);
4712 if (SMALL_BYTES_FOR_MSIZE(msize
) > szone
->large_threshold
) {
4713 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
4714 ptr
, msize_and_free
);
4717 ptr
+= SMALL_BYTES_FOR_MSIZE(msize
);
4721 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4722 free_head
= (free_list_t
*)ptr
;
4723 follower
= (msize_t
*)FOLLOWING_SMALL_PTR(ptr
, msize
);
4725 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr
, msize
);
4729 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr
);
4732 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
4733 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
4734 if (previous
&& !SMALL_PTR_IS_FREE(previous
)) {
4735 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
4736 ptr
, free_head
->previous
);
4739 if (next
&& !SMALL_PTR_IS_FREE(next
)) {
4740 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr
);
4743 if (SMALL_PREVIOUS_MSIZE(follower
) != msize
) {
4744 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
4745 "(end marker incorrect) should be %d; in fact %d\n",
4746 ptr
, follower
, SMALL_REGION_ADDRESS(region
), region_end
, msize
, SMALL_PREVIOUS_MSIZE(follower
));
4749 ptr
= (unsigned char *)follower
;
4750 prev_free
= SMALL_IS_FREE
;
4756 static kern_return_t
4757 small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
4758 memory_reader_t reader
, vm_range_recorder_t recorder
)
4763 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4768 vm_range_t admin_range
;
4769 vm_range_t ptr_range
;
4770 unsigned char *mapped_region
;
4771 msize_t
*block_header
;
4772 unsigned block_index
;
4773 unsigned block_limit
;
4774 msize_t msize_and_free
;
4776 magazine_t
*small_mag_base
= NULL
;
4778 region_hash_generation_t
*srg_ptr
;
4779 err
= reader(task
, (vm_address_t
)szone
->small_region_generation
, sizeof(region_hash_generation_t
), (void **)&srg_ptr
);
4780 if (err
) return err
;
4782 num_regions
= srg_ptr
->num_regions_allocated
;
4783 err
= reader(task
, (vm_address_t
)srg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
4784 if (err
) return err
;
4786 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4787 // Map in all active magazines. Do this outside the iteration over regions.
4788 err
= reader(task
, (vm_address_t
)(szone
->small_magazines
),
4789 szone
->num_small_magazines
*sizeof(magazine_t
),(void **)&small_mag_base
);
4790 if (err
) return err
;
4793 for (index
= 0; index
< num_regions
; ++index
) {
4794 region
= regions
[index
];
4795 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
4796 range
.address
= (vm_address_t
)SMALL_REGION_ADDRESS(region
);
4797 range
.size
= SMALL_REGION_SIZE
;
4798 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4799 admin_range
.address
= range
.address
+ SMALL_METADATA_START
;
4800 admin_range
.size
= SMALL_METADATA_SIZE
;
4801 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
4803 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
4804 ptr_range
.address
= range
.address
;
4805 ptr_range
.size
= NUM_SMALL_BLOCKS
* SMALL_QUANTUM
;
4806 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
4808 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4809 void *mag_last_free
;
4810 vm_address_t mag_last_free_ptr
= 0;
4811 msize_t mag_last_free_msize
= 0;
4813 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
4817 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(mapped_region
);
4818 magazine_t
*small_mag_ptr
= small_mag_base
+ mag_index
;
4820 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4821 mag_last_free
= small_mag_ptr
->mag_last_free
;
4822 if (mag_last_free
) {
4823 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4824 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4827 for (mag_index
= 0; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4828 if ((void *)range
.address
== (small_mag_base
+ mag_index
)->mag_last_free_rgn
) {
4829 mag_last_free
= (small_mag_base
+ mag_index
)->mag_last_free
;
4830 if (mag_last_free
) {
4831 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4832 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4838 block_header
= (msize_t
*)(mapped_region
+ SMALL_METADATA_START
+ sizeof(region_trailer_t
));
4840 block_limit
= NUM_SMALL_BLOCKS
;
4841 if (region
== small_mag_ptr
->mag_last_region
) {
4842 block_index
+= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_start
);
4843 block_limit
-= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
4845 while (block_index
< block_limit
) {
4846 msize_and_free
= block_header
[block_index
];
4847 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4848 if (! (msize_and_free
& SMALL_IS_FREE
) &&
4849 range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
) != mag_last_free_ptr
) {
4851 buffer
[count
].address
= range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
);
4852 buffer
[count
].size
= SMALL_BYTES_FOR_MSIZE(msize
);
4854 if (count
>= MAX_RECORDER_BUFFER
) {
4855 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4861 return KERN_FAILURE
; // Somethings amiss. Avoid looping at this block_index.
4863 block_index
+= msize
;
4866 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4876 small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4880 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4881 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4882 free_list_t
**the_slot
= free_list
+ slot
;
4884 free_list_t
**limit
;
4886 msize_t leftover_msize
;
4887 free_list_t
*leftover_ptr
;
4889 // Assumes we've locked the region
4890 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4892 // Look for an exact match by checking the freelist for this msize.
4896 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4898 next
->previous
= ptr
->previous
;
4900 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4904 goto return_small_alloc
;
4907 // Mask off the bits representing slots holding free blocks smaller than
4908 // the size we need. If there are no larger free blocks, try allocating
4909 // from the free space at the end of the small region.
4910 if (szone
->is_largemem
) {
4911 // BITMAPN_CTZ implementation
4912 unsigned idx
= slot
>> 5;
4914 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4915 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4916 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4921 // Check for fallthrough: No bits set in bitmap
4922 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4923 goto try_small_from_end
;
4925 // Start looking at the first set bit, plus 32 bits for every word of
4926 // zeroes or entries that were too small.
4927 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4929 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4931 goto try_small_from_end
;
4933 slot
= BITMAP32_CTZ((&bitmap
));
4935 // FIXME: Explain use of - 1 here, last slot has special meaning
4936 limit
= free_list
+ szone
->num_small_slots
- 1;
4939 if (free_list
< limit
) {
4943 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4946 next
->previous
= ptr
->previous
;
4948 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4950 this_msize
= SMALL_PTR_SIZE(ptr
);
4951 goto add_leftover_and_proceed
;
4954 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4958 // We are now looking at the last slot, which contains blocks equal to, or
4959 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4960 // If the last freelist is not empty, and the head contains a block that is
4961 // larger than our request, then the remainder is put back on the free list.
4965 this_msize
= SMALL_PTR_SIZE(ptr
);
4966 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4967 if (this_msize
- msize
>= szone
->num_small_slots
) {
4968 // the leftover will go back to the free list, so we optimize by
4969 // modifying the free list rather than a pop and push of the head
4970 leftover_msize
= this_msize
- msize
;
4971 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4972 *limit
= leftover_ptr
;
4974 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
4976 leftover_ptr
->previous
= ptr
->previous
;
4977 leftover_ptr
->next
= ptr
->next
;
4978 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(leftover_ptr
),
4979 SMALL_META_INDEX_FOR_PTR(leftover_ptr
), leftover_msize
);
4980 // Store msize at the end of the block denoted by "leftover_ptr" (i.e. at a negative offset from follower)
4981 SMALL_PREVIOUS_MSIZE(FOLLOWING_SMALL_PTR(leftover_ptr
, leftover_msize
)) = leftover_msize
; // Access is safe
4983 if (LOG(szone
,ptr
)) {
4984 malloc_printf("in small_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr
, msize
, this_msize
);
4988 goto return_small_alloc
;
4991 next
->previous
= ptr
->previous
;
4994 goto add_leftover_and_proceed
;
4998 // Let's see if we can use small_mag_ptr->mag_bytes_free_at_end
4999 if (small_mag_ptr
->mag_bytes_free_at_end
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
5000 ptr
= (free_list_t
*)(SMALL_REGION_END(small_mag_ptr
->mag_last_region
) -
5001 small_mag_ptr
->mag_bytes_free_at_end
);
5002 small_mag_ptr
->mag_bytes_free_at_end
-= SMALL_BYTES_FOR_MSIZE(msize
);
5003 if (small_mag_ptr
->mag_bytes_free_at_end
) {
5004 // let's mark this block as in use to serve as boundary
5005 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
),
5006 SMALL_META_INDEX_FOR_PTR((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
)),
5007 SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
));
5010 goto return_small_alloc
;
5013 // Try from start if nothing left at end
5014 if (small_mag_ptr
->mag_bytes_free_at_start
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
5015 ptr
= (free_list_t
*)(SMALL_REGION_ADDRESS(small_mag_ptr
->mag_last_region
) +
5016 small_mag_ptr
->mag_bytes_free_at_start
- SMALL_BYTES_FOR_MSIZE(msize
));
5017 small_mag_ptr
->mag_bytes_free_at_start
-= SMALL_BYTES_FOR_MSIZE(msize
);
5018 if (small_mag_ptr
->mag_bytes_free_at_start
) {
5019 // let's mark this block as in use to serve as boundary
5020 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
));
5023 goto return_small_alloc
;
5028 add_leftover_and_proceed
:
5029 if (this_msize
> msize
) {
5030 leftover_msize
= this_msize
- msize
;
5031 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
5033 if (LOG(szone
,ptr
)) {
5034 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
5037 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover_ptr
, leftover_msize
);
5042 small_mag_ptr
->mag_num_objects
++;
5043 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(this_msize
);
5045 // Update this region's bytes in use count
5046 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5047 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(this_msize
);
5048 node
->bytes_used
= bytes_used
;
5050 // Emptiness discriminant
5051 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
5052 /* After this allocation the region is still sparse, so it must have been even more so before
5053 the allocation. That implies the region is already correctly marked. Do nothing. */
5055 /* Region has crossed threshold from sparsity to density. Mark in not "suitable" on the
5056 recirculation candidates list. */
5057 node
->recirc_suitable
= FALSE
;
5060 if (LOG(szone
,ptr
)) {
5061 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
5064 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), this_msize
);
5067 #undef DENSITY_THRESHOLD
5070 static INLINE
void *
5071 small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
5074 mag_index_t mag_index
= mag_get_thread_index(szone
);
5075 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5077 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5080 ptr
= (void *)small_mag_ptr
->mag_last_free
;
5082 if ((((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) == msize
) {
5084 small_mag_ptr
->mag_last_free
= NULL
;
5085 small_mag_ptr
->mag_last_free_rgn
= NULL
;
5086 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5087 CHECK(szone
, __PRETTY_FUNCTION__
);
5088 ptr
= (void *)((uintptr_t)ptr
& ~ (SMALL_QUANTUM
- 1));
5089 if (cleared_requested
) {
5090 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
5094 #endif /* SMALL_CACHE */
5097 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
5099 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5100 CHECK(szone
, __PRETTY_FUNCTION__
);
5101 if (cleared_requested
) {
5102 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
5107 if (small_get_region_from_depot(szone
, small_mag_ptr
, mag_index
, msize
)) {
5108 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
5110 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5111 CHECK(szone
, __PRETTY_FUNCTION__
);
5112 if (cleared_requested
) {
5113 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
5119 // The magazine is exhausted. A new region (heap) must be allocated to satisfy this call to malloc().
5120 // The allocation, an mmap() system call, will be performed outside the magazine spin locks by the first
5121 // thread that suffers the exhaustion. That thread sets "alloc_underway" and enters a critical section.
5122 // Threads arriving here later are excluded from the critical section, yield the CPU, and then retry the
5123 // allocation. After some time the magazine is resupplied, the original thread leaves with its allocation,
5124 // and retry-ing threads succeed in the code just above.
5125 if (!small_mag_ptr
->alloc_underway
) {
5128 // time to create a new region (do this outside the magazine lock)
5129 small_mag_ptr
->alloc_underway
= TRUE
;
5131 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5132 fresh_region
= allocate_pages_securely(szone
, SMALL_REGION_SIZE
, SMALL_BLOCKS_ALIGN
, VM_MEMORY_MALLOC_SMALL
);
5133 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5135 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
, fresh_region
, SMALL_REGION_SIZE
); // DTrace USDT Probe
5137 if (!fresh_region
) { // out of memory!
5138 small_mag_ptr
->alloc_underway
= FALSE
;
5140 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5144 ptr
= small_malloc_from_region_no_lock(szone
, small_mag_ptr
, mag_index
, msize
, fresh_region
);
5146 // we don't clear because this freshly allocated space is pristine
5147 small_mag_ptr
->alloc_underway
= FALSE
;
5149 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5150 CHECK(szone
, __PRETTY_FUNCTION__
);
5153 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5155 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5161 static NOINLINE
void
5162 free_small_botch(szone_t
*szone
, free_list_t
*ptr
)
5164 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5165 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5166 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5167 szone_error(szone
, 1, "double free", ptr
, NULL
);
5171 free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
)
5174 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5175 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5177 // ptr is known to be in small_region
5179 msize
= SMALL_MSIZE_FOR_BYTES(known_size
+ SMALL_QUANTUM
- 1);
5181 msize
= SMALL_PTR_SIZE(ptr
);
5182 if (SMALL_PTR_IS_FREE(ptr
)) {
5183 free_small_botch(szone
, ptr
);
5188 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5191 // Depot does not participate in SMALL_CACHE since it can't be directly malloc()'d
5192 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
5194 void *ptr2
= small_mag_ptr
->mag_last_free
; // Might be NULL
5195 region_t rgn2
= small_mag_ptr
->mag_last_free_rgn
;
5197 /* check that we don't already have this pointer in the cache */
5198 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (SMALL_QUANTUM
- 1))) {
5199 free_small_botch(szone
, ptr
);
5203 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
5204 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
5206 small_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
5207 small_mag_ptr
->mag_last_free_rgn
= small_region
;
5210 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5211 CHECK(szone
, __PRETTY_FUNCTION__
);
5215 msize
= (uintptr_t)ptr2
& (SMALL_QUANTUM
- 1);
5216 ptr
= (void *)(((uintptr_t)ptr2
) & ~(SMALL_QUANTUM
- 1));
5217 small_region
= rgn2
;
5219 #endif /* SMALL_CACHE */
5221 // Now in the time it took to acquire the lock, the region may have migrated
5222 // from one magazine to another. I.e. trailer->mag_index is volatile.
5223 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
5224 // is stale. If so, keep on tryin' ...
5225 region_trailer_t
*trailer
= REGION_TRAILER_FOR_SMALL_REGION(small_region
);
5226 mag_index_t refreshed_index
;
5228 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
5230 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5232 mag_index
= refreshed_index
;
5233 small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5234 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5237 if (small_free_no_lock(szone
, small_mag_ptr
, mag_index
, small_region
, ptr
, msize
))
5238 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5240 CHECK(szone
, __PRETTY_FUNCTION__
);
5244 print_small_free_list(szone_t
*szone
)
5247 _SIMPLE_STRING b
= _simple_salloc();
5248 mag_index_t mag_index
;
5251 _simple_sappend(b
, "small free sizes:\n");
5252 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
5254 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
5255 while (slot
< szone
->num_small_slots
) {
5256 ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
5258 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== szone
->num_small_slots
-1) ? ">=" : "",
5259 (slot
+ 1) * SMALL_QUANTUM
, free_list_count(szone
, ptr
));
5263 _simple_sappend(b
,"\n");
5265 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5271 print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_start
, size_t bytes_at_end
)
5273 unsigned counts
[1024];
5274 unsigned in_use
= 0;
5275 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(region
);
5276 uintptr_t current
= start
+ bytes_at_start
;
5277 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(region
) - bytes_at_end
;
5278 msize_t msize_and_free
;
5282 uintptr_t pgTot
= 0;
5284 if (region
== HASHRING_REGION_DEALLOCATED
) {
5285 if ((b
= _simple_salloc()) != NULL
) {
5286 _simple_sprintf(b
, "Small region [unknown address] was returned to the OS\n");
5287 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5293 memset(counts
, 0, sizeof(counts
));
5294 while (current
< limit
) {
5295 msize_and_free
= *SMALL_METADATA_FOR_PTR(current
);
5296 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
5298 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
5301 if (!(msize_and_free
& SMALL_IS_FREE
)) {
5307 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
5308 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
5311 pgTot
+= (pgHi
- pgLo
);
5314 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
5316 if ((b
= _simple_salloc()) != NULL
) {
5317 _simple_sprintf(b
, "Small region [%p-%p, %y] \t", (void *)start
, SMALL_REGION_END(region
), (int)SMALL_REGION_SIZE
);
5318 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_SMALL_REGION(region
));
5319 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_SMALL_REGION(region
));
5320 if (bytes_at_end
|| bytes_at_start
)
5321 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
+ bytes_at_start
);
5322 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_SMALL_REGION(region
)) {
5323 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
5325 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
5327 if (verbose
&& in_use
) {
5328 _simple_sappend(b
, "\n\tSizes in use: ");
5329 for (ci
= 0; ci
< 1024; ci
++)
5331 _simple_sprintf(b
, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
5333 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5339 small_free_list_check(szone_t
*szone
, grain_t slot
)
5341 mag_index_t mag_index
;
5343 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
5344 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5345 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
5348 free_list_t
*ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
5349 msize_t msize_and_free
;
5350 free_list_t
*previous
= NULL
;
5353 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
5354 if (!(msize_and_free
& SMALL_IS_FREE
)) {
5355 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5356 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5359 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
5360 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5361 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5364 if (!small_region_for_ptr_no_lock(szone
, ptr
)) {
5365 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5366 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5369 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
5370 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
5371 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5375 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
5379 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5384 /*******************************************************************************
5385 * Large allocator implementation
5386 ******************************************************************************/
5387 #pragma mark large allocator
5392 large_debug_print(szone_t
*szone
)
5395 large_entry_t
*range
;
5396 _SIMPLE_STRING b
= _simple_salloc();
5399 for (index
= 0, range
= szone
->large_entries
; index
< szone
->num_large_entries
; index
++, range
++)
5401 _simple_sprintf(b
, "%d: %p(%y); ", index
, range
->address
, range
->size
);
5403 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
5410 * Scan the hash ring looking for an entry for the given pointer.
5412 static large_entry_t
*
5413 large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
5415 // result only valid with lock held
5416 unsigned num_large_entries
= szone
->num_large_entries
;
5417 unsigned hash_index
;
5419 large_entry_t
*range
;
5421 if (!num_large_entries
)
5424 hash_index
= ((uintptr_t)ptr
>> vm_page_shift
) % num_large_entries
;
5428 range
= szone
->large_entries
+ index
;
5429 if (range
->address
== (vm_address_t
)ptr
)
5431 if (0 == range
->address
)
5432 return NULL
; // end of chain
5434 if (index
== num_large_entries
)
5436 } while (index
!= hash_index
);
5442 large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
)
5444 unsigned num_large_entries
= szone
->num_large_entries
;
5445 unsigned hash_index
= (((uintptr_t)(range
.address
)) >> vm_page_shift
) % num_large_entries
;
5446 unsigned index
= hash_index
;
5447 large_entry_t
*entry
;
5449 // assert(szone->num_large_objects_in_use < szone->num_large_entries); /* must be called with room to spare */
5452 entry
= szone
->large_entries
+ index
;
5453 if (0 == entry
->address
) {
5455 return; // end of chain
5458 if (index
== num_large_entries
)
5460 } while (index
!= hash_index
);
5462 // assert(0); /* must not fallthrough! */
5465 // FIXME: can't we simply swap the (now empty) entry with the last entry on the collision chain for this hash slot?
5467 large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
)
5469 unsigned num_large_entries
= szone
->num_large_entries
;
5470 unsigned hash_index
= entry
- szone
->large_entries
;
5471 unsigned index
= hash_index
;
5472 large_entry_t range
;
5474 // assert(entry->address == 0) /* caller must have cleared *entry */
5478 if (index
== num_large_entries
)
5480 range
= szone
->large_entries
[index
];
5481 if (0 == range
.address
)
5483 szone
->large_entries
[index
].address
= (vm_address_t
)0;
5484 szone
->large_entries
[index
].size
= 0;
5485 szone
->large_entries
[index
].did_madvise_reusable
= FALSE
;
5486 large_entry_insert_no_lock(szone
, range
); // this will reinsert in the
5488 } while (index
!= hash_index
);
5490 // assert(0); /* since entry->address == 0, must not fallthrough! */
5493 // FIXME: num should probably be a size_t, since you can theoretically allocate
5494 // more than 2^32-1 large_threshold objects in 64 bit.
5495 static INLINE large_entry_t
*
5496 large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
)
5498 size_t size
= num
* sizeof(large_entry_t
);
5500 // Note that we allocate memory (via a system call) under a spin lock
5501 // That is certainly evil, however it's very rare in the lifetime of a process
5502 // The alternative would slow down the normal case
5503 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC_LARGE
);
5507 large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
)
5509 size_t size
= num
* sizeof(large_entry_t
);
5511 range_to_deallocate
->address
= (vm_address_t
)entries
;
5512 range_to_deallocate
->size
= round_page(size
);
5515 static large_entry_t
*
5516 large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
)
5518 // sets range_to_deallocate
5519 unsigned old_num_entries
= szone
->num_large_entries
;
5520 large_entry_t
*old_entries
= szone
->large_entries
;
5521 // always an odd number for good hashing
5522 unsigned new_num_entries
= (old_num_entries
) ? old_num_entries
* 2 + 1 :
5523 ((vm_page_size
/ sizeof(large_entry_t
)) - 1);
5524 large_entry_t
*new_entries
= large_entries_alloc_no_lock(szone
, new_num_entries
);
5525 unsigned index
= old_num_entries
;
5526 large_entry_t oldRange
;
5528 // if the allocation of new entries failed, bail
5529 if (new_entries
== NULL
)
5532 szone
->num_large_entries
= new_num_entries
;
5533 szone
->large_entries
= new_entries
;
5535 /* rehash entries into the new list */
5537 oldRange
= old_entries
[index
];
5538 if (oldRange
.address
) {
5539 large_entry_insert_no_lock(szone
, oldRange
);
5544 large_entries_free_no_lock(szone
, old_entries
, old_num_entries
, range_to_deallocate
);
5546 range_to_deallocate
->address
= (vm_address_t
)0;
5547 range_to_deallocate
->size
= 0;
5553 // frees the specific entry in the size table
5554 // returns a range to truly deallocate
5556 large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
)
5560 range
.address
= entry
->address
;
5561 range
.size
= entry
->size
;
5563 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
5564 protect((void *)range
.address
, range
.size
, PROT_READ
| PROT_WRITE
, szone
->debug_flags
);
5565 range
.address
-= vm_page_size
;
5566 range
.size
+= 2 * vm_page_size
;
5571 entry
->did_madvise_reusable
= FALSE
;
5572 large_entries_rehash_after_entry_no_lock(szone
, entry
);
5575 if (large_entry_for_pointer_no_lock(szone
, (void *)range
.address
)) {
5576 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
5577 range
.address
, szone
->num_large_entries
);
5578 large_debug_print(szone
);
5585 static NOINLINE kern_return_t
5586 large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
,
5587 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
5590 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
5592 large_entry_t
*entries
;
5595 large_entry_t entry
;
5597 err
= reader(task
, large_entries_address
, sizeof(large_entry_t
) * num_entries
, (void **)&entries
);
5601 index
= num_entries
;
5602 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
5603 range
.address
= large_entries_address
;
5604 range
.size
= round_page(num_entries
* sizeof(large_entry_t
));
5605 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &range
, 1);
5607 if (type_mask
& (MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
)) {
5609 entry
= entries
[index
];
5610 if (entry
.address
) {
5611 range
.address
= entry
.address
;
5612 range
.size
= entry
.size
;
5613 buffer
[count
++] = range
;
5614 if (count
>= MAX_RECORDER_BUFFER
) {
5615 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
5623 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
5630 large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
,
5631 boolean_t cleared_requested
)
5634 vm_range_t range_to_deallocate
;
5636 large_entry_t large_entry
;
5639 num_pages
= 1; // minimal allocation size for this szone
5640 size
= (size_t)num_pages
<< vm_page_shift
;
5641 range_to_deallocate
.size
= 0;
5642 range_to_deallocate
.address
= 0;
5645 if (size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
) { // Look for a large_entry_t on the death-row cache?
5648 int i
, best
= -1, idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5649 size_t best_size
= SIZE_T_MAX
;
5651 while (1) { // Scan large_entry_cache for best fit, starting with most recent entry
5652 size_t this_size
= szone
->large_entry_cache
[idx
].size
;
5653 addr
= (void *)szone
->large_entry_cache
[idx
].address
;
5655 if (0 == alignment
|| 0 == (((uintptr_t) addr
) & (((uintptr_t) 1 << alignment
) - 1))) {
5656 if (size
== this_size
) { // size match!
5658 best_size
= this_size
;
5662 if (size
<= this_size
&& this_size
< best_size
) { // improved fit?
5664 best_size
= this_size
;
5668 if (idx
== stop_idx
) // exhausted live ring?
5672 idx
--; // bump idx down
5674 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5677 if (best
> -1 && (best_size
- size
) < size
) { //limit fragmentation to 50%
5678 addr
= (void *)szone
->large_entry_cache
[best
].address
;
5679 boolean_t was_madvised_reusable
= szone
->large_entry_cache
[best
].did_madvise_reusable
;
5681 // Compact live ring to fill entry now vacated at large_entry_cache[best]
5682 // while preserving time-order
5683 if (szone
->large_entry_cache_oldest
< szone
->large_entry_cache_newest
) {
5685 // Ring hasn't wrapped. Fill in from right.
5686 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5687 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5689 szone
->large_entry_cache_newest
--; // Pull in right endpoint.
5691 } else if (szone
->large_entry_cache_newest
< szone
->large_entry_cache_oldest
) {
5693 // Ring has wrapped. Arrange to fill in from the contiguous side.
5694 if (best
<= szone
->large_entry_cache_newest
) {
5696 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5697 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5699 if (0 < szone
->large_entry_cache_newest
)
5700 szone
->large_entry_cache_newest
--;
5702 szone
->large_entry_cache_newest
= LARGE_ENTRY_CACHE_SIZE
- 1;
5705 for ( i
= best
; i
> szone
->large_entry_cache_oldest
; --i
)
5706 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
- 1];
5708 if (szone
->large_entry_cache_oldest
< LARGE_ENTRY_CACHE_SIZE
- 1)
5709 szone
->large_entry_cache_oldest
++;
5711 szone
->large_entry_cache_oldest
= 0;
5715 // By trichotomy, large_entry_cache_newest == large_entry_cache_oldest.
5716 // That implies best == large_entry_cache_newest == large_entry_cache_oldest
5717 // and the ring is now empty.
5718 szone
->large_entry_cache
[best
].address
= 0;
5719 szone
->large_entry_cache
[best
].size
= 0;
5720 szone
->large_entry_cache
[best
].did_madvise_reusable
= FALSE
;
5723 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5724 // density of hash table too high; grow table
5725 // we do that under lock to avoid a race
5726 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5727 if (entries
== NULL
) {
5728 SZONE_UNLOCK(szone
);
5733 large_entry
.address
= (vm_address_t
)addr
;
5734 large_entry
.size
= best_size
;
5735 large_entry
.did_madvise_reusable
= FALSE
;
5736 large_entry_insert_no_lock(szone
, large_entry
);
5738 szone
->num_large_objects_in_use
++;
5739 szone
->num_bytes_in_large_objects
+= best_size
;
5740 if (!was_madvised_reusable
)
5741 szone
->large_entry_cache_reserve_bytes
-= best_size
;
5743 szone
->large_entry_cache_bytes
-= best_size
;
5745 if (szone
->flotsam_enabled
&& szone
->large_entry_cache_bytes
< SZONE_FLOTSAM_THRESHOLD_LOW
) {
5746 szone
->flotsam_enabled
= FALSE
;
5749 SZONE_UNLOCK(szone
);
5751 if (range_to_deallocate
.size
) {
5752 // we deallocate outside the lock
5753 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5756 // Perform the madvise() outside the lock.
5757 // Typically the madvise() is successful and we'll quickly return from this routine.
5758 // In the unusual case of failure, reacquire the lock to unwind.
5759 #if TARGET_OS_EMBEDDED
5760 // Ok to do this madvise on embedded because we won't call MADV_FREE_REUSABLE on a large
5761 // cache block twice without MADV_FREE_REUSE in between.
5763 if (was_madvised_reusable
&& -1 == madvise(addr
, size
, MADV_FREE_REUSE
)) {
5764 /* -1 return: VM map entry change makes this unfit for reuse. */
5766 szone_error(szone
, 0, "large_malloc madvise(..., MADV_FREE_REUSE) failed",
5767 addr
, "length=%d\n", size
);
5771 szone
->num_large_objects_in_use
--;
5772 szone
->num_bytes_in_large_objects
-= large_entry
.size
;
5774 // Re-acquire "entry" after interval just above where we let go the lock.
5775 large_entry_t
*entry
= large_entry_for_pointer_no_lock(szone
, addr
);
5776 if (NULL
== entry
) {
5777 szone_error(szone
, 1, "entry for pointer being discarded from death-row vanished", addr
, NULL
);
5778 SZONE_UNLOCK(szone
);
5781 range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5782 SZONE_UNLOCK(szone
);
5784 if (range_to_deallocate
.size
) {
5785 // we deallocate outside the lock
5786 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5789 /* Fall through to allocate_pages() afresh. */
5791 if (cleared_requested
) {
5792 memset(addr
, 0, size
);
5798 SZONE_UNLOCK(szone
);
5802 range_to_deallocate
.size
= 0;
5803 range_to_deallocate
.address
= 0;
5804 #endif /* LARGE_CACHE */
5806 addr
= allocate_pages(szone
, size
, alignment
, szone
->debug_flags
, VM_MEMORY_MALLOC_LARGE
);
5812 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5813 // density of hash table too high; grow table
5814 // we do that under lock to avoid a race
5815 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5816 if (entries
== NULL
) {
5817 SZONE_UNLOCK(szone
);
5822 large_entry
.address
= (vm_address_t
)addr
;
5823 large_entry
.size
= size
;
5824 large_entry
.did_madvise_reusable
= FALSE
;
5825 large_entry_insert_no_lock(szone
, large_entry
);
5827 szone
->num_large_objects_in_use
++;
5828 szone
->num_bytes_in_large_objects
+= size
;
5829 SZONE_UNLOCK(szone
);
5831 if (range_to_deallocate
.size
) {
5832 // we deallocate outside the lock
5833 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5838 static NOINLINE
void
5839 free_large(szone_t
*szone
, void *ptr
)
5841 // We have established ptr is page-aligned and neither tiny nor small
5842 large_entry_t
*entry
;
5843 vm_range_t vm_range_to_deallocate
;
5846 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5849 #ifndef MADV_CAN_REUSE
5850 #define MADV_CAN_REUSE 9 /* per Francois, for testing until xnu is resubmitted to B&I */
5852 if (entry
->size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
&&
5853 -1 != madvise((void *)(entry
->address
), entry
->size
, MADV_CAN_REUSE
)) { // Put the large_entry_t on the death-row cache?
5854 int idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5855 large_entry_t this_entry
= *entry
; // Make a local copy, "entry" is volatile when lock is let go.
5856 boolean_t reusable
= TRUE
;
5857 boolean_t should_madvise
= szone
->large_entry_cache_reserve_bytes
+ this_entry
.size
> szone
->large_entry_cache_reserve_limit
;
5860 // [Note that repeated entries in death-row risk vending the same entry subsequently
5861 // to two different malloc() calls. By checking here the (illegal) double free
5862 // is accommodated, matching the behavior of the previous implementation.]
5863 while (1) { // Scan large_entry_cache starting with most recent entry
5864 if (szone
->large_entry_cache
[idx
].address
== entry
->address
) {
5865 szone_error(szone
, 1, "pointer being freed already on death-row", ptr
, NULL
);
5866 SZONE_UNLOCK(szone
);
5870 if (idx
== stop_idx
) // exhausted live ring?
5874 idx
--; // bump idx down
5876 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5879 SZONE_UNLOCK(szone
);
5881 if (szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) { // Are we a purgable zone?
5882 int state
= VM_PURGABLE_NONVOLATILE
; // restore to default condition
5884 if (KERN_SUCCESS
!= vm_purgable_control(mach_task_self(), this_entry
.address
, VM_PURGABLE_SET_STATE
, &state
)) {
5885 malloc_printf("*** can't vm_purgable_control(..., VM_PURGABLE_SET_STATE) for large freed block at %p\n",
5886 this_entry
.address
);
5891 if (szone
->large_legacy_reset_mprotect
) { // Linked for Leopard?
5892 // Accomodate Leopard apps that (illegally) mprotect() their own guard pages on large malloc'd allocations
5893 int err
= mprotect((void *)(this_entry
.address
), this_entry
.size
, PROT_READ
| PROT_WRITE
);
5895 malloc_printf("*** can't reset protection for large freed block at %p\n", this_entry
.address
);
5900 // madvise(..., MADV_REUSABLE) death-row arrivals if hoarding would exceed large_entry_cache_reserve_limit
5901 if (should_madvise
) {
5902 // Issue madvise to avoid paging out the dirtied free()'d pages in "entry"
5903 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)0, (void *)(this_entry
.address
), this_entry
.size
); // DTrace USDT Probe
5905 #if TARGET_OS_EMBEDDED
5906 // Ok to do this madvise on embedded because we won't call MADV_FREE_REUSABLE on a large
5907 // cache block twice without MADV_FREE_REUSE in between.
5909 if (-1 == madvise((void *)(this_entry
.address
), this_entry
.size
, MADV_FREE_REUSABLE
)) {
5910 /* -1 return: VM map entry change makes this unfit for reuse. */
5912 szone_error(szone
, 0, "free_large madvise(..., MADV_FREE_REUSABLE) failed",
5913 (void *)this_entry
.address
, "length=%d\n", this_entry
.size
);
5921 // Re-acquire "entry" after interval just above where we let go the lock.
5922 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5923 if (NULL
== entry
) {
5924 szone_error(szone
, 1, "entry for pointer being freed from death-row vanished", ptr
, NULL
);
5925 SZONE_UNLOCK(szone
);
5929 // Add "entry" to death-row ring
5931 int idx
= szone
->large_entry_cache_newest
; // Most recently occupied
5935 if (szone
->large_entry_cache_newest
== szone
->large_entry_cache_oldest
&&
5936 0 == szone
->large_entry_cache
[idx
].address
) {
5937 // Ring is empty, idx is good as it stands
5941 // Extend the queue to the "right" by bumping up large_entry_cache_newest
5942 if (idx
== LARGE_ENTRY_CACHE_SIZE
- 1)
5943 idx
= 0; // Wrap index
5945 idx
++; // Bump index
5947 if (idx
== szone
->large_entry_cache_oldest
) { // Fully occupied
5948 // Drop this entry from the cache and deallocate the VM
5949 addr
= szone
->large_entry_cache
[idx
].address
;
5950 adjsize
= szone
->large_entry_cache
[idx
].size
;
5951 szone
->large_entry_cache_bytes
-= adjsize
;
5952 if (!szone
->large_entry_cache
[idx
].did_madvise_reusable
)
5953 szone
->large_entry_cache_reserve_bytes
-= adjsize
;
5955 // Using an unoccupied cache slot
5961 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
))
5962 memset((void *)(entry
->address
), 0x55, entry
->size
);
5964 entry
->did_madvise_reusable
= should_madvise
; // Was madvise()'d above?
5965 if (!should_madvise
) // Entered on death-row without madvise() => up the hoard total
5966 szone
->large_entry_cache_reserve_bytes
+= entry
->size
;
5968 szone
->large_entry_cache_bytes
+= entry
->size
;
5970 if (!szone
->flotsam_enabled
&& szone
->large_entry_cache_bytes
> SZONE_FLOTSAM_THRESHOLD_HIGH
) {
5971 szone
->flotsam_enabled
= TRUE
;
5974 szone
->large_entry_cache
[idx
] = *entry
;
5975 szone
->large_entry_cache_newest
= idx
;
5977 szone
->num_large_objects_in_use
--;
5978 szone
->num_bytes_in_large_objects
-= entry
->size
;
5980 (void)large_entry_free_no_lock(szone
, entry
);
5983 SZONE_UNLOCK(szone
);
5987 // Fall through to drop large_entry_cache_oldest from the cache,
5988 // and then deallocate its pages.
5990 // Trim the queue on the "left" by bumping up large_entry_cache_oldest
5991 if (szone
->large_entry_cache_oldest
== LARGE_ENTRY_CACHE_SIZE
- 1)
5992 szone
->large_entry_cache_oldest
= 0;
5994 szone
->large_entry_cache_oldest
++;
5996 // we deallocate_pages, including guard pages, outside the lock
5997 SZONE_UNLOCK(szone
);
5998 deallocate_pages(szone
, (void *)addr
, (size_t)adjsize
, 0);
6001 /* fall through to discard an allocation that is not reusable */
6004 #endif /* LARGE_CACHE */
6006 szone
->num_large_objects_in_use
--;
6007 szone
->num_bytes_in_large_objects
-= entry
->size
;
6009 vm_range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
6012 large_debug_print(szone
);
6014 szone_error(szone
, 1, "pointer being freed was not allocated", ptr
, NULL
);
6015 SZONE_UNLOCK(szone
);
6018 SZONE_UNLOCK(szone
); // we release the lock asap
6019 CHECK(szone
, __PRETTY_FUNCTION__
);
6021 // we deallocate_pages, including guard pages, outside the lock
6022 if (vm_range_to_deallocate
.address
) {
6024 // FIXME: large_entry_for_pointer_no_lock() needs the lock held ...
6025 if (large_entry_for_pointer_no_lock(szone
, (void *)vm_range_to_deallocate
.address
)) {
6026 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n",
6027 vm_range_to_deallocate
.address
, szone
->num_large_entries
);
6028 large_debug_print(szone
);
6032 deallocate_pages(szone
, (void *)vm_range_to_deallocate
.address
, (size_t)vm_range_to_deallocate
.size
, 0);
6036 static INLINE
void *
6037 large_try_shrink_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_good_size
)
6039 size_t shrinkage
= old_size
- new_good_size
;
6043 /* contract existing large entry */
6044 large_entry_t
*large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6046 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
6047 SZONE_UNLOCK(szone
);
6051 large_entry
->address
= (vm_address_t
)ptr
;
6052 large_entry
->size
= new_good_size
;
6053 szone
->num_bytes_in_large_objects
-= shrinkage
;
6054 SZONE_UNLOCK(szone
); // we release the lock asap
6056 deallocate_pages(szone
, (void *)((uintptr_t)ptr
+ new_good_size
), shrinkage
, 0);
6062 large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
6064 vm_address_t addr
= (vm_address_t
)ptr
+ old_size
;
6065 large_entry_t
*large_entry
;
6069 large_entry
= large_entry_for_pointer_no_lock(szone
, (void *)addr
);
6070 SZONE_UNLOCK(szone
);
6072 if (large_entry
) { // check if "addr = ptr + old_size" is already spoken for
6073 return 0; // large pointer already exists in table - extension is not going to work
6076 new_size
= round_page(new_size
);
6078 * Ask for allocation at a specific address, and mark as realloc
6079 * to request coalescing with previous realloc'ed extensions.
6081 err
= vm_allocate(mach_task_self(), &addr
, new_size
- old_size
, VM_MAKE_TAG(VM_MEMORY_REALLOC
));
6082 if (err
!= KERN_SUCCESS
) {
6087 /* extend existing large entry */
6088 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6090 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
6091 SZONE_UNLOCK(szone
);
6092 return 0; // Bail, leaking "addr"
6095 large_entry
->address
= (vm_address_t
)ptr
;
6096 large_entry
->size
= new_size
;
6097 szone
->num_bytes_in_large_objects
+= new_size
- old_size
;
6098 SZONE_UNLOCK(szone
); // we release the lock asap
6103 /********************* Zone call backs ************************/
6105 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
6107 static NOINLINE
void
6108 szone_free(szone_t
*szone
, void *ptr
)
6110 region_t tiny_region
;
6111 region_t small_region
;
6114 if (LOG(szone
, ptr
))
6115 malloc_printf("in szone_free with %p\n", ptr
);
6120 * Try to free to a tiny region.
6122 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
6123 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
6126 if ((tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
6127 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
6128 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
6131 free_tiny(szone
, ptr
, tiny_region
, 0);
6136 * Try to free to a small region.
6138 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
6139 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
6142 if ((small_region
= small_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
6143 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
6144 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
6147 free_small(szone
, ptr
, small_region
, 0);
6151 /* check that it's a legal large allocation */
6152 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
6153 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
6156 free_large(szone
, ptr
);
6159 static NOINLINE
void
6160 szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
6163 if (LOG(szone
, ptr
))
6164 malloc_printf("in szone_free_definite_size with %p\n", ptr
);
6167 szone_error(szone
, 1, "pointer of size zero being freed", ptr
, NULL
);
6176 * Try to free to a tiny region.
6178 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
6179 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
6182 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6183 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
6184 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
6187 free_tiny(szone
, ptr
, TINY_REGION_FOR_PTR(ptr
), size
);
6192 * Try to free to a small region.
6194 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
6195 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
6198 if (size
<= szone
->large_threshold
) {
6199 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
6200 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
6203 free_small(szone
, ptr
, SMALL_REGION_FOR_PTR(ptr
), size
);
6207 /* check that it's a legal large allocation */
6208 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
6209 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
6212 free_large(szone
, ptr
);
6215 static NOINLINE
void *
6216 szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
)
6221 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6223 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6226 ptr
= tiny_malloc_should_clear(szone
, msize
, cleared_requested
);
6227 } else if (size
<= szone
->large_threshold
) {
6229 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6232 ptr
= small_malloc_should_clear(szone
, msize
, cleared_requested
);
6235 size_t num_pages
= round_page(size
) >> vm_page_shift
;
6236 if (num_pages
== 0) /* Overflowed */
6239 ptr
= large_malloc(szone
, num_pages
, 0, cleared_requested
);
6242 if (LOG(szone
, ptr
))
6243 malloc_printf("szone_malloc returned %p\n", ptr
);
6246 * If requested, scribble on allocated memory.
6248 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && ptr
&& !cleared_requested
&& size
)
6249 memset(ptr
, 0xaa, size
);
6254 static NOINLINE
void *
6255 szone_malloc(szone_t
*szone
, size_t size
) {
6256 return szone_malloc_should_clear(szone
, size
, 0);
6259 static NOINLINE
void *
6260 szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
6262 size_t total_bytes
= num_items
* size
;
6264 // Check for overflow of integer multiplication
6265 if (num_items
> 1) {
6266 #if __LP64__ /* size_t is uint64_t */
6267 if ((num_items
| size
) & 0xffffffff00000000ul
) {
6268 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
6269 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
6270 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
6273 #else /* size_t is uint32_t */
6274 if ((num_items
| size
) & 0xffff0000ul
) {
6275 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
6276 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
6277 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
6283 return szone_malloc_should_clear(szone
, total_bytes
, 1);
6286 static NOINLINE
void *
6287 szone_valloc(szone_t
*szone
, size_t size
)
6291 if (size
<= szone
->large_threshold
) {
6292 ptr
= szone_memalign(szone
, vm_page_size
, size
);
6296 num_pages
= round_page(size
) >> vm_page_shift
;
6297 ptr
= large_malloc(szone
, num_pages
, 0, 0);
6301 if (LOG(szone
, ptr
))
6302 malloc_printf("szone_valloc returned %p\n", ptr
);
6307 /* Isolate PIC-base load (for __is_threaded) here. */
6308 static NOINLINE
size_t
6309 szone_size_try_large(szone_t
*szone
, const void *ptr
)
6312 large_entry_t
*entry
;
6315 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6319 SZONE_UNLOCK(szone
);
6321 if (LOG(szone
, ptr
)) {
6322 malloc_printf("szone_size for %p returned %d\n", ptr
, (unsigned)size
);
6328 static NOINLINE
size_t
6329 szone_size(szone_t
*szone
, const void *ptr
)
6332 msize_t msize
, msize_and_free
;
6337 if (LOG(szone
, ptr
)) {
6338 malloc_printf("in szone_size for %p (szone=%p)\n", ptr
, szone
);
6343 * Look for it in a tiny region.
6345 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1))
6347 if (tiny_region_for_ptr_no_lock(szone
, ptr
)) {
6348 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
6350 msize
= get_tiny_meta_header(ptr
, &is_free
);
6355 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
6356 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
6357 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6359 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
6362 for (mag_index
= 0; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6363 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6365 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
6371 return TINY_BYTES_FOR_MSIZE(msize
);
6375 * Look for it in a small region.
6377 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1))
6379 if (small_region_for_ptr_no_lock(szone
, ptr
)) {
6380 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
)
6382 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
6383 if (msize_and_free
& SMALL_IS_FREE
)
6387 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
6388 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
6389 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
6391 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
6394 for (mag_index
= 0; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6395 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
6397 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
6403 return SMALL_BYTES_FOR_MSIZE(msize_and_free
);
6407 * If not page-aligned, it cannot have come from a large allocation.
6409 if ((uintptr_t)ptr
& (vm_page_size
- 1))
6413 * Look for it in a large entry.
6415 return szone_size_try_large(szone
, ptr
);
6418 static NOINLINE
void *
6419 szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
6421 size_t old_size
, new_good_size
, valid_size
;
6425 if (LOG(szone
, ptr
)) {
6426 malloc_printf("in szone_realloc for %p, %d\n", ptr
, (unsigned)new_size
);
6430 // If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size.
6431 return szone_malloc(szone
, new_size
);
6432 } else if (0 == new_size
) {
6433 // If size is 0 and ptr is not a null pointer, the object pointed to is freed.
6434 szone_free(szone
, ptr
);
6435 // If size is 0, either a null pointer or a unique pointer that can be successfully passed
6436 // to free() shall be returned.
6437 return szone_malloc(szone
, 1);
6440 old_size
= szone_size(szone
, ptr
);
6442 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
6446 new_good_size
= szone_good_size(szone
, new_size
);
6447 if (new_good_size
== old_size
) { // Existing allocation is best fit evar?
6452 * If the new size suits the tiny allocator and the pointer being resized
6453 * belongs to a tiny region, try to reallocate in-place.
6455 if (new_good_size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6456 if (old_size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6457 if (new_good_size
<= (old_size
>> 1)) {
6459 * Serious shrinkage (more than half). free() the excess.
6461 return tiny_try_shrink_in_place(szone
, ptr
, old_size
, new_good_size
);
6462 } else if (new_good_size
<= old_size
) {
6464 * new_good_size smaller than old_size but not by much (less than half).
6465 * Avoid thrashing at the expense of some wasted storage.
6468 } else if (tiny_try_realloc_in_place(szone
, ptr
, old_size
, new_good_size
)) { // try to grow the allocation
6474 * Else if the new size suits the small allocator and the pointer being resized
6475 * belongs to a small region, and we're not protecting the small allocations
6476 * try to reallocate in-place.
6478 } else if (new_good_size
<= szone
->large_threshold
) {
6479 if ((NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
< old_size
&& old_size
<= szone
->large_threshold
) {
6480 if (new_good_size
<= (old_size
>> 1)) {
6481 return small_try_shrink_in_place(szone
, ptr
, old_size
, new_good_size
);
6482 } else if (new_good_size
<= old_size
) {
6484 } else if (small_try_realloc_in_place(szone
, ptr
, old_size
, new_good_size
)) {
6489 * Else if the allocation's a large allocation, try to reallocate in-place there.
6491 } else if (!(szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) && // purgeable needs fresh allocation
6492 (old_size
> szone
->large_threshold
) &&
6493 (new_good_size
> szone
->large_threshold
)) {
6494 if (new_good_size
<= (old_size
>> 1)) {
6495 return large_try_shrink_in_place(szone
, ptr
, old_size
, new_good_size
);
6496 } else if (new_good_size
<= old_size
) {
6498 } else if (large_try_realloc_in_place(szone
, ptr
, old_size
, new_good_size
)) {
6504 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
6506 if (new_good_size
<= (old_size
>> 1)) {
6507 /* Serious shrinkage (more than half). FALL THROUGH to alloc/copy/free. */
6508 } else if (new_good_size
<= old_size
) {
6512 new_ptr
= szone_malloc(szone
, new_size
);
6513 if (new_ptr
== NULL
)
6517 * If the allocation's large enough, try to copy using VM. If that fails, or
6518 * if it's too small, just copy by hand.
6520 valid_size
= MIN(old_size
, new_size
);
6521 if ((valid_size
< szone
->vm_copy_threshold
) ||
6522 vm_copy(mach_task_self(), (vm_address_t
)ptr
, valid_size
, (vm_address_t
)new_ptr
))
6523 memcpy(new_ptr
, ptr
, valid_size
);
6524 szone_free(szone
, ptr
);
6527 if (LOG(szone
, ptr
)) {
6528 malloc_printf("szone_realloc returned %p for %d\n", new_ptr
, (unsigned)new_size
);
6534 static NOINLINE
void *
6535 szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
6537 if ((size
+ alignment
) < size
) // size_t arithmetic wrapped!
6540 // alignment is gauranteed a power of 2 at least as large as sizeof(void *), hence non-zero.
6541 // Since size + alignment didn't wrap, 0 <= size + alignment - 1 < size + alignment
6542 size_t span
= size
+ alignment
- 1;
6544 if (alignment
<= TINY_QUANTUM
) {
6545 return szone_malloc(szone
, size
); // Trivially satisfied by tiny, small, or large
6547 } else if (span
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6548 msize_t mspan
= TINY_MSIZE_FOR_BYTES(span
+ TINY_QUANTUM
- 1);
6549 void *p
= szone_malloc(szone
, span
); // avoids inlining tiny_malloc_should_clear(szone, mspan, 0);
6554 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
6555 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
6557 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6558 msize_t mpad
= TINY_MSIZE_FOR_BYTES(pad
+ TINY_QUANTUM
- 1);
6559 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
6562 void *q
= (void *)(((uintptr_t) p
) + pad
);
6564 // Mark q as a block header and in-use, thus creating two blocks.
6565 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6566 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
6567 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
6568 set_tiny_meta_header_in_use(q
, msize
);
6569 tiny_mag_ptr
->mag_num_objects
++;
6571 // set_tiny_meta_header_in_use() "reaffirms" the block_header on the *following* block, so
6572 // now set its in_use bit as well. But only if its within the original allocation made above.
6574 BITARRAY_SET(TINY_INUSE_FOR_HEADER(TINY_BLOCK_HEADER_FOR_PTR(q
)), TINY_INDEX_FOR_PTR(q
) + msize
);
6575 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6577 // Give up mpad blocks beginning at p to the tiny free list
6578 // region_t r = TINY_REGION_FOR_PTR(p);
6579 szone_free(szone
, p
); // avoids inlining free_tiny(szone, p, &r);
6581 p
= q
; // advance p to the desired alignment
6585 void *q
= (void *)(((uintptr_t) p
) + TINY_BYTES_FOR_MSIZE(msize
));
6586 // Mark q as block header and in-use, thus creating two blocks.
6587 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6588 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
6589 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
6590 set_tiny_meta_header_in_use(q
, mwaste
);
6591 tiny_mag_ptr
->mag_num_objects
++;
6592 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6594 // Give up mwaste blocks beginning at q to the tiny free list
6595 // region_t r = TINY_REGION_FOR_PTR(q);
6596 szone_free(szone
, q
); // avoids inlining free_tiny(szone, q, &r);
6599 return p
; // p has the desired size and alignment, and can later be free()'d
6601 } else if ((NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
< size
&& alignment
<= SMALL_QUANTUM
) {
6602 return szone_malloc(szone
, size
); // Trivially satisfied by small or large
6604 } else if (span
<= szone
->large_threshold
) {
6606 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
6607 size
= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
+ TINY_QUANTUM
; // ensure block allocated by small does not have a tiny-possible size
6608 span
= size
+ alignment
- 1;
6611 msize_t mspan
= SMALL_MSIZE_FOR_BYTES(span
+ SMALL_QUANTUM
- 1);
6612 void *p
= szone_malloc(szone
, span
); // avoid inlining small_malloc_should_clear(szone, mspan, 0);
6617 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
6618 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
6620 msize_t msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6621 msize_t mpad
= SMALL_MSIZE_FOR_BYTES(pad
+ SMALL_QUANTUM
- 1);
6622 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
6625 void *q
= (void *)(((uintptr_t) p
) + pad
);
6627 // Mark q as block header and in-use, thus creating two blocks.
6628 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6629 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
6630 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
6631 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), mpad
);
6632 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), msize
+ mwaste
);
6633 small_mag_ptr
->mag_num_objects
++;
6634 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6636 // Give up mpad blocks beginning at p to the small free list
6637 // region_t r = SMALL_REGION_FOR_PTR(p);
6638 szone_free(szone
, p
); // avoid inlining free_small(szone, p, &r);
6640 p
= q
; // advance p to the desired alignment
6643 void *q
= (void *)(((uintptr_t) p
) + SMALL_BYTES_FOR_MSIZE(msize
));
6644 // Mark q as block header and in-use, thus creating two blocks.
6645 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6646 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
6647 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
6648 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), msize
);
6649 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mwaste
);
6650 small_mag_ptr
->mag_num_objects
++;
6651 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6653 // Give up mwaste blocks beginning at q to the small free list
6654 // region_t r = SMALL_REGION_FOR_PTR(q);
6655 szone_free(szone
, q
); // avoid inlining free_small(szone, q, &r);
6658 return p
; // p has the desired size and alignment, and can later be free()'d
6660 } else if (szone
->large_threshold
< size
&& alignment
<= vm_page_size
) {
6661 return szone_malloc(szone
, size
); // Trivially satisfied by large
6664 // ensure block allocated by large does not have a small-possible size
6665 size_t num_pages
= round_page(MAX(szone
->large_threshold
+ 1, size
)) >> vm_page_shift
;
6668 if (num_pages
== 0) /* Overflowed */
6671 p
= large_malloc(szone
, num_pages
, MAX(vm_page_shift
, __builtin_ctz(alignment
)), 0);
6678 // given a size, returns the number of pointers allocated capable of holding
6679 // that size, up to the limit specified by the 'count' argument. These pointers
6680 // are stored in the 'results' array, which must be allocated by the caller.
6681 // may return zero, since this function is only a best attempt at allocating
6682 // the pointers. clients should be prepared to call malloc for any additional
6683 // blocks they need.
6684 static NOINLINE
unsigned
6685 szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
6687 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6689 mag_index_t mag_index
= mag_get_thread_index(szone
);
6690 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6692 // only bother implementing this for tiny
6693 if (size
> (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
)
6695 // make sure to return objects at least one quantum in size
6699 CHECK(szone
, __PRETTY_FUNCTION__
);
6701 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
6702 // the caller has done so.
6703 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
6705 // with the zone locked, allocate objects from the free list until all
6706 // sufficiently large objects have been exhausted, or we have met our quota
6707 // of objects to allocate.
6708 while (found
< count
) {
6709 void *ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
6716 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6720 /* Try caching the tiny_region and checking if the next ptr hits there. */
6721 static NOINLINE
void
6722 szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
6726 region_t tiny_region
= NULL
;
6729 magazine_t
*tiny_mag_ptr
= NULL
;
6730 mag_index_t mag_index
= -1;
6732 // frees all the pointers in to_be_freed
6733 // note that to_be_freed may be overwritten during the process
6737 CHECK(szone
, __PRETTY_FUNCTION__
);
6738 while (cc
< count
) {
6739 ptr
= to_be_freed
[cc
];
6741 if (NULL
== tiny_region
|| tiny_region
!= TINY_REGION_FOR_PTR(ptr
)) { // region same as last iteration?
6742 if (tiny_mag_ptr
) { // non-NULL iff magazine lock taken
6743 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6744 tiny_mag_ptr
= NULL
;
6747 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
6750 tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6751 REGION_TRAILER_FOR_TINY_REGION(tiny_region
),
6752 MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
));
6753 mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
6757 // this is a tiny pointer
6758 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
6759 break; // pointer to metadata; let the standard free deal with it
6760 msize
= get_tiny_meta_header(ptr
, &is_free
);
6762 break; // a double free; let the standard free deal with it
6764 if (!tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
)) {
6765 // Arrange to re-acquire magazine lock
6766 tiny_mag_ptr
= NULL
;
6769 to_be_freed
[cc
] = NULL
;
6771 // No region in this zone claims ptr; let the standard free deal with it
6779 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6780 tiny_mag_ptr
= NULL
;
6783 CHECK(szone
, __PRETTY_FUNCTION__
);
6785 ptr
= to_be_freed
[count
];
6787 szone_free(szone
, ptr
);
6791 // FIXME: Suppose one of the locks is held?
6793 szone_destroy(szone_t
*szone
)
6796 large_entry_t
*large
;
6797 vm_range_t range_to_deallocate
;
6802 /* disable any memory pressure responder */
6803 szone
->flotsam_enabled
= FALSE
;
6805 // stack allocated copy of the death-row cache
6806 int idx
= szone
->large_entry_cache_oldest
, idx_max
= szone
->large_entry_cache_newest
;
6807 large_entry_t local_entry_cache
[LARGE_ENTRY_CACHE_SIZE
];
6809 memcpy((void *)local_entry_cache
, (void *)szone
->large_entry_cache
, sizeof(local_entry_cache
));
6811 szone
->large_entry_cache_oldest
= szone
->large_entry_cache_newest
= 0;
6812 szone
->large_entry_cache
[0].address
= 0x0;
6813 szone
->large_entry_cache
[0].size
= 0;
6814 szone
->large_entry_cache_bytes
= 0;
6815 szone
->large_entry_cache_reserve_bytes
= 0;
6817 SZONE_UNLOCK(szone
);
6819 // deallocate the death-row cache outside the zone lock
6820 while (idx
!= idx_max
) {
6821 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
6822 if (++idx
== LARGE_ENTRY_CACHE_SIZE
) idx
= 0;
6824 if (0 != local_entry_cache
[idx
].address
&& 0 != local_entry_cache
[idx
].size
) {
6825 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
6829 /* destroy large entries */
6830 index
= szone
->num_large_entries
;
6832 large
= szone
->large_entries
+ index
;
6833 if (large
->address
) {
6834 // we deallocate_pages, including guard pages
6835 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6838 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6839 if (range_to_deallocate
.size
)
6840 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6842 /* destroy tiny regions */
6843 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
)
6844 if ((HASHRING_OPEN_ENTRY
!= szone
->tiny_region_generation
->hashed_regions
[index
]) &&
6845 (HASHRING_REGION_DEALLOCATED
!= szone
->tiny_region_generation
->hashed_regions
[index
]))
6846 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
[index
], TINY_REGION_SIZE
, 0);
6848 /* destroy small regions */
6849 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
)
6850 if ((HASHRING_OPEN_ENTRY
!= szone
->small_region_generation
->hashed_regions
[index
]) &&
6851 (HASHRING_REGION_DEALLOCATED
!= szone
->small_region_generation
->hashed_regions
[index
]))
6852 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
[index
], SMALL_REGION_SIZE
, 0);
6854 /* destroy region hash rings, if any */
6855 if (szone
->tiny_region_generation
->hashed_regions
!= szone
->initial_tiny_regions
) {
6856 size_t size
= round_page(szone
->tiny_region_generation
->num_regions_allocated
* sizeof(region_t
));
6857 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
, size
, 0);
6859 if (szone
->small_region_generation
->hashed_regions
!= szone
->initial_small_regions
) {
6860 size_t size
= round_page(szone
->small_region_generation
->num_regions_allocated
* sizeof(region_t
));
6861 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
, size
, 0);
6864 /* Now destroy the separate szone region */
6865 if (szone
->cpu_id_key
!= (pthread_key_t
) -1)
6866 (void)pthread_key_delete(szone
->cpu_id_key
);
6867 deallocate_pages(szone
, (void *)&(szone
->tiny_magazines
[-1]), TINY_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6868 deallocate_pages(szone
, (void *)&(szone
->small_magazines
[-1]), SMALL_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6869 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, 0);
6872 static NOINLINE
size_t
6873 szone_good_size(szone_t
*szone
, size_t size
)
6877 // Find a good size for this tiny allocation.
6878 if (size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6879 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6882 return TINY_BYTES_FOR_MSIZE(msize
);
6885 // Find a good size for this small allocation.
6886 if (size
<= szone
->large_threshold
) {
6887 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6890 return SMALL_BYTES_FOR_MSIZE(msize
);
6893 // Check for integer overflow on the size, since unlike the two cases above,
6894 // there is no upper bound on allocation size at this point.
6895 if (size
> round_page(size
))
6896 return (size_t)(-1LL);
6899 // It is not acceptable to see a size of zero here, since that means we
6900 // failed to catch a request for zero bytes in the tiny check, or the size
6901 // overflowed to zero during some arithmetic.
6903 malloc_printf("szone_good_size() invariant broken %y\n", size
);
6905 return round_page(size
);
6908 unsigned szone_check_counter
= 0;
6909 unsigned szone_check_start
= 0;
6910 unsigned szone_check_modulo
= 1;
6912 static NOINLINE boolean_t
6913 szone_check_all(szone_t
*szone
, const char *function
)
6917 /* check tiny regions - chould check region count */
6918 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6919 region_t tiny
= szone
->tiny_region_generation
->hashed_regions
[index
];
6921 if (HASHRING_REGION_DEALLOCATED
== tiny
)
6925 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6926 REGION_TRAILER_FOR_TINY_REGION(tiny
), MAGAZINE_INDEX_FOR_TINY_REGION(tiny
));
6928 if (!tiny_check_region(szone
, tiny
)) {
6929 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6930 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6931 szone_error(szone
, 1, "check: tiny region incorrect", NULL
,
6932 "*** tiny region %ld incorrect szone_check_all(%s) counter=%d\n",
6933 index
, function
, szone_check_counter
);
6936 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6939 /* check tiny free lists */
6940 for (index
= 0; index
< NUM_TINY_SLOTS
; ++index
) {
6941 if (!tiny_free_list_check(szone
, index
)) {
6942 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6943 szone_error(szone
, 1, "check: tiny free list incorrect", NULL
,
6944 "*** tiny free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6945 index
, function
, szone_check_counter
);
6950 /* check small regions - could check region count */
6951 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6952 region_t small
= szone
->small_region_generation
->hashed_regions
[index
];
6954 if (HASHRING_REGION_DEALLOCATED
== small
)
6958 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6959 REGION_TRAILER_FOR_SMALL_REGION(small
), MAGAZINE_INDEX_FOR_SMALL_REGION(small
));
6961 if (!small_check_region(szone
, small
)) {
6962 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6963 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6964 szone_error(szone
, 1, "check: small region incorrect", NULL
,
6965 "*** small region %ld incorrect szone_check_all(%s) counter=%d\n",
6966 index
, function
, szone_check_counter
);
6969 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6972 /* check small free lists */
6973 for (index
= 0; index
< szone
->num_small_slots
; ++index
) {
6974 if (!small_free_list_check(szone
, index
)) {
6975 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6976 szone_error(szone
, 1, "check: small free list incorrect", NULL
,
6977 "*** small free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6978 index
, function
, szone_check_counter
);
6987 szone_check(szone_t
*szone
)
6989 if ((++szone_check_counter
% 10000) == 0)
6990 _malloc_printf(ASL_LEVEL_NOTICE
, "at szone_check counter=%d\n", szone_check_counter
);
6992 if (szone_check_counter
< szone_check_start
)
6995 if (szone_check_counter
% szone_check_modulo
)
6998 return szone_check_all(szone
, "");
7001 static kern_return_t
7002 szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
7003 memory_reader_t reader
, vm_range_recorder_t recorder
)
7008 if (!reader
) reader
= _szone_default_reader
;
7010 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
7011 if (err
) return err
;
7013 err
= tiny_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
7014 if (err
) return err
;
7016 err
= small_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
7017 if (err
) return err
;
7019 err
= large_in_use_enumerator(task
, context
, type_mask
,
7020 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
7024 // Following method is deprecated: use scalable_zone_statistics instead
7026 scalable_zone_info(malloc_zone_t
*zone
, unsigned *info_to_fill
, unsigned count
)
7028 szone_t
*szone
= (void *)zone
;
7031 // We do not lock to facilitate debug
7036 mag_index_t mag_index
;
7038 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
7039 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
;
7040 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
7041 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
7042 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
7048 for (t
= 0, u
= 0, mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
7049 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
;
7050 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
7051 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
7052 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
7058 info
[8] = szone
->num_large_objects_in_use
;
7059 info
[9] = szone
->num_bytes_in_large_objects
;
7061 info
[10] = 0; // DEPRECATED szone->num_huge_entries;
7062 info
[11] = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7064 info
[12] = szone
->debug_flags
;
7066 info
[0] = info
[4] + info
[6] + info
[8] + info
[10];
7067 info
[1] = info
[5] + info
[7] + info
[9] + info
[11];
7069 info
[3] = (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
7070 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ info
[9] + info
[11];
7072 info
[2] = info
[3] - s
;
7073 memcpy(info_to_fill
, info
, sizeof(unsigned)*count
);
7076 // FIXME: consistent picture requires locking!
7077 static NOINLINE
void
7078 szone_print(szone_t
*szone
, boolean_t verbose
)
7084 scalable_zone_info((void *)szone
, info
, 13);
7085 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7086 "Scalable zone %p: inUse=%u(%y) touched=%y allocated=%y flags=%d\n",
7087 szone
, info
[0], info
[1], info
[2], info
[3], info
[12]);
7088 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7089 "\ttiny=%u(%y) small=%u(%y) large=%u(%y) huge=%u(%y)\n",
7090 info
[4], info
[5], info
[6], info
[7], info
[8], info
[9], info
[10], info
[11]);
7092 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7093 "%lu tiny regions:\n", szone
->num_tiny_regions
);
7094 if (szone
->num_tiny_regions_dealloc
)
7095 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7096 "[%lu tiny regions have been vm_deallocate'd]\n", szone
->num_tiny_regions_dealloc
);
7097 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
7098 region
= szone
->tiny_region_generation
->hashed_regions
[index
];
7099 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
7100 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
7101 print_tiny_region(verbose
, region
,
7102 (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
7103 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
: 0,
7104 (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
7105 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
7109 print_tiny_free_list(szone
);
7111 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7112 "%lu small regions:\n", szone
->num_small_regions
);
7113 if (szone
->num_small_regions_dealloc
)
7114 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7115 "[%lu small regions have been vm_deallocate'd]\n", szone
->num_small_regions_dealloc
);
7116 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
7117 region
= szone
->small_region_generation
->hashed_regions
[index
];
7118 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
7119 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(region
);
7120 print_small_region(szone
, verbose
, region
,
7121 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
7122 szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
: 0,
7123 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
7124 szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
7128 print_small_free_list(szone
);
7132 szone_log(malloc_zone_t
*zone
, void *log_address
)
7134 szone_t
*szone
= (szone_t
*)zone
;
7136 szone
->log_address
= log_address
;
7140 szone_force_lock(szone_t
*szone
)
7144 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
7145 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7147 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
])));
7149 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
7150 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[i
])));
7152 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
])));
7158 szone_force_unlock(szone_t
*szone
)
7162 SZONE_UNLOCK(szone
);
7164 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
7165 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
7168 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
7169 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7174 szone_locked(szone_t
*szone
)
7179 tookLock
= SZONE_TRY_LOCK(szone
);
7182 SZONE_UNLOCK(szone
);
7184 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
7185 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->small_magazines
[i
])));
7188 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
7191 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
7192 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7195 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
7201 szone_pressure_relief(szone_t
*szone
, size_t goal
)
7204 if (!szone
->flotsam_enabled
)
7209 // stack allocated copy of the death-row cache
7210 int idx
= szone
->large_entry_cache_oldest
, idx_max
= szone
->large_entry_cache_newest
;
7211 large_entry_t local_entry_cache
[LARGE_ENTRY_CACHE_SIZE
];
7213 memcpy((void *)local_entry_cache
, (void *)szone
->large_entry_cache
, sizeof(local_entry_cache
));
7215 szone
->large_entry_cache_oldest
= szone
->large_entry_cache_newest
= 0;
7216 szone
->large_entry_cache
[0].address
= 0x0;
7217 szone
->large_entry_cache
[0].size
= 0;
7218 szone
->large_entry_cache_bytes
= 0;
7219 szone
->large_entry_cache_reserve_bytes
= 0;
7221 szone
->flotsam_enabled
= FALSE
;
7223 SZONE_UNLOCK(szone
);
7225 // deallocate the death-row cache outside the zone lock
7227 while (idx
!= idx_max
) {
7228 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
7229 total
+= local_entry_cache
[idx
].size
;
7230 if (++idx
== LARGE_ENTRY_CACHE_SIZE
) idx
= 0;
7232 if (0 != local_entry_cache
[idx
].address
&& 0 != local_entry_cache
[idx
].size
) {
7233 deallocate_pages(szone
, (void *) local_entry_cache
[idx
].address
, local_entry_cache
[idx
].size
, 0);
7234 total
+= local_entry_cache
[idx
].size
;
7236 MAGMALLOC_PRESSURERELIEF((void *)szone
, goal
, total
); // DTrace USDT Probe
7244 scalable_zone_statistics(malloc_zone_t
*zone
, malloc_statistics_t
*stats
, unsigned subzone
)
7246 szone_t
*szone
= (szone_t
*)zone
;
7254 mag_index_t mag_index
;
7256 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
7257 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
;
7258 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
7259 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
7260 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
7263 stats
->blocks_in_use
= t
;
7264 stats
->size_in_use
= u
;
7265 stats
->size_allocated
= (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
;
7266 stats
->max_size_in_use
= stats
->size_allocated
- s
;
7274 mag_index_t mag_index
;
7276 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
7277 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
;
7278 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
7279 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
7280 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
7283 stats
->blocks_in_use
= t
;
7284 stats
->size_in_use
= u
;
7285 stats
->size_allocated
= (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
;
7286 stats
->max_size_in_use
= stats
->size_allocated
- s
;
7290 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
7291 stats
->size_in_use
= szone
->num_bytes_in_large_objects
;
7292 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
7295 stats
->blocks_in_use
= 0; // DEPRECATED szone->num_huge_entries;
7296 stats
->size_in_use
= 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7297 stats
->max_size_in_use
= stats
->size_allocated
= 0;
7304 szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
7311 mag_index_t mag_index
;
7313 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
7314 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_start
;
7315 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
7316 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
7317 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
7320 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
7321 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_start
;
7322 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
7323 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
7324 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
7327 large
= szone
->num_bytes_in_large_objects
+ 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7329 stats
->blocks_in_use
= t
+ szone
->num_large_objects_in_use
+ 0; // DEPRECATED szone->num_huge_entries;
7330 stats
->size_in_use
= u
+ large
;
7331 stats
->max_size_in_use
= stats
->size_allocated
=
7332 (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
7333 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ large
;
7334 // Now we account for the untouched areas
7335 stats
->max_size_in_use
-= s
;
7339 legacy_zeroing_large_malloc(szone_t
*szone
, size_t size
) {
7340 if (size
> LARGE_THRESHOLD
) // Leopard and earlier returned a ZFOD range, so ...
7341 return szone_calloc(szone
, 1, size
); // Clear to zero always, ham-handedly touching in each page
7343 return szone_malloc(szone
, size
);
7347 legacy_zeroing_large_valloc(szone_t
*szone
, size_t size
) {
7348 void *p
= szone_valloc(szone
, size
);
7350 // Leopard and earlier returned a ZFOD range, so ...
7351 memset(p
, 0, size
); // Clear to zero always, ham-handedly touching in each page
7355 void zeroify_scalable_zone(malloc_zone_t
*zone
)
7357 szone_t
*szone
= (szone_t
*)zone
;
7360 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
| PROT_WRITE
);
7361 szone
->basic_zone
.malloc
= (void *)legacy_zeroing_large_malloc
;
7362 szone
->basic_zone
.valloc
= (void *)legacy_zeroing_large_valloc
;
7363 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
);
7367 static const struct malloc_introspection_t szone_introspect
= {
7368 (void *)szone_ptr_in_use_enumerator
,
7369 (void *)szone_good_size
,
7370 (void *)szone_check
,
7371 (void *)szone_print
,
7373 (void *)szone_force_lock
,
7374 (void *)szone_force_unlock
,
7375 (void *)szone_statistics
,
7376 (void *)szone_locked
,
7377 NULL
, NULL
, NULL
, NULL
, /* Zone enumeration version 7 and forward. */
7378 }; // marked as const to spare the DATA section
7381 create_scalable_zone(size_t initial_size
, unsigned debug_flags
)
7384 uint64_t hw_memsize
= 0;
7387 * Sanity-check our build-time assumptions about the size of a page.
7388 * Since we have sized various things assuming the default page size,
7389 * attempting to determine it dynamically is not useful.
7391 if ((vm_page_size
!= _vm_page_size
) || (vm_page_shift
!= _vm_page_shift
)) {
7392 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
7396 #if defined(__i386__) || defined(__x86_64__)
7397 if (_COMM_PAGE_VERSION_REQD
> (*((short *) _COMM_PAGE_VERSION
))) { // _COMM_PAGE_CPU_NUMBER must be present at runtime
7398 malloc_printf("*** ERROR - comm page version mismatch.\n");
7403 /* get memory for the zone. */
7404 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, 0, VM_MEMORY_MALLOC
);
7408 /* set up the szone structure */
7410 #warning CHECK_REGIONS enabled
7411 debug_flags
|= CHECK_REGIONS
;
7414 #warning LOG enabled
7415 szone
->log_address
= ~0;
7417 szone
->trg
[0].nextgen
= &(szone
->trg
[1]);
7418 szone
->trg
[1].nextgen
= &(szone
->trg
[0]);
7419 szone
->tiny_region_generation
= &(szone
->trg
[0]);
7421 szone
->tiny_region_generation
->hashed_regions
= szone
->initial_tiny_regions
;
7422 szone
->tiny_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
7423 szone
->tiny_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
7425 szone
->srg
[0].nextgen
= &(szone
->srg
[1]);
7426 szone
->srg
[1].nextgen
= &(szone
->srg
[0]);
7427 szone
->small_region_generation
= &(szone
->srg
[0]);
7429 szone
->small_region_generation
->hashed_regions
= szone
->initial_small_regions
;
7430 szone
->small_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
7431 szone
->small_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
7435 * Initialize variables that size the free list for SMALL allocations based
7436 * upon the amount of memory in the system. Switch to a larger number of
7437 * free list entries at 1GB.
7439 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7440 if ((hw_memsize
= *(uint64_t *)(uintptr_t)_COMM_PAGE_MEMORY_SIZE
) >= (1ULL << 30))
7442 size_t uint64_t_size
= sizeof(hw_memsize
);
7444 if (0 == sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0) &&
7445 hw_memsize
>= (1ULL << 30))
7448 szone
->is_largemem
= 1;
7449 szone
->num_small_slots
= NUM_SMALL_SLOTS_LARGEMEM
;
7450 szone
->large_threshold
= LARGE_THRESHOLD_LARGEMEM
;
7451 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD_LARGEMEM
;
7453 szone
->is_largemem
= 0;
7454 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
7455 szone
->large_threshold
= LARGE_THRESHOLD
;
7456 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7459 szone
->large_entry_cache_reserve_limit
=
7460 hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
7462 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7463 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
7464 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
7465 szone
->large_legacy_reset_mprotect
= TRUE
;
7467 szone
->large_legacy_reset_mprotect
= FALSE
;
7470 // Initialize the security token.
7471 szone
->cookie
= (uintptr_t)malloc_entropy
[0];
7474 #if __i386__ || __LP64__ || TARGET_OS_EMBEDDED
7476 uintptr_t stackbase
= 0x8fe00000;
7477 int entropic_bits
= 3;
7479 uintptr_t stackbase
= USRSTACK64
;
7480 int entropic_bits
= 16;
7482 uintptr_t stackbase
= USRSTACK
;
7483 int entropic_bits
= 3;
7485 if (0 != _dyld_get_image_slide((const struct mach_header
*)_NSGetMachExecuteHeader())) {
7486 if (0 == entropic_address
) {
7487 uintptr_t t
= stackbase
- MAXSSIZ
- ((uintptr_t) (malloc_entropy
[1] & ((1 << entropic_bits
) - 1)) << SMALL_BLOCKS_ALIGN
);
7488 (void)__sync_bool_compare_and_swap(&entropic_limit
, 0, t
); // Just one initialization please
7489 (void)__sync_bool_compare_and_swap(&entropic_address
, 0, t
- ENTROPIC_KABILLION
); // Just one initialization please
7491 debug_flags
&= ~DISABLE_ASLR
;
7493 debug_flags
|= DISABLE_ASLR
;
7497 debug_flags
|= DISABLE_ASLR
;
7500 szone
->basic_zone
.version
= 8;
7501 szone
->basic_zone
.size
= (void *)szone_size
;
7502 szone
->basic_zone
.malloc
= (void *)szone_malloc
;
7503 szone
->basic_zone
.calloc
= (void *)szone_calloc
;
7504 szone
->basic_zone
.valloc
= (void *)szone_valloc
;
7505 szone
->basic_zone
.free
= (void *)szone_free
;
7506 szone
->basic_zone
.realloc
= (void *)szone_realloc
;
7507 szone
->basic_zone
.destroy
= (void *)szone_destroy
;
7508 szone
->basic_zone
.batch_malloc
= (void *)szone_batch_malloc
;
7509 szone
->basic_zone
.batch_free
= (void *)szone_batch_free
;
7510 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
7511 szone
->basic_zone
.memalign
= (void *)szone_memalign
;
7512 szone
->basic_zone
.free_definite_size
= (void *)szone_free_definite_size
;
7513 szone
->basic_zone
.pressure_relief
= (void *)szone_pressure_relief
;
7515 szone
->basic_zone
.reserved1
= 0; /* Set to zero once and for all as required by CFAllocator. */
7516 szone
->basic_zone
.reserved2
= 0; /* Set to zero once and for all as required by CFAllocator. */
7517 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
); /* Prevent overwriting the function pointers in basic_zone. */
7519 szone
->debug_flags
= debug_flags
;
7520 LOCK_INIT(szone
->large_szone_lock
);
7522 #if defined(__ppc__) || defined(__ppc64__)
7524 * In the interest of compatibility for PPC applications executing via Rosetta,
7525 * arrange to zero-fill allocations as occurred by side effect in Leopard and earlier.
7527 zeroify_scalable_zone((malloc_zone_t
*)szone
);
7530 #if defined(__i386__) || defined(__x86_64__)
7531 szone
->cpu_id_key
= (pthread_key_t
) -1; // Unused. _COMM_PAGE_CPU_NUMBER preferred.
7534 if ((err
= pthread_key_create(&(szone
->cpu_id_key
), NULL
))) {
7535 malloc_printf("*** ERROR -pthread_key_create failure err=%d.\n", err
);
7536 szone
->cpu_id_key
= (pthread_key_t
) -1;
7540 // Query the number of configured processors.
7541 // Uniprocessor case gets just one tiny and one small magazine (whose index is zero). This gives
7542 // the same behavior as the original scalable malloc. MP gets per-CPU magazines
7543 // that scale (way) better.
7544 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7545 int nproc
= *(uint8_t *)(uintptr_t)_COMM_PAGE_NCPUS
;
7547 int nproc
= sysconf(_SC_NPROCESSORS_CONF
);
7549 szone
->num_tiny_magazines
= (nproc
> 1) ? MIN(nproc
, TINY_MAX_MAGAZINES
) : 1;
7551 // FIXME vm_allocate() based on number of configured CPUs
7552 magazine_t
*tiny_magazines
= allocate_pages(NULL
, TINY_MAGAZINE_PAGED_SIZE
, 0,
7553 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
7554 if (NULL
== tiny_magazines
)
7557 szone
->tiny_magazines
= &(tiny_magazines
[1]); // szone->tiny_magazines[-1] is the Depot
7559 // The magazines are indexed in [0 .. (num_tiny_magazines - 1)]
7560 // Find the smallest power of 2 that exceeds (num_tiny_magazines - 1)
7561 szone
->num_tiny_magazines_mask_shift
= 0;
7563 while( i
<= (szone
->num_tiny_magazines
- 1) ) {
7564 szone
->num_tiny_magazines_mask_shift
++;
7568 // Now if i <= TINY_MAX_MAGAZINES we'll never access tiny_magazines[] out of bounds.
7569 if (i
> TINY_MAX_MAGAZINES
) {
7570 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
7574 // Reduce i by 1 to obtain a mask covering [0 .. (num_tiny_magazines - 1)]
7575 szone
->num_tiny_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
7576 #if TARGET_OS_EMBEDDED
7577 szone
->last_tiny_advise
= 0;
7580 // Init the tiny_magazine locks
7581 LOCK_INIT(szone
->tiny_regions_lock
);
7582 LOCK_INIT(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
7583 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
7584 LOCK_INIT(szone
->tiny_magazines
[i
].magazine_lock
);
7587 szone
->num_small_magazines
= (nproc
> 1) ? MIN(nproc
, SMALL_MAX_MAGAZINES
) : 1;
7589 // FIXME vm_allocate() based on number of configured CPUs
7590 magazine_t
*small_magazines
= allocate_pages(NULL
, SMALL_MAGAZINE_PAGED_SIZE
, 0,
7591 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
7592 if (NULL
== small_magazines
)
7595 szone
->small_magazines
= &(small_magazines
[1]); // szone->small_magazines[-1] is the Depot
7597 // The magazines are indexed in [0 .. (num_small_magazines - 1)]
7598 // Find the smallest power of 2 that exceeds (num_small_magazines - 1)
7599 szone
->num_small_magazines_mask_shift
= 0;
7600 while( i
<= (szone
->num_small_magazines
- 1) ) {
7601 szone
->num_small_magazines_mask_shift
++;
7605 // Now if i <= SMALL_MAX_MAGAZINES we'll never access small_magazines[] out of bounds.
7606 if (i
> SMALL_MAX_MAGAZINES
) {
7607 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
7611 // Reduce i by 1 to obtain a mask covering [0 .. (num_small_magazines - 1)]
7612 szone
->num_small_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
7613 #if TARGET_OS_EMBEDDED
7614 szone
->last_small_advise
= 0;
7617 // Init the small_magazine locks
7618 LOCK_INIT(szone
->small_regions_lock
);
7619 LOCK_INIT(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
7620 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
7621 LOCK_INIT(szone
->small_magazines
[i
].magazine_lock
);
7624 CHECK(szone
, __PRETTY_FUNCTION__
);
7625 return (malloc_zone_t
*)szone
;
7629 // purgeable zones have their own "large" allocation pool, but share "tiny" and "large"
7630 // heaps with a helper_zone identified in the call to create_purgeable_zone()
7633 purgeable_size(szone_t
*szone
, const void *ptr
)
7635 // Only claim our large allocations, leave the shared tiny/small for the helper zone to claim.
7636 return szone_size_try_large(szone
, ptr
);
7640 purgeable_malloc(szone_t
*szone
, size_t size
) {
7641 if (size
<= szone
->large_threshold
)
7642 return szone_malloc(szone
->helper_zone
, size
);
7644 return szone_malloc(szone
, size
);
7648 purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
7650 size_t total_bytes
= num_items
* size
;
7652 // Check for overflow of integer multiplication
7653 if (num_items
> 1) {
7654 #if __LP64__ /* size_t is uint64_t */
7655 if ((num_items
| size
) & 0xffffffff00000000ul
) {
7656 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
7657 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
7658 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
7661 #else /* size_t is uint32_t */
7662 if ((num_items
| size
) & 0xffff0000ul
) {
7663 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
7664 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
7665 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
7671 if (total_bytes
<= szone
->large_threshold
)
7672 return szone_calloc(szone
->helper_zone
, 1, total_bytes
);
7674 return szone_calloc(szone
, 1, total_bytes
);
7678 purgeable_valloc(szone_t
*szone
, size_t size
)
7680 if (size
<= szone
->large_threshold
)
7681 return szone_valloc(szone
->helper_zone
, size
);
7683 return szone_valloc(szone
, size
);
7687 purgeable_free(szone_t
*szone
, void *ptr
)
7689 large_entry_t
*entry
;
7692 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
7693 SZONE_UNLOCK(szone
);
7695 return free_large(szone
, ptr
);
7697 return szone_free(szone
->helper_zone
, ptr
);
7702 purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
7704 if (size
<= szone
->large_threshold
)
7705 return szone_free_definite_size(szone
->helper_zone
, ptr
, size
);
7707 return szone_free_definite_size(szone
, ptr
, size
);
7711 purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
7716 // If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size.
7717 return purgeable_malloc(szone
, new_size
);
7718 } else if (0 == new_size
) {
7719 // If size is 0 and ptr is not a null pointer, the object pointed to is freed.
7720 purgeable_free(szone
, ptr
);
7721 // If size is 0, either a null pointer or a unique pointer that can be successfully passed
7722 // to free() shall be returned.
7723 return purgeable_malloc(szone
, 1);
7726 old_size
= purgeable_size(szone
, ptr
); // Now ptr can be safely size()'d
7728 old_size
= szone_size(szone
->helper_zone
, ptr
);
7731 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
7735 // Distinguish 4 cases: {oldsize, newsize} x { <= , > large_threshold }
7736 // and deal with the allocation crossing from the purgeable zone to the helper zone and vice versa.
7737 if (old_size
<= szone
->large_threshold
) {
7738 if (new_size
<= szone
->large_threshold
)
7739 return szone_realloc(szone
->helper_zone
, ptr
, new_size
);
7741 // allocation crosses from helper to purgeable zone
7742 void * new_ptr
= purgeable_malloc(szone
, new_size
);
7744 memcpy(new_ptr
, ptr
, old_size
);
7745 szone_free_definite_size(szone
->helper_zone
, ptr
, old_size
);
7747 return new_ptr
; // in state VM_PURGABLE_NONVOLATILE
7750 if (new_size
<= szone
->large_threshold
) {
7751 // allocation crosses from purgeable to helper zone
7752 void * new_ptr
= szone_malloc(szone
->helper_zone
, new_size
);
7754 memcpy(new_ptr
, ptr
, new_size
);
7755 purgeable_free_definite_size(szone
, ptr
, old_size
);
7759 void * new_ptr
= purgeable_malloc(szone
, new_size
);
7761 memcpy(new_ptr
, ptr
, MIN(old_size
, new_size
));
7762 purgeable_free_definite_size(szone
, ptr
, old_size
);
7764 return new_ptr
; // in state VM_PURGABLE_NONVOLATILE
7771 purgeable_destroy(szone_t
*szone
)
7773 /* destroy large entries */
7774 size_t index
= szone
->num_large_entries
;
7775 large_entry_t
*large
;
7776 vm_range_t range_to_deallocate
;
7779 large
= szone
->large_entries
+ index
;
7780 if (large
->address
) {
7781 // we deallocate_pages, including guard pages
7782 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
7785 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
7786 if (range_to_deallocate
.size
)
7787 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
7789 /* Now destroy the separate szone region */
7790 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, 0);
7794 purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
7796 return szone_batch_malloc(szone
->helper_zone
, size
, results
, count
);
7800 purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
7802 return szone_batch_free(szone
->helper_zone
, to_be_freed
, count
);
7806 purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
7808 if (size
<= szone
->large_threshold
)
7809 return szone_memalign(szone
->helper_zone
, alignment
, size
);
7811 return szone_memalign(szone
, alignment
, size
);
7814 static kern_return_t
7815 purgeable_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
7816 memory_reader_t reader
, vm_range_recorder_t recorder
)
7821 if (!reader
) reader
= _szone_default_reader
;
7823 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
7824 if (err
) return err
;
7826 err
= large_in_use_enumerator(task
, context
, type_mask
,
7827 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
7832 purgeable_good_size(szone_t
*szone
, size_t size
)
7834 if (size
<= szone
->large_threshold
)
7835 return szone_good_size(szone
->helper_zone
, size
);
7837 return szone_good_size(szone
, size
);
7841 purgeable_check(szone_t
*szone
)
7847 purgeable_print(szone_t
*szone
, boolean_t verbose
)
7849 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
7850 "Scalable zone %p: inUse=%u(%y) flags=%d\n",
7851 szone
, szone
->num_large_objects_in_use
, szone
->num_bytes_in_large_objects
, szone
->debug_flags
);
7855 purgeable_log(malloc_zone_t
*zone
, void *log_address
)
7857 szone_t
*szone
= (szone_t
*)zone
;
7859 szone
->log_address
= log_address
;
7863 purgeable_force_lock(szone_t
*szone
)
7869 purgeable_force_unlock(szone_t
*szone
)
7871 SZONE_UNLOCK(szone
);
7875 purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
7877 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
7878 stats
->size_in_use
= stats
->max_size_in_use
= stats
->size_allocated
= szone
->num_bytes_in_large_objects
;
7882 purgeable_locked(szone_t
*szone
)
7886 tookLock
= SZONE_TRY_LOCK(szone
);
7889 SZONE_UNLOCK(szone
);
7894 purgeable_pressure_relief(szone_t
*szone
, size_t goal
)
7896 return szone_pressure_relief(szone
, goal
) + szone_pressure_relief(szone
->helper_zone
, goal
);
7899 static const struct malloc_introspection_t purgeable_introspect
= {
7900 (void *)purgeable_ptr_in_use_enumerator
,
7901 (void *)purgeable_good_size
,
7902 (void *)purgeable_check
,
7903 (void *)purgeable_print
,
7905 (void *)purgeable_force_lock
,
7906 (void *)purgeable_force_unlock
,
7907 (void *)purgeable_statistics
,
7908 (void *)purgeable_locked
,
7909 NULL
, NULL
, NULL
, NULL
, /* Zone enumeration version 7 and forward. */
7910 }; // marked as const to spare the DATA section
7912 __private_extern__ malloc_zone_t
*
7913 create_purgeable_zone(size_t initial_size
, malloc_zone_t
*malloc_default_zone
, unsigned debug_flags
)
7916 uint64_t hw_memsize
= 0;
7918 /* get memory for the zone. */
7919 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, 0, VM_MEMORY_MALLOC
);
7923 /* set up the szone structure */
7925 #warning LOG enabled
7926 szone
->log_address
= ~0;
7929 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7930 hw_memsize
= *(uint64_t *)(uintptr_t)_COMM_PAGE_MEMORY_SIZE
;
7932 size_t uint64_t_size
= sizeof(hw_memsize
);
7934 sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0);
7936 /* Purgeable zone does not participate in the adaptive "largemem" sizing. */
7937 szone
->is_largemem
= 0;
7938 szone
->large_threshold
= LARGE_THRESHOLD
;
7939 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7942 szone
->large_entry_cache_reserve_limit
=
7943 hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
7945 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7946 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
7947 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
7948 szone
->large_legacy_reset_mprotect
= TRUE
;
7950 szone
->large_legacy_reset_mprotect
= FALSE
;
7953 szone
->basic_zone
.version
= 8;
7954 szone
->basic_zone
.size
= (void *)purgeable_size
;
7955 szone
->basic_zone
.malloc
= (void *)purgeable_malloc
;
7956 szone
->basic_zone
.calloc
= (void *)purgeable_calloc
;
7957 szone
->basic_zone
.valloc
= (void *)purgeable_valloc
;
7958 szone
->basic_zone
.free
= (void *)purgeable_free
;
7959 szone
->basic_zone
.realloc
= (void *)purgeable_realloc
;
7960 szone
->basic_zone
.destroy
= (void *)purgeable_destroy
;
7961 szone
->basic_zone
.batch_malloc
= (void *)purgeable_batch_malloc
;
7962 szone
->basic_zone
.batch_free
= (void *)purgeable_batch_free
;
7963 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&purgeable_introspect
;
7964 szone
->basic_zone
.memalign
= (void *)purgeable_memalign
;
7965 szone
->basic_zone
.free_definite_size
= (void *)purgeable_free_definite_size
;
7966 szone
->basic_zone
.pressure_relief
= (void *)purgeable_pressure_relief
;
7968 szone
->basic_zone
.reserved1
= 0; /* Set to zero once and for all as required by CFAllocator. */
7969 szone
->basic_zone
.reserved2
= 0; /* Set to zero once and for all as required by CFAllocator. */
7970 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
); /* Prevent overwriting the function pointers in basic_zone. */
7972 szone
->debug_flags
= debug_flags
| SCALABLE_MALLOC_PURGEABLE
;
7974 /* Purgeable zone does not support SCALABLE_MALLOC_ADD_GUARD_PAGES. */
7975 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
7976 _malloc_printf(ASL_LEVEL_INFO
, "purgeable zone does not support guard pages\n");
7977 szone
->debug_flags
&= ~SCALABLE_MALLOC_ADD_GUARD_PAGES
;
7980 LOCK_INIT(szone
->large_szone_lock
);
7982 szone
->helper_zone
= (struct szone_s
*)malloc_default_zone
;
7984 CHECK(szone
, __PRETTY_FUNCTION__
);
7985 return (malloc_zone_t
*)szone
;
7989 * For use by CheckFix: create a new zone whose behavior is, apart from
7990 * the use of death-row and per-CPU magazines, that of Leopard.
7992 static NOINLINE
void *
7993 legacy_valloc(szone_t
*szone
, size_t size
)
7998 num_pages
= round_page(size
) >> vm_page_shift
;
7999 ptr
= large_malloc(szone
, num_pages
, 0, TRUE
);
8001 if (LOG(szone
, ptr
))
8002 malloc_printf("legacy_valloc returned %p\n", ptr
);
8007 __private_extern__ malloc_zone_t
*
8008 create_legacy_scalable_zone(size_t initial_size
, unsigned debug_flags
)
8010 malloc_zone_t
*mzone
= create_scalable_zone(initial_size
, debug_flags
);
8011 szone_t
*szone
= (szone_t
*)mzone
;
8016 szone
->is_largemem
= 0;
8017 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
8018 szone
->large_threshold
= LARGE_THRESHOLD
;
8019 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
8021 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
| PROT_WRITE
);
8022 szone
->basic_zone
.valloc
= (void *)legacy_valloc
;
8023 szone
->basic_zone
.free_definite_size
= NULL
;
8024 mprotect(szone
, sizeof(szone
->basic_zone
), PROT_READ
);
8029 /********* Support code for emacs unexec ************/
8031 /* History of freezedry version numbers:
8033 * 1) Old malloc (before the scalable malloc implementation in this file
8035 * 2) Original freezedrying code for scalable malloc. This code was apparently
8036 * based on the old freezedrying code and was fundamentally flawed in its
8037 * assumption that tracking allocated memory regions was adequate to fake
8038 * operations on freezedried memory. This doesn't work, since scalable
8039 * malloc does not store flags in front of large page-aligned allocations.
8040 * 3) Original szone-based freezedrying code.
8041 * 4) Fresher malloc with tiny zone
8042 * 5) 32/64bit compatible malloc
8043 * 6) Metadata within 1MB and 8MB region for tiny and small
8045 * No version backward compatibility is provided, but the version number does
8046 * make it possible for malloc_jumpstart() to return an error if the application
8047 * was freezedried with an older version of malloc.
8049 #define MALLOC_FREEZEDRY_VERSION 6
8058 frozen_malloc(szone_t
*zone
, size_t new_size
)
8060 return malloc(new_size
);
8064 frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
)
8066 return calloc(num_items
, size
);
8070 frozen_valloc(szone_t
*zone
, size_t new_size
)
8072 return valloc(new_size
);
8076 frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
)
8078 size_t old_size
= szone_size(zone
, ptr
);
8081 if (new_size
<= old_size
) {
8084 new_ptr
= malloc(new_size
);
8086 memcpy(new_ptr
, ptr
, old_size
);
8092 frozen_free(szone_t
*zone
, void *ptr
)
8097 frozen_destroy(szone_t
*zone
)
8101 /********* Pseudo-private API for emacs unexec ************/
8104 * malloc_freezedry() records all of the szones in use, so that they can be
8105 * partially reconstituted by malloc_jumpstart(). Due to the differences
8106 * between reconstituted memory regions and those created by the szone code,
8107 * care is taken not to reallocate from the freezedried memory, except in the
8108 * case of a non-growing realloc().
8110 * Due to the flexibility provided by the zone registration mechanism, it is
8111 * impossible to implement generic freezedrying for any zone type. This code
8112 * only handles applications that use the szone allocator, so malloc_freezedry()
8113 * returns 0 (error) if any non-szone zones are encountered.
8117 malloc_freezedry(void)
8119 extern unsigned malloc_num_zones
;
8120 extern malloc_zone_t
**malloc_zones
;
8121 malloc_frozen
*data
;
8124 /* Allocate space in which to store the freezedry state. */
8125 data
= (malloc_frozen
*) malloc(sizeof(malloc_frozen
));
8127 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
8128 data
->version
= MALLOC_FREEZEDRY_VERSION
;
8130 /* Allocate the array of szone pointers. */
8131 data
->nszones
= malloc_num_zones
;
8132 data
->szones
= (szone_t
*) calloc(malloc_num_zones
, sizeof(szone_t
));
8135 * Fill in the array of szone structures. They are copied rather than
8136 * referenced, since the originals are likely to be clobbered during malloc
8139 for (i
= 0; i
< malloc_num_zones
; i
++) {
8140 if (strcmp(malloc_zones
[i
]->zone_name
, "DefaultMallocZone")) {
8141 /* Unknown zone type. */
8146 memcpy(&data
->szones
[i
], malloc_zones
[i
], sizeof(szone_t
));
8149 return((uintptr_t)data
);
8153 malloc_jumpstart(uintptr_t cookie
)
8155 malloc_frozen
*data
= (malloc_frozen
*)cookie
;
8158 if (data
->version
!= MALLOC_FREEZEDRY_VERSION
) {
8159 /* Unsupported freezedry version. */
8163 for (i
= 0; i
< data
->nszones
; i
++) {
8164 /* Set function pointers. Even the functions that stay the same must be
8165 * set, since there are no guarantees that they will be mapped to the
8166 * same addresses. */
8167 data
->szones
[i
].basic_zone
.size
= (void *) szone_size
;
8168 data
->szones
[i
].basic_zone
.malloc
= (void *) frozen_malloc
;
8169 data
->szones
[i
].basic_zone
.calloc
= (void *) frozen_calloc
;
8170 data
->szones
[i
].basic_zone
.valloc
= (void *) frozen_valloc
;
8171 data
->szones
[i
].basic_zone
.free
= (void *) frozen_free
;
8172 data
->szones
[i
].basic_zone
.realloc
= (void *) frozen_realloc
;
8173 data
->szones
[i
].basic_zone
.destroy
= (void *) frozen_destroy
;
8174 data
->szones
[i
].basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
8176 /* Register the freezedried zone. */
8177 malloc_zone_register(&data
->szones
[i
].basic_zone
);