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/sysctl.h>
57 #include <libkern/OSAtomic.h>
58 #include <mach-o/dyld.h> /* for NSVersionOfLinkTimeLibrary() */
60 /********************* DEFINITIONS ************************/
62 #define DEBUG_MALLOC 0 // set to one to debug malloc itself
64 #define DEBUG_CLIENT 0 // set to one to debug malloc client
67 #warning DEBUG_MALLOC ENABLED
70 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) \
72 if (__is_threaded && TRY_LOCK(mag_ptr->magazine_lock)) { \
73 malloc_printf("*** magazine_lock was not set %p in %s\n", \
74 mag_ptr->magazine_lock, fun); \
78 # define INLINE __inline__
79 # define ALWAYSINLINE __attribute__((always_inline))
80 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) {}
83 # define NOINLINE __attribute__((noinline))
85 #if defined(__i386__) || defined(__x86_64__)
86 #define CACHE_ALIGN __attribute__ ((aligned (128) )) /* Future-proofing at 128B */
87 #elif defined(__ppc__) || defined(__ppc64__)
88 #define CACHE_ALIGN __attribute__ ((aligned (128) ))
94 * Access to global variables is slow, so optimise our handling of vm_page_size
97 #define _vm_page_size vm_page_size /* to get to the originals */
98 #define _vm_page_shift vm_page_shift
99 #define vm_page_size 4096 /* our normal working sizes */
100 #define vm_page_shift 12
103 * msize - a type to refer to the number of quanta of a tiny or small
104 * allocation. A tiny block with an msize of 3 would be 3 << SHIFT_TINY_QUANTUM
107 typedef unsigned short msize_t
;
119 typedef unsigned int grain_t
; // N.B. wide enough to index all free slots
121 typedef int mag_index_t
;
123 #define CHECK_REGIONS (1 << 31)
125 #define MAX_RECORDER_BUFFER 256
127 /********************* DEFINITIONS for tiny ************************/
130 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the
131 * szone's hashed_regions pointer.
133 * Each region is laid out as a heap, followed by a header block, all within
134 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header
135 * is 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
137 * The header block is arranged as in struct tiny_region defined just below, and
138 * consists of two bitfields (or bit arrays) interleaved 32 bits by 32 bits.
140 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding
141 * TINY_QUANTUM block within the heap.
143 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
144 * that the corresponding quantum is the first quantum in a block (either in use or free). The
145 * in-use bit is set for the header if the block has been handed out (allocated). If the header
146 * bit is not set, the in-use bit is invalid.
148 * The szone maintains an array of NUM_TINY_SLOTS freelists, each of which is used to hold
149 * free objects of the corresponding quantum size.
151 * A free block is laid out depending on its size, in order to fit all free
152 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
153 * not store their size in the block, instead relying on the header information
154 * to determine their size. Blocks of two or more quanta have room to store
155 * their size in the block, and store it both after the 'next' pointer, and in
156 * the last 2 bytes of the block.
159 * Offset (32-bit mode) (64-bit mode)
165 * Offset (32-bit mode) (64-bit mode)
168 * 0x8 0x10 : size (in quantum counts)
169 * end - 2 end - 2 : size (in quantum counts)
172 * All fields are pointer-sized, except for the size which is an unsigned short.
176 #define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
177 #define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
179 #define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
182 #define NUM_TINY_SLOTS 64 // number of slots for free-lists
184 #define NUM_TINY_SLOTS 32 // number of slots for free-lists
187 #define NUM_TINY_BLOCKS 64520
188 #define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
189 #define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
190 #define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM) // 20
193 * Enough room for the data, followed by the bit arrays (2-bits per block)
194 * plus rounding to the nearest page.
196 #define CEIL_NUM_TINY_BLOCKS_WORDS (((NUM_TINY_BLOCKS + 31) & ~31) >> 5)
197 #define TINY_METADATA_SIZE (sizeof(region_trailer_t) + sizeof(tiny_header_inuse_pair_t) * CEIL_NUM_TINY_BLOCKS_WORDS)
198 #define TINY_REGION_SIZE \
199 ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
201 #define TINY_METADATA_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
204 * Beginning and end pointers for a region's heap.
206 #define TINY_REGION_ADDRESS(region) ((void *)(region))
207 #define TINY_REGION_END(region) ((void *)(((uintptr_t)(region)) + (NUM_TINY_BLOCKS * TINY_QUANTUM)))
210 * Locate the heap base for a pointer known to be within a tiny region.
212 #define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
215 * Convert between byte and msize units.
217 #define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
218 #define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
221 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
223 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
225 #define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
228 * Layout of a tiny region
230 typedef uint32_t tiny_block_t
[4]; // assert(TINY_QUANTUM == sizeof(tiny_block_t))
232 typedef struct tiny_header_inuse_pair
236 } tiny_header_inuse_pair_t
;
238 typedef struct region_trailer
240 struct region_trailer
*prev
;
241 struct region_trailer
*next
;
242 boolean_t recirc_suitable
;
244 mag_index_t mag_index
;
247 typedef struct tiny_region
249 tiny_block_t blocks
[NUM_TINY_BLOCKS
];
251 region_trailer_t trailer
;
253 // The interleaved bit arrays comprising the header and inuse bitfields.
254 // The unused bits of each component in the last pair will be initialized to sentinel values.
255 tiny_header_inuse_pair_t pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
];
257 uint8_t pad
[TINY_REGION_SIZE
- (NUM_TINY_BLOCKS
* sizeof(tiny_block_t
)) - TINY_METADATA_SIZE
];
261 * Per-region meta data for tiny allocator
263 #define REGION_TRAILER_FOR_TINY_REGION(r) (&(((tiny_region_t)(r))->trailer))
264 #define MAGAZINE_INDEX_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->mag_index)
265 #define BYTES_USED_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->bytes_used)
268 * Locate the block header for a pointer known to be within a tiny region.
270 #define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)&(((tiny_region_t)TINY_REGION_FOR_PTR(_p))->pairs))
273 * Locate the inuse map for a given block header pointer.
275 #define TINY_INUSE_FOR_HEADER(_h) ((void *)&(((tiny_header_inuse_pair_t *)(_h))->inuse))
278 * Compute the bitmap index for a pointer known to be within a tiny region.
280 #define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
282 #define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
285 #warning TINY_CACHE turned off
288 #define TINY_REGION_PAYLOAD_BYTES (NUM_TINY_BLOCKS * TINY_QUANTUM)
290 /********************* DEFINITIONS for small ************************/
293 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's hashed_regions
296 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
297 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
298 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
301 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
302 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
304 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
305 * of the corresponding quantum size.
307 * A free block is laid out as:
309 * Offset (32-bit mode) (64-bit mode)
312 * 0x8 0x10 : size (in quantum counts)
313 * end - 2 end - 2 : size (in quantum counts)
316 * All fields are pointer-sized, except for the size which is an unsigned short.
320 #define SMALL_IS_FREE (1 << 15)
322 #define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
323 #define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
325 #define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
328 * The number of slots in the free-list for small blocks. To avoid going to
329 * vm system as often on large memory machines, increase the number of free list
330 * spots above some amount of RAM installed in the system.
332 #define NUM_SMALL_SLOTS 32
333 #define NUM_SMALL_SLOTS_LARGEMEM 256
334 #define SMALL_BITMAP_WORDS 8
337 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
338 * convention msize=0 => msize = (1<<15)
340 #define NUM_SMALL_BLOCKS 16320
341 #define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
342 #define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
343 #define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
345 #define SMALL_METADATA_SIZE (sizeof(region_trailer_t) + NUM_SMALL_BLOCKS * sizeof(msize_t))
346 #define SMALL_REGION_SIZE \
347 ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
349 #define SMALL_METADATA_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
352 * Beginning and end pointers for a region's heap.
354 #define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
355 #define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
358 * Locate the heap base for a pointer known to be within a small region.
360 #define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
363 * Convert between byte and msize units.
365 #define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
366 #define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
368 #define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
371 * Layout of a small region
373 typedef uint32_t small_block_t
[SMALL_QUANTUM
/sizeof(uint32_t)];
375 typedef struct small_region
377 small_block_t blocks
[NUM_SMALL_BLOCKS
];
379 region_trailer_t trailer
;
381 msize_t small_meta_words
[NUM_SMALL_BLOCKS
];
383 uint8_t pad
[SMALL_REGION_SIZE
- (NUM_SMALL_BLOCKS
* sizeof(small_block_t
)) - SMALL_METADATA_SIZE
];
387 * Per-region meta data for small allocator
389 #define REGION_TRAILER_FOR_SMALL_REGION(r) (&(((small_region_t)(r))->trailer))
390 #define MAGAZINE_INDEX_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->mag_index)
391 #define BYTES_USED_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->bytes_used)
394 * Locate the metadata base for a pointer known to be within a small region.
396 #define SMALL_META_HEADER_FOR_PTR(_p) (((small_region_t)SMALL_REGION_FOR_PTR(_p))->small_meta_words)
399 * Compute the metadata index for a pointer known to be within a small region.
401 #define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
404 * Find the metadata word for a pointer known to be within a small region.
406 #define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
409 * Determine whether a pointer known to be within a small region points to memory which is free.
411 #define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
414 * Extract the msize value for a pointer known to be within a small region.
416 #define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
418 #define PROTECT_SMALL 0 // Should be 0: 1 is too slow for normal use
420 #define SMALL_CACHE 1
422 #warning SMALL_CACHE turned off
425 #define SMALL_REGION_PAYLOAD_BYTES (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
427 /************************* DEFINITIONS for large ****************************/
429 #define LARGE_THRESHOLD (15 * 1024) // strictly above this use "large"
430 #define LARGE_THRESHOLD_LARGEMEM (127 * 1024)
432 #if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
433 #error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
436 #if (LARGE_THRESHOLD_LARGEMEM > NUM_SMALL_SLOTS_LARGEMEM * SMALL_QUANTUM)
437 #error LARGE_THRESHOLD_LARGEMEM should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
441 * When all memory is touched after a copy, vm_copy() is always a lose
442 * But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages
445 * This must be larger than LARGE_THRESHOLD
447 #define VM_COPY_THRESHOLD (40 * 1024)
448 #define VM_COPY_THRESHOLD_LARGEMEM (128 * 1024)
451 vm_address_t address
;
453 boolean_t did_madvise_reusable
;
456 #if !TARGET_OS_EMBEDDED
457 #define LARGE_CACHE 1
459 #define LARGE_CACHE 0
462 #warning LARGE_CACHE turned off
464 #if defined(__LP64__)
465 #define LARGE_ENTRY_CACHE_SIZE 16
466 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x80000000) /* 2Gb */
468 #define LARGE_ENTRY_CACHE_SIZE 8
469 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x02000000) /* 32Mb */
471 #define LARGE_CACHE_SIZE_ENTRY_LIMIT (LARGE_CACHE_SIZE_LIMIT/LARGE_ENTRY_CACHE_SIZE)
473 /*******************************************************************************
474 * Definitions for region hash
475 ******************************************************************************/
477 typedef void * region_t
;
478 typedef region_t
* rgnhdl_t
; /* A pointer into hashed_regions array. */
480 #define INITIAL_NUM_REGIONS_SHIFT 6 // log2(INITIAL_NUM_REGIONS)
481 #define INITIAL_NUM_REGIONS (1 << INITIAL_NUM_REGIONS_SHIFT) // Must be a power of 2!
482 #define HASHRING_OPEN_ENTRY ((region_t) 0) // Initial value and sentinel marking end of collision chain
483 #define HASHRING_REGION_DEALLOCATED ((region_t)-1) // Region at this slot reclaimed by OS
484 #define HASH_BLOCKS_ALIGN TINY_BLOCKS_ALIGN // MIN( TINY_BLOCKS_ALIGN, SMALL_BLOCKS_ALIGN, ... )
486 typedef struct region_hash_generation
{
487 size_t num_regions_allocated
;
488 size_t num_regions_allocated_shift
; // log2(num_regions_allocated)
489 region_t
*hashed_regions
; // hashed by location
490 struct region_hash_generation
*nextgen
;
491 } region_hash_generation_t
;
493 /*******************************************************************************
494 * Per-processor magazine for tiny and small allocators
495 ******************************************************************************/
497 typedef struct { // vm_allocate()'d, so the array of magazines is page-aligned to begin with.
498 // Take magazine_lock first, Depot lock when needed for recirc, then szone->{tiny,small}_regions_lock when needed for alloc
499 pthread_lock_t magazine_lock CACHE_ALIGN
;
501 // One element deep "death row", optimizes malloc/free/malloc for identical size.
502 void *mag_last_free
; // low SHIFT_{TINY,SMALL}_QUANTUM bits indicate the msize
503 region_t mag_last_free_rgn
; // holds the region for mag_last_free
505 free_list_t
*mag_free_list
[256]; // assert( 256 >= MAX( NUM_TINY_SLOTS, NUM_SMALL_SLOTS_LARGEMEM ))
506 unsigned mag_bitmap
[8]; // assert( sizeof(mag_bitmap) << 3 >= sizeof(mag_free_list)/sizeof(free_list_t) )
508 // the last free region in the last block is treated as a big block in use that is not accounted for
509 size_t mag_bytes_free_at_end
;
510 region_t mag_last_region
; // Valid iff mag_bytes_free_at_end > 0
513 unsigned mag_num_objects
;
514 size_t mag_num_bytes_in_objects
;
515 size_t num_bytes_in_magazine
;
517 // recirculation list -- invariant: all regions owned by this magazine that meet the emptiness criteria
518 // are located nearer to the head of the list than any region that doesn't satisfy that criteria.
519 // Doubly linked list for efficient extraction.
520 unsigned recirculation_entries
;
521 region_trailer_t
*firstNode
;
522 region_trailer_t
*lastNode
;
525 uint64_t pad
[49]; // So sizeof(magazine_t) is 2560 bytes. FIXME: assert this at compile time
527 uint32_t pad
[45]; // So sizeof(magazine_t) is 1280 bytes. FIXME: assert this at compile time
531 #define TINY_MAX_MAGAZINES 16 /* MUST BE A POWER OF 2! */
532 #define TINY_MAGAZINE_PAGED_SIZE \
533 (((sizeof(magazine_t) * (TINY_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
534 ~ (vm_page_size - 1)) /* + 1 for the Depot */
536 #define SMALL_MAX_MAGAZINES 16 /* MUST BE A POWER OF 2! */
537 #define SMALL_MAGAZINE_PAGED_SIZE \
538 (((sizeof(magazine_t) * (SMALL_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
539 ~ (vm_page_size - 1)) /* + 1 for the Depot */
541 #define DEPOT_MAGAZINE_INDEX -1
543 /****************************** zone itself ***********************************/
546 * Note that objects whose adddress are held in pointers here must be pursued
547 * individually in the {tiny,small}_in_use_enumeration() routines. See for
548 * example the treatment of region_hash_generation and tiny_magazines below.
551 typedef struct szone_s
{ // vm_allocate()'d, so page-aligned to begin with.
552 malloc_zone_t basic_zone
;
553 pthread_key_t cpu_id_key
;
554 unsigned debug_flags
;
557 /* Regions for tiny objects */
558 pthread_lock_t tiny_regions_lock CACHE_ALIGN
;
559 size_t num_tiny_regions
;
560 size_t num_tiny_regions_dealloc
;
561 region_hash_generation_t
*tiny_region_generation
;
562 region_hash_generation_t trg
[2];
564 int num_tiny_magazines
;
565 unsigned num_tiny_magazines_mask
;
566 int num_tiny_magazines_mask_shift
;
567 magazine_t
*tiny_magazines
; // array of per-processor magazines
569 #if TARGET_OS_EMBEDDED
570 uintptr_t last_tiny_advise
;
573 /* Regions for small objects */
574 pthread_lock_t small_regions_lock CACHE_ALIGN
;
575 size_t num_small_regions
;
576 size_t num_small_regions_dealloc
;
577 region_hash_generation_t
*small_region_generation
;
578 region_hash_generation_t srg
[2];
580 unsigned num_small_slots
; // determined by physmem size
582 int num_small_magazines
;
583 unsigned num_small_magazines_mask
;
584 int num_small_magazines_mask_shift
;
585 magazine_t
*small_magazines
; // array of per-processor magazines
587 #if TARGET_OS_EMBEDDED
588 uintptr_t last_small_advise
;
591 /* large objects: all the rest */
592 pthread_lock_t large_szone_lock CACHE_ALIGN
; // One customer at a time for large
593 unsigned num_large_objects_in_use
;
594 unsigned num_large_entries
;
595 large_entry_t
*large_entries
; // hashed by location; null entries don't count
596 size_t num_bytes_in_large_objects
;
599 int large_entry_cache_oldest
;
600 int large_entry_cache_newest
;
601 large_entry_t large_entry_cache
[LARGE_ENTRY_CACHE_SIZE
]; // "death row" for large malloc/free
602 boolean_t large_legacy_reset_mprotect
;
603 size_t large_entry_cache_hoard_bytes
;
604 size_t large_entry_cache_hoard_lmit
;
607 /* flag and limits pertaining to altered malloc behavior for systems with
608 large amounts of physical memory */
609 unsigned is_largemem
;
610 unsigned large_threshold
;
611 unsigned vm_copy_threshold
;
613 /* security cookie */
616 /* Initial region list */
617 region_t initial_tiny_regions
[INITIAL_NUM_REGIONS
];
618 region_t initial_small_regions
[INITIAL_NUM_REGIONS
];
620 /* The purgeable zone constructed by create_purgeable_zone() would like to hand off tiny and small
621 * allocations to the default scalable zone. Record the latter as the "helper" zone here. */
622 struct szone_s
*helper_zone
;
625 #define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
627 #if DEBUG_MALLOC || DEBUG_CLIENT
628 static void szone_sleep(void);
630 __private_extern__
void malloc_error_break(void);
632 // msg prints after fmt, ...
633 static NOINLINE
void szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
636 static void protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
);
637 static void *allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
,
639 static void deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
);
640 #if TARGET_OS_EMBEDDED
641 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
, uintptr_t *last
);
643 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
);
645 static kern_return_t
_szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
);
647 static INLINE mag_index_t
mag_get_thread_index(szone_t
*szone
) ALWAYSINLINE
;
648 static magazine_t
*mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
,
649 mag_index_t mag_index
);
651 static INLINE rgnhdl_t
hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
)
653 static void hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
);
654 static region_t
*hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
);
655 static region_t
*hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
,
656 size_t *mutable_shift
, size_t *new_size
);
658 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
) ALWAYSINLINE
;
659 static INLINE
uintptr_t free_list_checksum_ptr(szone_t
*szone
, void *p
) ALWAYSINLINE
;
660 static INLINE
void *free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
) ALWAYSINLINE
;
661 static unsigned free_list_count(szone_t
*szone
, free_list_t
*ptr
);
663 static INLINE
void recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
664 static INLINE
void recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
665 static INLINE
void recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
667 static INLINE
void BITARRAY_SET(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
668 static INLINE
void BITARRAY_CLR(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
669 static INLINE boolean_t
BITARRAY_BIT(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
671 static msize_t
get_tiny_free_size(const void *ptr
);
672 static msize_t
get_tiny_previous_free_msize(const void *ptr
);
673 static INLINE msize_t
get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
) ALWAYSINLINE
;
674 static INLINE
void set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
675 static INLINE
void set_tiny_meta_header_in_use_1(const void *ptr
) ALWAYSINLINE
;
676 static INLINE
void set_tiny_meta_header_middle(const void *ptr
) ALWAYSINLINE
;
677 static INLINE
void set_tiny_meta_header_free(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
678 static INLINE boolean_t
tiny_meta_header_is_free(const void *ptr
) ALWAYSINLINE
;
679 static INLINE
void *tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
) ALWAYSINLINE
;
681 static void tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
682 static void tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
683 static INLINE region_t
tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
685 static void tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
);
686 static int tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
687 static size_t tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
688 static void tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
689 static void tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
690 static void tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
);
691 static boolean_t
tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
);
693 static INLINE
void tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
,
694 void *ptr
, msize_t msize
) ALWAYSINLINE
;
695 static void *tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
697 static boolean_t
tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
698 static boolean_t
tiny_check_region(szone_t
*szone
, region_t region
);
699 static kern_return_t
tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
700 memory_reader_t reader
, vm_range_recorder_t recorder
);
701 static void *tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
703 static INLINE
void *tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
704 static INLINE
void free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
) ALWAYSINLINE
;
705 static void print_tiny_free_list(szone_t
*szone
);
706 static void print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
);
707 static boolean_t
tiny_free_list_check(szone_t
*szone
, grain_t slot
);
709 static INLINE
void small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
) ALWAYSINLINE
;
710 static INLINE
void small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
) ALWAYSINLINE
;
711 static INLINE
void small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
) ALWAYSINLINE
;
712 static void small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
713 static void small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
714 static INLINE region_t
small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
716 static void small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
);
717 static int small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
718 static size_t small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
719 static void small_free_scan_depot_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
720 static void small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
721 static void small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
);
722 static boolean_t
small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
);
723 static INLINE
void small_free_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, region_t region
,
724 void *ptr
, msize_t msize
) ALWAYSINLINE
;
725 static void *small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
727 static boolean_t
small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
728 static boolean_t
small_check_region(szone_t
*szone
, region_t region
);
729 static kern_return_t
small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
730 memory_reader_t reader
, vm_range_recorder_t recorder
);
731 static void *small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
733 static INLINE
void *small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
734 static INLINE
void free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
) ALWAYSINLINE
;
735 static void print_small_free_list(szone_t
*szone
);
736 static void print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
);
737 static boolean_t
small_free_list_check(szone_t
*szone
, grain_t grain
);
740 static void large_debug_print(szone_t
*szone
);
742 static large_entry_t
*large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
);
743 static void large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
);
744 static INLINE
void large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
) ALWAYSINLINE
;
745 static INLINE large_entry_t
*large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
) ALWAYSINLINE
;
746 static void large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
,
747 vm_range_t
*range_to_deallocate
);
748 static large_entry_t
*large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
);
749 static vm_range_t
large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
);
750 static NOINLINE kern_return_t
large_in_use_enumerator(task_t task
, void *context
,
751 unsigned type_mask
, vm_address_t large_entries_address
,
752 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
);
753 static void *large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
, boolean_t cleared_requested
);
754 static NOINLINE
void free_large(szone_t
*szone
, void *ptr
);
755 static INLINE
int large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
758 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
760 static NOINLINE
void szone_free(szone_t
*szone
, void *ptr
);
761 static NOINLINE
void *szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
);
762 static NOINLINE
void *szone_malloc(szone_t
*szone
, size_t size
);
763 static NOINLINE
void *szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
764 static NOINLINE
void *szone_valloc(szone_t
*szone
, size_t size
);
765 static NOINLINE
size_t szone_size_try_large(szone_t
*szone
, const void *ptr
);
766 static NOINLINE
size_t szone_size(szone_t
*szone
, const void *ptr
);
767 static NOINLINE
void *szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
768 static NOINLINE
void *szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
769 static NOINLINE
void szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
770 static NOINLINE
unsigned szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
771 static NOINLINE
void szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
772 static void szone_destroy(szone_t
*szone
);
773 static NOINLINE
size_t szone_good_size(szone_t
*szone
, size_t size
);
775 static NOINLINE boolean_t
szone_check_all(szone_t
*szone
, const char *function
);
776 static boolean_t
szone_check(szone_t
*szone
);
777 static kern_return_t
szone_ptr_in_use_enumerator(task_t task
, void *context
,
778 unsigned type_mask
, vm_address_t zone_address
,
779 memory_reader_t reader
, vm_range_recorder_t recorder
);
780 static NOINLINE
void szone_print(szone_t
*szone
, boolean_t verbose
);
781 static void szone_log(malloc_zone_t
*zone
, void *log_address
);
782 static void szone_force_lock(szone_t
*szone
);
783 static void szone_force_unlock(szone_t
*szone
);
784 static boolean_t
szone_locked(szone_t
*szone
);
786 static void szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
788 static void purgeable_free(szone_t
*szone
, void *ptr
);
789 static void *purgeable_malloc(szone_t
*szone
, size_t size
);
790 static void *purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
791 static void *purgeable_valloc(szone_t
*szone
, size_t size
);
792 static size_t purgeable_size(szone_t
*szone
, const void *ptr
);
793 static void *purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
794 static void *purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
795 static void purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
796 static unsigned purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
797 static void purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
798 static void purgeable_destroy(szone_t
*szone
);
799 static size_t purgeable_good_size(szone_t
*szone
, size_t size
);
801 static boolean_t
purgeable_check(szone_t
*szone
);
802 static kern_return_t
purgeable_ptr_in_use_enumerator(task_t task
, void *context
,
803 unsigned type_mask
, vm_address_t zone_address
,
804 memory_reader_t reader
, vm_range_recorder_t recorder
);
805 static void purgeable_print(szone_t
*szone
, boolean_t verbose
);
806 static void purgeable_log(malloc_zone_t
*zone
, void *log_address
);
807 static void purgeable_force_lock(szone_t
*szone
);
808 static void purgeable_force_unlock(szone_t
*szone
);
809 static boolean_t
purgeable_locked(szone_t
*szone
);
811 static void purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
813 static void *frozen_malloc(szone_t
*zone
, size_t new_size
);
814 static void *frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
);
815 static void *frozen_valloc(szone_t
*zone
, size_t new_size
);
816 static void *frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
);
817 static void frozen_free(szone_t
*zone
, void *ptr
);
818 static void frozen_destroy(szone_t
*zone
);
820 #define SZONE_LOCK(szone) \
822 LOCK(szone->large_szone_lock); \
825 #define SZONE_UNLOCK(szone) \
827 UNLOCK(szone->large_szone_lock); \
830 #define SZONE_TRY_LOCK(szone) \
831 TRY_LOCK(szone->large_szone_lock);
833 #define SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr) \
835 LOCK(mag_ptr->magazine_lock); \
838 #define SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr) \
840 UNLOCK(mag_ptr->magazine_lock); \
843 #define SZONE_MAGAZINE_PTR_TRY_LOCK(szone, mag_ptr) \
844 TRY_LOCK(mag_ptr->magazine_lock);
847 # define LOG(szone,ptr) \
848 (szone->log_address && (((uintptr_t)szone->log_address == -1) || \
849 (szone->log_address == (void *)(ptr))))
851 # define LOG(szone,ptr) 0
854 #if DEBUG_MALLOC || DEBUG_CLIENT
855 # define CHECK(szone,fun) \
856 if ((szone)->debug_flags & CHECK_REGIONS) \
857 szone_check_all(szone, fun)
859 # define CHECK(szone,fun) \
863 /********************* VERY LOW LEVEL UTILITIES ************************/
865 #if DEBUG_MALLOC || DEBUG_CLIENT
870 if (getenv("MallocErrorSleep")) {
871 _malloc_printf(ASL_LEVEL_NOTICE
, "*** sleeping to help debug\n");
872 sleep(3600); // to help debug
877 extern const char *__crashreporter_info__
;
879 // msg prints after fmt, ...
881 szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
884 _SIMPLE_STRING b
= _simple_salloc();
886 if (szone
) SZONE_UNLOCK(szone
); // FIXME: unlock magazine and region locks?
890 _simple_vsprintf(b
, fmt
, ap
);
894 _simple_sprintf(b
, "*** error for object %p: %s\n", ptr
, msg
);
896 _simple_sprintf(b
, "*** error: %s\n", msg
);
898 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b
));
901 * Should only get here if vm_allocate() can't get a single page of
902 * memory, implying _simple_asl_log() would also fail. So we just
903 * print to the file descriptor.
907 _malloc_vprintf(MALLOC_PRINTF_NOLOG
, fmt
, ap
);
911 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error for object %p: %s\n", ptr
, msg
);
913 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error: %s\n", msg
);
915 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** set a breakpoint in malloc_error_break to debug\n");
917 malloc_error_break();
919 szone_print(szone
, 1);
925 // Call abort() if this is a memory corruption error and the abort on
926 // corruption flag is set, or if any error should abort.
927 if ((is_corruption
&& (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_CORRUPTION
)) ||
928 (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_ERROR
)) {
929 __crashreporter_info__
= b
? _simple_string(b
) : msg
;
937 protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
)
941 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_PRELUDE
)) {
942 err
= vm_protect(mach_task_self(), (vm_address_t
)(uintptr_t)address
- vm_page_size
, vm_page_size
, 0, protection
);
944 malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
945 protection
,(uintptr_t)address
- (1 << vm_page_shift
));
948 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE
)) {
949 err
= vm_protect(mach_task_self(), (vm_address_t
)(uintptr_t)address
+ size
, vm_page_size
, 0, protection
);
951 malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
952 protection
, (uintptr_t)address
+ size
);
958 allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
, int vm_page_label
)
960 // align specifies a desired alignment (as a log) or 0 if no alignment requested
962 uintptr_t addr
= 0, aligned_address
;
963 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
964 boolean_t purgeable
= debug_flags
& SCALABLE_MALLOC_PURGEABLE
;
965 size_t allocation_size
= round_page(size
);
967 int flags
= VM_MAKE_TAG(vm_page_label
);
969 if (align
) add_guard_pages
= 0; // too cumbersome to deal with that
970 if (!allocation_size
) allocation_size
= 1 << vm_page_shift
;
971 if (add_guard_pages
) allocation_size
+= 2 * (1 << vm_page_shift
);
972 if (align
) allocation_size
+= (size_t)1 << align
;
973 if (purgeable
) flags
|= VM_FLAGS_PURGABLE
;
975 if (allocation_size
< size
) // size_t arithmetic wrapped!
978 vm_addr
= mmap(0, allocation_size
, PROT_READ
| PROT_WRITE
, MAP_ANON
| MAP_PRIVATE
, flags
, 0);
979 if ((uintptr_t)vm_addr
== -1) {
980 szone_error(szone
, 0, "can't allocate region", NULL
, "*** mmap(size=%lu) failed (error code=%d)\n",
981 allocation_size
, errno
);
984 addr
= (uintptr_t)vm_addr
;
987 aligned_address
= (addr
+ ((uintptr_t)1 << align
) - 1) & ~ (((uintptr_t)1 << align
) - 1);
988 if (aligned_address
!= addr
) {
989 delta
= aligned_address
- addr
;
990 if (munmap((void *)addr
, delta
) == -1)
991 malloc_printf("*** munmap unaligned header failed with %d\n", errno
);
992 addr
= aligned_address
;
993 allocation_size
-= delta
;
995 if (allocation_size
> size
) {
996 if (munmap((void *)(addr
+ size
), allocation_size
- size
) == -1)
997 malloc_printf("*** munmap unaligned footer failed with %d\n", errno
);
1000 if (add_guard_pages
) {
1001 addr
+= (uintptr_t)1 << vm_page_shift
;
1002 protect((void *)addr
, size
, 0, debug_flags
);
1004 return (void *)addr
;
1008 deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
)
1011 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
1013 if (add_guard_pages
) {
1014 addr
= (void *)((uintptr_t)addr
- (1 << vm_page_shift
));
1015 size
+= 2 * (1 << vm_page_shift
);
1017 err
= munmap(addr
, size
);
1018 if ((err
== -1) && szone
)
1019 szone_error(szone
, 0, "Can't deallocate_pages region", addr
, NULL
);
1023 #if TARGET_OS_EMBEDDED
1024 madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
, uintptr_t *last
)
1026 madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
)
1030 size_t len
= pgHi
- pgLo
;
1033 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
)
1034 memset((void *)pgLo
, 0xed, len
); // Scribble on MADV_FREEd memory
1037 #if TARGET_OS_EMBEDDED
1046 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)r
, (void *)pgLo
, len
); // DTrace USDT Probe
1047 #if TARGET_OS_EMBEDDED
1048 if (-1 == madvise((void *)pgLo
, len
, MADV_FREE
)) {
1050 if (-1 == madvise((void *)pgLo
, len
, MADV_FREE_REUSABLE
)) {
1052 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
1054 szone_error(szone
, 1, "madvise_free_range madvise(..., MADV_FREE_REUSABLE) failed", (void *)pgLo
, NULL
);
1061 static kern_return_t
1062 _szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
)
1064 *ptr
= (void *)address
;
1068 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1069 // pthread_t's are page aligned, (sometimes even in ascending sequence). These hash well.
1070 // See Knuth TAOCP, Vol. 3.
1072 #define HASH_SELF() \
1073 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 11400714819323198549ULL) >> (64 - szone->num_tiny_magazines_mask_shift)
1075 #define HASH_SELF() \
1076 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 2654435761UL) >> (32 - szone->num_tiny_magazines_mask_shift)
1079 #if defined(__i386__) || defined(__x86_64__)
1080 #define __APPLE_API_PRIVATE
1081 #include <machine/cpu_capabilities.h>
1082 #define _COMM_PAGE_VERSION_REQD 9
1083 #undef __APPLE_API_PRIVATE
1086 * These commpage routines provide fast access to the logical cpu number
1087 * of the calling processor assuming no pre-emption occurs.
1089 #define CPU_NUMBER() (((int (*)()) _COMM_PAGE_CPU_NUMBER)()) /* Zero-based */
1091 static INLINE mag_index_t
1092 mag_get_thread_index(szone_t
*szone
)
1097 return CPU_NUMBER() & (TINY_MAX_MAGAZINES
- 1);
1100 #elif defined(__arm__)
1102 static INLINE mag_index_t
1103 mag_get_thread_index(szone_t
*szone
)
1109 #warning deriving magazine index from pthread_self() [want processor number]
1111 static INLINE mag_index_t
1112 mag_get_thread_index(szone_t
*szone
)
1116 else if ((pthread_key_t
) -1 == szone
->cpu_id_key
) { // In case pthread_key_create() failed.
1119 mag_index_t idx
= (mag_index_t
)(intptr_t)pthread_getspecific(szone
->cpu_id_key
);
1121 // Has this thread been hinted with a non-zero value [i.e. 1 + cpuid()] ?
1122 // If so, bump down the hint to a zero-based magazine index and return it.
1126 // No hint available. Contruct a magazine index for this thread ...
1129 // bump up the hint to exclude zero and try to memorize it ...
1130 pthread_setspecific(szone
->cpu_id_key
, (const void *)((uintptr_t)idx
+ 1));
1132 // and return the (zero-based) magazine index.
1140 mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
, mag_index_t mag_index
)
1142 mag_index_t refreshed_index
;
1143 magazine_t
*mag_ptr
= &(magazines
[mag_index
]);
1145 // Take the lock on entry.
1146 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1148 // Now in the time it took to acquire the lock, the region may have migrated
1149 // from one magazine to another. In which case the magazine lock we obtained
1150 // (namely magazines[mag_index].mag_lock) is stale. If so, keep on tryin' ...
1151 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
1153 SZONE_MAGAZINE_PTR_UNLOCK(szone
, mag_ptr
);
1155 mag_index
= refreshed_index
;
1156 mag_ptr
= &(magazines
[mag_index
]);
1157 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1163 /*******************************************************************************
1164 * Region hash implementation
1166 * This is essentially a duplicate of the existing Large allocator hash, minus
1167 * the ability to remove entries. The two should be combined eventually.
1168 ******************************************************************************/
1169 #pragma mark region hash
1172 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
1175 * FIXME: If consecutive queries of the same region are likely, a one-entry
1176 * cache would likely be a significant performance win here.
1178 static INLINE rgnhdl_t
1179 hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1180 size_t index
, hash_index
;
1186 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1187 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1188 // this hash works really well. See Knuth TAOCP, Vol. 3.
1190 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1192 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1195 entry
= regions
+ index
;
1200 if (++index
== num_entries
)
1202 } while (index
!= hash_index
);
1207 * hash_region_insert_no_lock - Insert a region into the hash ring.
1210 hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1211 size_t index
, hash_index
;
1214 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1215 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1216 // this hash works really well. See Knuth TAOCP, Vol. 3.
1218 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1220 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1223 entry
= regions
+ index
;
1224 if (*entry
== HASHRING_OPEN_ENTRY
|| *entry
== HASHRING_REGION_DEALLOCATED
) {
1228 if (++index
== num_entries
)
1230 } while (index
!= hash_index
);
1234 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
1235 * must be a VM allocation as to avoid recursing between allocating a new small
1236 * region, and asking the small region to allocate space for the new list of
1240 hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
)
1242 size_t size
= num_entries
* sizeof(region_t
);
1244 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC
);
1248 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
1249 * Return the new region and new size to update the szone. Do not deallocate
1250 * the old entries since someone may still be allocating them.
1253 hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
, size_t *mutable_shift
,
1256 // double in size and allocate memory for the regions
1257 *new_size
= old_size
+ old_size
;
1258 *mutable_shift
= *mutable_shift
+ 1;
1259 region_t
*new_regions
= hash_regions_alloc_no_lock(szone
, *new_size
);
1261 // rehash the entries into the new list
1263 for (index
= 0; index
< old_size
; ++index
) {
1264 region_t r
= regions
[index
];
1265 if (r
!= HASHRING_OPEN_ENTRY
&& r
!= HASHRING_REGION_DEALLOCATED
)
1266 hash_region_insert_no_lock(new_regions
, *new_size
, *mutable_shift
, r
);
1271 /********************* FREE LIST UTILITIES ************************/
1273 // A free list entry is comprised of a pair of pointers, previous and next.
1274 // These are used to implement a doubly-linked list, which permits efficient
1277 // Because the free list entries are previously freed objects, a misbehaved
1278 // program may write to a pointer after it has called free() on that pointer,
1279 // either by dereference or buffer overflow from an adjacent pointer. This write
1280 // would then corrupt the free list's previous and next pointers, leading to a
1281 // crash. In order to detect this case, we take advantage of the fact that
1282 // malloc'd pointers are known to be at least 16 byte aligned, and thus have
1283 // at least 4 trailing zero bits.
1285 // When an entry is added to the free list, a checksum of the previous and next
1286 // pointers is calculated and written to the low four bits of the respective
1287 // pointers. Upon detection of an invalid checksum, an error is logged and NULL
1288 // is returned. Since all code which un-checksums pointers checks for a NULL
1289 // return, a potentially crashing or malicious dereference is avoided at the
1290 // cost of leaking the corrupted block, and any subsequent blocks on the free
1291 // list of that size.
1293 static NOINLINE
void
1294 free_list_checksum_botch(szone_t
*szone
, free_list_t
*ptr
)
1296 szone_error(szone
, 1, "incorrect checksum for freed object "
1297 "- object was probably modified after being freed.", ptr
, NULL
);
1300 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
)
1304 chk
= (unsigned char)(ptr
>> 0);
1305 chk
+= (unsigned char)(ptr
>> 8);
1306 chk
+= (unsigned char)(ptr
>> 16);
1307 chk
+= (unsigned char)(ptr
>> 24);
1309 chk
+= (unsigned char)(ptr
>> 32);
1310 chk
+= (unsigned char)(ptr
>> 40);
1311 chk
+= (unsigned char)(ptr
>> 48);
1312 chk
+= (unsigned char)(ptr
>> 56);
1315 return chk
& (uintptr_t)0xF;
1318 static INLINE
uintptr_t
1319 free_list_checksum_ptr(szone_t
*szone
, void *ptr
)
1321 uintptr_t p
= (uintptr_t)ptr
;
1322 return p
| free_list_gen_checksum(p
^ szone
->cookie
);
1325 static INLINE
void *
1326 free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
)
1329 p
.u
= (ptr
->u
>> 4) << 4;
1331 if ((ptr
->u
& (uintptr_t)0xF) != free_list_gen_checksum(p
.u
^ szone
->cookie
))
1333 free_list_checksum_botch(szone
, (free_list_t
*)ptr
);
1340 free_list_count(szone_t
*szone
, free_list_t
*ptr
)
1346 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
1352 recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1354 // excise node from list
1355 if (NULL
== node
->prev
)
1356 mag_ptr
->firstNode
= node
->next
;
1358 node
->prev
->next
= node
->next
;
1360 if (NULL
== node
->next
)
1361 mag_ptr
->lastNode
= node
->prev
;
1363 node
->next
->prev
= node
->prev
;
1365 mag_ptr
->recirculation_entries
--;
1369 recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1371 if (NULL
== mag_ptr
->lastNode
) {
1372 mag_ptr
->firstNode
= node
;
1375 node
->prev
= mag_ptr
->lastNode
;
1376 mag_ptr
->lastNode
->next
= node
;
1378 mag_ptr
->lastNode
= node
;
1380 node
->recirc_suitable
= FALSE
;
1381 mag_ptr
->recirculation_entries
++;
1385 recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1387 if (NULL
== mag_ptr
->firstNode
) {
1388 mag_ptr
->lastNode
= node
;
1391 node
->next
= mag_ptr
->firstNode
;
1392 mag_ptr
->firstNode
->prev
= node
;
1394 mag_ptr
->firstNode
= node
;
1396 node
->recirc_suitable
= FALSE
;
1397 mag_ptr
->recirculation_entries
++;
1400 /* Macros used to manipulate the uint32_t quantity mag_bitmap. */
1402 /* BITMAPV variants are used by tiny. */
1403 #if defined(__LP64__)
1404 // assert(NUM_SLOTS == 64) in which case (slot >> 5) is either 0 or 1
1405 #define BITMAPV_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1406 #define BITMAPV_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1407 #define BITMAPV_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1408 #define BITMAPV_CTZ(bitmap) (__builtin_ctzl(bitmap))
1410 // assert(NUM_SLOTS == 32) in which case (slot >> 5) is always 0, so code it that way
1411 #define BITMAPV_SET(bitmap,slot) (bitmap[0] |= 1 << (slot))
1412 #define BITMAPV_CLR(bitmap,slot) (bitmap[0] &= ~ (1 << (slot)))
1413 #define BITMAPV_BIT(bitmap,slot) ((bitmap[0] >> (slot)) & 1)
1414 #define BITMAPV_CTZ(bitmap) (__builtin_ctz(bitmap))
1417 /* BITMAPN is used by small. (slot >> 5) takes on values from 0 to 7. */
1418 #define BITMAPN_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1419 #define BITMAPN_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1420 #define BITMAPN_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1422 /* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
1423 #define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap[0]))
1425 /********************* TINY FREE LIST UTILITIES ************************/
1427 // We encode the meta-headers as follows:
1428 // Each quantum has an associated set of 2 bits:
1429 // block_header when 1 says this block is the beginning of a block
1430 // in_use when 1 says this block is in use
1431 // so a block in use of size 3 is 1-1 0-X 0-X
1432 // for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
1433 // for a block middle the bits are 0-0
1435 // We store the meta-header bit arrays by interleaving them 32 bits at a time.
1436 // Initial 32 bits of block_header, followed by initial 32 bits of in_use, followed
1437 // by next 32 bits of block_header, followed by next 32 bits of in_use, etc.
1438 // This localizes memory references thereby reducing cache and TLB pressures.
1441 BITARRAY_SET(uint32_t *bits
, msize_t index
)
1443 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1444 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1445 bits
[(index
>> 5) << 1] |= (1 << (index
& 31));
1449 BITARRAY_CLR(uint32_t *bits
, msize_t index
)
1451 bits
[(index
>> 5) << 1] &= ~(1 << (index
& 31));
1454 static INLINE boolean_t
1455 BITARRAY_BIT(uint32_t *bits
, msize_t index
)
1457 return ((bits
[(index
>> 5) << 1]) >> (index
& 31)) & 1;
1461 static INLINE
void bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
) ALWAYSINLINE
;
1464 bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
)
1466 // start >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1467 // (start >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1468 uint32_t *addr
= bits
+ ((start
>> 5) << 1);
1470 uint32_t span
= end
- start
;
1475 addr
[0] &= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1476 addr
[2] &= (0xFFFFFFFFU
<< (end
- 32));
1478 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1479 mask
|= (0xFFFFFFFFU
<< end
);
1486 * Obtain the size of a free tiny block (in msize_t units).
1489 get_tiny_free_size(const void *ptr
)
1491 void *next_block
= (void *)((uintptr_t)ptr
+ TINY_QUANTUM
);
1492 void *region_end
= TINY_REGION_END(TINY_REGION_FOR_PTR(ptr
));
1494 // check whether the next block is outside the tiny region or a block header
1495 // if so, then the size of this block is one, and there is no stored size.
1496 if (next_block
< region_end
)
1498 uint32_t *next_header
= TINY_BLOCK_HEADER_FOR_PTR(next_block
);
1499 msize_t next_index
= TINY_INDEX_FOR_PTR(next_block
);
1501 if (!BITARRAY_BIT(next_header
, next_index
))
1502 return TINY_FREE_SIZE(ptr
);
1508 * Get the size of the previous free block, which is stored in the last two
1509 * bytes of the block. If the previous block is not free, then the result is
1513 get_tiny_previous_free_msize(const void *ptr
)
1515 // check whether the previous block is in the tiny region and a block header
1516 // if so, then the size of the previous block is one, and there is no stored
1518 if (ptr
!= TINY_REGION_FOR_PTR(ptr
))
1520 void *prev_block
= (void *)((uintptr_t)ptr
- TINY_QUANTUM
);
1521 uint32_t *prev_header
= TINY_BLOCK_HEADER_FOR_PTR(prev_block
);
1522 msize_t prev_index
= TINY_INDEX_FOR_PTR(prev_block
);
1523 if (BITARRAY_BIT(prev_header
, prev_index
))
1525 return TINY_PREVIOUS_MSIZE(ptr
);
1527 // don't read possibly unmapped memory before the beginning of the region
1531 static INLINE msize_t
1532 get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
)
1534 // returns msize and is_free
1535 // may return 0 for the msize component (meaning 65536)
1536 uint32_t *block_header
;
1539 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1540 index
= TINY_INDEX_FOR_PTR(ptr
);
1542 msize_t midx
= (index
>> 5) << 1;
1543 uint32_t mask
= 1 << (index
& 31);
1545 if (0 == (block_header
[midx
] & mask
)) // if (!BITARRAY_BIT(block_header, index))
1547 if (0 == (block_header
[midx
+ 1] & mask
)) { // if (!BITARRAY_BIT(in_use, index))
1549 return get_tiny_free_size(ptr
);
1552 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1553 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1554 #if defined(__LP64__)
1555 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1556 // That's guaranteed to be somewhwere in the next 64 bits. And those bits could span three
1557 // uint32_t block_header elements. Collect the bits into a single uint64_t and measure up with ffsl.
1558 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1559 uint32_t bitidx
= index
& 31;
1560 uint64_t word_lo
= addr
[0];
1561 uint64_t word_mid
= addr
[2];
1562 uint64_t word_hi
= addr
[4];
1563 uint64_t word_lomid
= (word_lo
>> bitidx
) | (word_mid
<< (32 - bitidx
));
1564 uint64_t word
= bitidx
? word_lomid
| (word_hi
<< (64 - bitidx
)) : word_lomid
;
1565 uint32_t result
= __builtin_ffsl(word
>> 1);
1567 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1568 // That's guaranteed to be somwhwere in the next 32 bits. And those bits could span two
1569 // uint32_t block_header elements. Collect the bits into a single uint32_t and measure up with ffs.
1570 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1571 uint32_t bitidx
= index
& 31;
1572 uint32_t word
= bitidx
? (addr
[0] >> bitidx
) | (addr
[2] << (32 - bitidx
)) : addr
[0];
1573 uint32_t result
= __builtin_ffs(word
>> 1);
1579 set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
)
1581 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1582 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1583 msize_t clr_msize
= msize
- 1;
1584 msize_t midx
= (index
>> 5) << 1;
1585 uint32_t val
= (1 << (index
& 31));
1588 if (msize
>= NUM_TINY_SLOTS
)
1589 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr
, msize
);
1590 if ((unsigned)index
+ (unsigned)msize
> 0x10000)
1591 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr
, msize
);
1594 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1595 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1597 // bitarray_mclr(block_header, index, end_bit);
1598 // bitarray_mclr(in_use, index, end_bit);
1601 midx
= (index
>> 5) << 1;
1603 unsigned start
= index
& 31;
1604 unsigned end
= start
+ clr_msize
;
1606 #if defined(__LP64__)
1608 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1609 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 64));
1610 block_header
[midx
+ 0] &= mask0
; // clear header
1611 block_header
[midx
+ 1] &= mask0
; // clear in_use
1612 block_header
[midx
+ 2] = 0; // clear header
1613 block_header
[midx
+ 3] = 0; // clear in_use
1614 block_header
[midx
+ 4] &= mask1
; // clear header
1615 block_header
[midx
+ 5] &= mask1
; // clear in_use
1619 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1620 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 32));
1621 block_header
[midx
+ 0] &= mask0
;
1622 block_header
[midx
+ 1] &= mask0
;
1623 block_header
[midx
+ 2] &= mask1
;
1624 block_header
[midx
+ 3] &= mask1
;
1626 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1627 mask
|= (0xFFFFFFFFU
<< end
);
1628 block_header
[midx
+ 0] &= mask
;
1629 block_header
[midx
+ 1] &= mask
;
1632 // we set the block_header bit for the following block to reaffirm next block is a block
1634 midx
= (index
>> 5) << 1;
1635 val
= (1 << (index
& 31));
1636 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize));
1642 mf
= get_tiny_meta_header(ptr
, &ff
);
1644 malloc_printf("setting header for tiny in_use %p : %d\n", ptr
, msize
);
1645 malloc_printf("reading header for tiny %p : %d %d\n", ptr
, mf
, ff
);
1652 set_tiny_meta_header_in_use_1(const void *ptr
) // As above with msize == 1
1654 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1655 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1656 msize_t midx
= (index
>> 5) << 1;
1657 uint32_t val
= (1 << (index
& 31));
1659 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1660 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1663 midx
= (index
>> 5) << 1;
1664 val
= (1 << (index
& 31));
1666 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize))
1670 set_tiny_meta_header_middle(const void *ptr
)
1672 // indicates this block is in the middle of an in use block
1673 uint32_t *block_header
;
1677 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1678 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1679 index
= TINY_INDEX_FOR_PTR(ptr
);
1681 BITARRAY_CLR(block_header
, index
);
1682 BITARRAY_CLR(in_use
, index
);
1686 set_tiny_meta_header_free(const void *ptr
, msize_t msize
)
1688 // !msize is acceptable and means 65536
1689 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1690 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1691 msize_t midx
= (index
>> 5) << 1;
1692 uint32_t val
= (1 << (index
& 31));
1695 if ((unsigned)index
+ (unsigned)msize
> 0x10000) {
1696 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr
, msize
);
1700 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1701 block_header
[midx
+ 1] &= ~val
; // BITARRAY_CLR(in_use, index);
1703 // mark the end of this block if msize is > 1. For msize == 0, the whole
1704 // region is free, so there is no following block. For msize == 1, there is
1705 // no space to write the size on 64 bit systems. The size for 1 quantum
1706 // blocks is computed from the metadata bitmaps.
1708 void *follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
1709 TINY_PREVIOUS_MSIZE(follower
) = msize
;
1710 TINY_FREE_SIZE(ptr
) = msize
;
1713 TINY_FREE_SIZE(ptr
) = msize
;
1717 msize_t mf
= get_tiny_meta_header(ptr
, &ff
);
1718 if ((msize
!= mf
) || !ff
) {
1719 malloc_printf("setting header for tiny free %p : %u\n", ptr
, msize
);
1720 malloc_printf("reading header for tiny %p : %u %u\n", ptr
, mf
, ff
);
1725 static INLINE boolean_t
1726 tiny_meta_header_is_free(const void *ptr
)
1728 uint32_t *block_header
;
1732 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1733 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1734 index
= TINY_INDEX_FOR_PTR(ptr
);
1735 if (!BITARRAY_BIT(block_header
, index
))
1737 return !BITARRAY_BIT(in_use
, index
);
1740 static INLINE
void *
1741 tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
)
1743 // returns the previous block, assuming and verifying it's free
1744 uint32_t *block_header
;
1747 msize_t previous_msize
;
1748 msize_t previous_index
;
1751 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1752 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1753 index
= TINY_INDEX_FOR_PTR(ptr
);
1757 if ((previous_msize
= get_tiny_previous_free_msize(ptr
)) > index
)
1760 previous_index
= index
- previous_msize
;
1761 previous_ptr
= (void *)((uintptr_t)TINY_REGION_FOR_PTR(ptr
) + TINY_BYTES_FOR_MSIZE(previous_index
));
1762 if (!BITARRAY_BIT(block_header
, previous_index
))
1764 if (BITARRAY_BIT(in_use
, previous_index
))
1766 if (get_tiny_free_size(previous_ptr
) != previous_msize
)
1769 // conservative check did match true check
1770 *prev_msize
= previous_msize
;
1771 return previous_ptr
;
1775 * Adds an item to the proper free list, and also marks the meta-header of the
1777 * Assumes szone has been locked
1780 tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1782 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1783 free_list_t
*free_ptr
= ptr
;
1784 free_list_t
*free_head
= tiny_mag_ptr
->mag_free_list
[slot
];
1787 if (LOG(szone
,ptr
)) {
1788 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1790 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
1791 szone_error(szone
, 1, "tiny_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
1794 set_tiny_meta_header_free(ptr
, msize
);
1797 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
1798 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
1799 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
1801 if (! tiny_meta_header_is_free(free_head
)) {
1802 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
1803 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
1806 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
1808 BITMAPV_SET(tiny_mag_ptr
->mag_bitmap
, slot
);
1810 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
1811 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
1813 tiny_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
1817 * Removes the item pointed to by ptr in the proper free list.
1818 * Assumes szone has been locked
1821 tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1823 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1824 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
1826 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
1827 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
1830 if (LOG(szone
,ptr
)) {
1831 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1835 // The block to remove is the head of the free list
1837 if (tiny_mag_ptr
->mag_free_list
[slot
] != ptr
) {
1838 szone_error(szone
, 1, "tiny_free_list_remove_ptr: Internal invariant broken (tiny_mag_ptr->mag_free_list[slot])", ptr
,
1839 "ptr=%p slot=%d msize=%d tiny_mag_ptr->mag_free_list[slot]=%p\n",
1840 ptr
, slot
, msize
, (void *)tiny_mag_ptr
->mag_free_list
[slot
]);
1844 tiny_mag_ptr
->mag_free_list
[slot
] = next
;
1845 if (!next
) BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
1847 // We know free_ptr is already checksummed, so we don't need to do it
1849 previous
->next
= free_ptr
->next
;
1852 // We know free_ptr is already checksummed, so we don't need to do it
1854 next
->previous
= free_ptr
->previous
;
1859 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
1860 * or NULL if not found.
1862 static INLINE region_t
1863 tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
1865 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
1866 szone
->tiny_region_generation
->num_regions_allocated
,
1867 szone
->tiny_region_generation
->num_regions_allocated_shift
,
1868 TINY_REGION_FOR_PTR(ptr
));
1873 tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
) {
1874 void *last_block
, *previous_block
;
1875 uint32_t *last_header
;
1876 msize_t last_msize
, previous_msize
, last_index
;
1878 last_block
= (void *)
1879 ((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) - tiny_mag_ptr
->mag_bytes_free_at_end
);
1880 last_msize
= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
1881 last_header
= TINY_BLOCK_HEADER_FOR_PTR(last_block
);
1882 last_index
= TINY_INDEX_FOR_PTR(last_block
);
1884 // Before anything we transform any remaining mag_bytes_free_at_end into a
1885 // regular free block. We take special care here to update the bitfield
1886 // information, since we are bypassing the normal free codepath. If there
1887 // is more than one quanta worth of memory in mag_bytes_free_at_end, then
1888 // there will be two block headers:
1889 // 1) header for the free space at end, msize = 1
1890 // 2) header inserted by set_tiny_meta_header_in_use after block
1891 // We must clear the second one so that when the free block's size is
1892 // queried, we do not think the block is only 1 quantum in size because
1893 // of the second set header bit.
1894 if (last_index
!= (NUM_TINY_BLOCKS
- 1))
1895 BITARRAY_CLR(last_header
, (last_index
+ 1));
1897 // It is possible that the block prior to the last block in the region has
1898 // been free'd, but was not coalesced with the free bytes at the end of the
1899 // block, since we treat the bytes at the end of the region as "in use" in
1900 // the meta headers. Attempt to coalesce the last block with the previous
1901 // block, so we don't violate the "no consecutive free blocks" invariant.
1903 // FIXME: Need to investigate how much work would be required to increase
1904 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
1905 // than performing this workaround.
1907 previous_block
= tiny_previous_preceding_free(last_block
, &previous_msize
);
1908 if (previous_block
) {
1909 set_tiny_meta_header_middle(last_block
);
1910 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous_block
, previous_msize
);
1911 last_block
= previous_block
;
1912 last_msize
+= previous_msize
;
1915 // splice last_block into the free list
1916 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, last_block
, last_msize
);
1917 tiny_mag_ptr
->mag_bytes_free_at_end
= 0;
1918 tiny_mag_ptr
->mag_last_region
= NULL
;
1922 tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
1923 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1924 uintptr_t current
= start
;
1925 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1928 int total_alloc
= 0;
1930 while (current
< limit
) {
1931 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1932 if (is_free
&& !msize
&& (current
== start
)) {
1933 // first block is all free
1938 malloc_printf("*** tiny_free_detach_region error with %p: msize=%d is_free =%d\n",
1939 (void *)current
, msize
, is_free
);
1944 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
1948 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1954 tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
1955 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1956 uintptr_t current
= start
;
1957 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1960 size_t total_alloc
= 0;
1962 while (current
< limit
) {
1963 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1964 if (is_free
&& !msize
&& (current
== start
)) {
1965 // first block is all free
1970 malloc_printf("*** tiny_free_reattach_region error with %p: msize=%d is_free =%d\n",
1971 (void *)current
, msize
, is_free
);
1976 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
1978 total_alloc
+= TINY_BYTES_FOR_MSIZE(msize
);
1980 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1986 tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
1987 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1988 uintptr_t current
= start
;
1989 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1992 boolean_t did_advise
= FALSE
;
1994 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
1996 while (current
< limit
) {
1997 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1998 if (is_free
&& !msize
&& (current
== start
)) {
1999 // first block is all free
2001 malloc_printf("*** tiny_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
2002 (void *)current
, msize
, is_free
);
2004 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
2005 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
2008 #if TARGET_OS_EMBEDDED
2009 madvise_free_range(szone
, r
, pgLo
, pgHi
, NULL
);
2011 madvise_free_range(szone
, r
, pgLo
, pgHi
);
2019 malloc_printf("*** tiny_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
2020 (void *)current
, msize
, is_free
);
2025 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
2026 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
2029 #if TARGET_OS_EMBEDDED
2030 madvise_free_range(szone
, r
, pgLo
, pgHi
, NULL
);
2032 madvise_free_range(szone
, r
, pgLo
, pgHi
);
2037 current
+= TINY_BYTES_FOR_MSIZE(msize
);
2041 /* Move the node to the tail of the Deopt's recirculation list to delay its re-use. */
2042 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(r
);
2043 recirc_list_extract(szone
, depot_ptr
, node
); // excise node from list
2044 recirc_list_splice_last(szone
, depot_ptr
, node
); // connect to magazine as last node
2049 tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
2051 #warning Tune Depot headroom
2052 if (0 < node
->bytes_used
||
2053 depot_ptr
->recirculation_entries
< (szone
->num_tiny_magazines
* 2)) {
2057 // disconnect node from Depot
2058 recirc_list_extract(szone
, depot_ptr
, node
);
2060 // Iterate the region pulling its free entries off the (locked) Depot's free list
2061 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2062 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2064 if (0 == objects_in_use
) {
2065 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
2066 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
2067 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2068 szone
->tiny_region_generation
->num_regions_allocated
,
2069 szone
->tiny_region_generation
->num_regions_allocated_shift
, sparse_region
);
2070 *pSlot
= HASHRING_REGION_DEALLOCATED
;
2071 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2072 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
2073 __sync_fetch_and_add( &(szone
->num_tiny_regions_dealloc
), 1); // Atomically increment num_tiny_regions_dealloc
2076 OSAtomicIncrement64( (volatile int64_t *)&(szone
->num_tiny_regions_dealloc
) );
2078 OSAtomicIncrement32( (volatile int32_t *)&(szone
->num_tiny_regions_dealloc
) );
2082 // Transfer ownership of the region back to the OS
2083 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
); // Avoid denial of service to Depot while in kernel
2084 deallocate_pages(szone
, sparse_region
, TINY_REGION_SIZE
, 0);
2085 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
2087 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
); // DTrace USDT Probe
2090 szone_error(szone
, 1, "tiny_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
2095 tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2097 // The entire magazine crossed the "emptiness threshold". Transfer a region
2098 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2099 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2100 region_trailer_t
*node
= tiny_mag_ptr
->firstNode
;
2102 while (node
&& !node
->recirc_suitable
) {
2108 malloc_printf("*** tiny_free_do_recirc_to_depot end of list\n");
2113 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2115 // Deal with unclaimed memory -- mag_bytes_free_at_end
2116 if (sparse_region
== tiny_mag_ptr
->mag_last_region
&& tiny_mag_ptr
->mag_bytes_free_at_end
) {
2117 tiny_finalize_region(szone
, tiny_mag_ptr
);
2120 // disconnect "suitable" node from magazine
2121 recirc_list_extract(szone
, tiny_mag_ptr
, node
);
2123 // Iterate the region pulling its free entries off its (locked) magazine's free list
2124 int objects_in_use
= tiny_free_detach_region(szone
, tiny_mag_ptr
, sparse_region
);
2125 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2127 // hand over the region to the (locked) Depot
2128 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2129 // this will cause tiny_free_list_add_ptr called by tiny_free_reattach_region to use
2130 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
2131 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
2133 // Iterate the region putting its free entries on Depot's free list
2134 size_t bytes_inplay
= tiny_free_reattach_region(szone
, depot_ptr
, sparse_region
);
2136 tiny_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2137 tiny_mag_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2138 tiny_mag_ptr
->mag_num_objects
-= objects_in_use
;
2140 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2141 depot_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2142 depot_ptr
->mag_num_objects
+= objects_in_use
;
2144 // connect to Depot as first (MRU) node
2145 recirc_list_splice_first(szone
, depot_ptr
, node
);
2147 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2149 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
2150 tiny_free_scan_madvise_free(szone
, depot_ptr
, sparse_region
);
2152 // If the region is entirely empty vm_deallocate() it
2153 tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
2155 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2159 tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2161 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2163 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2164 if (szone
->num_tiny_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
2168 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
2169 szone_error(szone
, 1, "tiny_get_region_from_depot called for magazine index -1", NULL
, NULL
);
2174 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2176 // Appropriate one of the Depot's regions. Prefer LIFO selection for best cache utilization.
2177 region_trailer_t
*node
= depot_ptr
->firstNode
;
2179 if (NULL
== node
) { // Depot empty?
2180 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2184 // disconnect first node from Depot
2185 recirc_list_extract(szone
, depot_ptr
, node
);
2187 // Iterate the region pulling its free entries off the (locked) Depot's free list
2188 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2189 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2191 // Transfer ownership of the region
2192 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = mag_index
;
2194 // Iterate the region putting its free entries on its new (locked) magazine's free list
2195 size_t bytes_inplay
= tiny_free_reattach_region(szone
, tiny_mag_ptr
, sparse_region
);
2197 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2198 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2199 depot_ptr
->mag_num_objects
-= objects_in_use
;
2201 tiny_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2202 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2203 tiny_mag_ptr
->mag_num_objects
+= objects_in_use
;
2205 // connect to magazine as first node (it's maximally sparse at this moment)
2206 recirc_list_splice_first(szone
, tiny_mag_ptr
, node
);
2208 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2210 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2212 #if !TARGET_OS_EMBEDDED
2213 if (-1 == madvise((void *)sparse_region
, TINY_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
2214 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
2216 szone_error(szone
, 1, "tiny_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed", sparse_region
, NULL
);
2225 #warning Tune K and f!
2226 #define K 1.5 // headroom measured in number of 1Mb regions
2227 #define DENSITY_THRESHOLD(a) \
2228 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
2231 tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
, void *ptr
,
2234 void *original_ptr
= ptr
;
2235 size_t original_size
= TINY_BYTES_FOR_MSIZE(msize
);
2236 void *next_block
= ((unsigned char *)ptr
+ original_size
);
2237 msize_t previous_msize
, next_msize
;
2239 free_list_t
*big_free_block
;
2240 free_list_t
*after_next_block
;
2241 free_list_t
*before_next_block
;
2242 boolean_t did_prepend
= FALSE
;
2243 boolean_t did_append
= FALSE
;
2246 if (LOG(szone
,ptr
)) {
2247 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2250 szone_error(szone
, 1, "trying to free tiny block that is too small", ptr
,
2251 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2255 // We try to coalesce this block with the preceeding one
2256 previous
= tiny_previous_preceding_free(ptr
, &previous_msize
);
2259 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
2260 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
2265 // clear the meta_header since this is no longer the start of a block
2266 set_tiny_meta_header_middle(ptr
);
2267 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous
, previous_msize
);
2269 msize
+= previous_msize
;
2271 // We try to coalesce with the next block
2272 if ((next_block
< TINY_REGION_END(region
)) && tiny_meta_header_is_free(next_block
)) {
2274 next_msize
= get_tiny_free_size(next_block
);
2276 if (LOG(szone
, ptr
) || LOG(szone
, next_block
)) {
2277 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
2278 ptr
, msize
, next_block
, next_msize
);
2281 // If we are coalescing with the next block, and the next block is in
2282 // the last slot of the free list, then we optimize this case here to
2283 // avoid removing next_block from the slot (NUM_TINY_SLOTS - 1) and then adding ptr back
2284 // to slot (NUM_TINY_SLOTS - 1).
2285 if (next_msize
>= NUM_TINY_SLOTS
) {
2286 msize
+= next_msize
;
2288 big_free_block
= (free_list_t
*)next_block
;
2289 after_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->next
);
2290 before_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->previous
);
2292 if (!before_next_block
) {
2293 tiny_mag_ptr
->mag_free_list
[NUM_TINY_SLOTS
-1] = ptr
;
2295 before_next_block
->next
.u
= free_list_checksum_ptr(szone
, ptr
);
2298 if (after_next_block
) {
2299 after_next_block
->previous
.u
= free_list_checksum_ptr(szone
, ptr
);
2302 // we don't need to checksum these since they are already checksummed
2303 ((free_list_t
*)ptr
)->previous
= big_free_block
->previous
;
2304 ((free_list_t
*)ptr
)->next
= big_free_block
->next
;
2306 // clear the meta_header to enable coalescing backwards
2307 set_tiny_meta_header_middle(big_free_block
);
2308 set_tiny_meta_header_free(ptr
, msize
);
2310 goto tiny_free_ending
;
2312 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2313 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2314 msize
+= next_msize
;
2317 // The tiny cache already scribbles free blocks as they go through the
2318 // cache, so we do not need to do it here.
2319 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
2320 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
2323 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, ptr
, msize
);
2325 // When in proper debug mode we write on the memory to help debug memory smashers
2327 tiny_mag_ptr
->mag_num_objects
--;
2328 // we use original_size and not msize to avoid double counting the coalesced blocks
2329 tiny_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
2331 // Update this region's bytes in use count
2332 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2333 size_t bytes_used
= node
->bytes_used
- original_size
;
2334 node
->bytes_used
= bytes_used
;
2336 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
2337 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2338 if (szone
->num_tiny_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
2340 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
2341 // Emptiness discriminant
2342 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2343 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
2344 recirculation candidates list. */
2345 node
->recirc_suitable
= TRUE
;
2347 /* After this free, we've found the region is still dense, so it must have been even more so before
2348 the free. That implies the region is already correctly marked. Do nothing. */
2351 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
2352 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2353 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2354 size_t a
= tiny_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
2355 size_t u
= tiny_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
2357 if (a
- u
> ((3 * TINY_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
))
2358 tiny_free_do_recirc_to_depot(szone
, tiny_mag_ptr
, mag_index
);
2362 // Freed to Depot. N.B. Lock on tiny_magazines[DEPOT_MAGAZINE_INDEX] is already held
2363 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
2364 uintptr_t round_safe
= round_page(safe_ptr
);
2366 uintptr_t safe_extent
= (uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
2367 uintptr_t trunc_extent
= trunc_page(safe_extent
);
2369 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
2370 if (round_safe
< trunc_extent
) { // Safe area covers a page
2371 if (did_prepend
& did_append
) { // Coalesced preceding with original_ptr *and* with following
2372 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
2373 uintptr_t rnd_safe_follow
=
2374 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
2376 #if TARGET_OS_EMBEDDED
2377 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
), &szone
->last_tiny_advise
);
2379 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
));
2381 } else if (did_prepend
) { // Coalesced preceding with original_ptr
2382 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
2384 #if TARGET_OS_EMBEDDED
2385 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
, &szone
->last_tiny_advise
);
2387 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
);
2389 } else if (did_append
) { // Coalesced original_ptr with following
2390 uintptr_t rnd_safe_follow
=
2391 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
2393 #if TARGET_OS_EMBEDDED
2394 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
), &szone
->last_tiny_advise
);
2396 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
));
2398 } else { // Isolated free cannot exceed 496 bytes, thus round_safe == trunc_extent, and so never get here.
2399 /* madvise_free_range(szone, region, round_safe, trunc_extent); */
2403 #if !TARGET_OS_EMBEDDED
2404 if (0 < bytes_used
) {
2405 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
2406 so as to avoid thrashing between the Depot's free list and a magazines's free list
2407 with detach_region/reattach_region */
2409 /* Depot'd region is just now empty. Consider return to OS. */
2410 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2411 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2412 tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
); // FIXME: depot_ptr is simply tiny_mag_ptr?
2418 // Allocates from the last region or a freshly allocated region
2420 tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2422 void *ptr
, *aligned_address
;
2424 // Deal with unclaimed memory -- mag_bytes_free_at_end
2425 if (tiny_mag_ptr
->mag_bytes_free_at_end
)
2426 tiny_finalize_region(szone
, tiny_mag_ptr
);
2428 // time to create a new region
2429 aligned_address
= allocate_pages(szone
, TINY_REGION_SIZE
, TINY_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_TINY
);
2430 if (!aligned_address
) // out of memory!
2433 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
); // DTrace USDT Probe
2435 // We set the unused bits of the header in the last pair to be all ones, and those of the inuse to zeroes.
2436 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].header
=
2437 (NUM_TINY_BLOCKS
& 31) ? (0xFFFFFFFFU
<< (NUM_TINY_BLOCKS
& 31)) : 0;
2438 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].inuse
= 0;
2440 // Here find the only place in tinyland that (infrequently) takes the tiny_regions_lock.
2441 // Only one thread at a time should be permitted to assess the density of the hash
2442 // ring and adjust if needed.
2443 // Only one thread at a time should be permitted to insert its new region on
2445 // It is safe for all other threads to read the hash ring (hashed_regions) and
2446 // the associated sizes (num_regions_allocated and num_tiny_regions).
2448 LOCK(szone
->tiny_regions_lock
);
2450 // Check to see if the hash ring of tiny regions needs to grow. Try to
2451 // avoid the hash ring becoming too dense.
2452 if (szone
->tiny_region_generation
->num_regions_allocated
< (2 * szone
->num_tiny_regions
)) {
2453 region_t
*new_regions
;
2455 size_t new_shift
= szone
->tiny_region_generation
->num_regions_allocated_shift
; // In/Out parameter
2456 new_regions
= hash_regions_grow_no_lock(szone
, szone
->tiny_region_generation
->hashed_regions
,
2457 szone
->tiny_region_generation
->num_regions_allocated
,
2460 // Do not deallocate the current hashed_regions allocation since someone may
2461 // be iterating it. Instead, just leak it.
2463 // Prepare to advance to the "next generation" of the hash ring.
2464 szone
->tiny_region_generation
->nextgen
->hashed_regions
= new_regions
;
2465 szone
->tiny_region_generation
->nextgen
->num_regions_allocated
= new_size
;
2466 szone
->tiny_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
2468 // Throw the switch to atomically advance to the next generation.
2469 szone
->tiny_region_generation
= szone
->tiny_region_generation
->nextgen
;
2470 // Ensure everyone sees the advance.
2471 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
2472 __sync_synchronize();
2477 // Tag the region at "aligned_address" as belonging to us,
2478 // and so put it under the protection of the magazine lock we are holding.
2479 // Do this before advertising "aligned_address" on the hash ring(!)
2480 MAGAZINE_INDEX_FOR_TINY_REGION(aligned_address
) = mag_index
;
2482 // Insert the new region into the hash ring, and update malloc statistics
2483 hash_region_insert_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2484 szone
->tiny_region_generation
->num_regions_allocated
,
2485 szone
->tiny_region_generation
->num_regions_allocated_shift
,
2488 szone
->num_tiny_regions
++;
2489 UNLOCK(szone
->tiny_regions_lock
);
2491 tiny_mag_ptr
->mag_last_region
= aligned_address
;
2492 BYTES_USED_FOR_TINY_REGION(aligned_address
) = TINY_BYTES_FOR_MSIZE(msize
);
2493 ptr
= aligned_address
;
2494 set_tiny_meta_header_in_use(ptr
, msize
);
2495 tiny_mag_ptr
->mag_num_objects
++;
2496 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(msize
);
2497 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2499 // We put a header on the last block so that it appears in use (for coalescing, etc...)
2500 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
)));
2501 tiny_mag_ptr
->mag_bytes_free_at_end
= TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS
- msize
);
2503 // connect to magazine as first node (it's maximally sparse at this moment)
2504 recirc_list_splice_first(szone
, tiny_mag_ptr
, REGION_TRAILER_FOR_TINY_REGION(aligned_address
));
2507 if (LOG(szone
,ptr
)) {
2508 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2514 static INLINE boolean_t
2515 tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
2517 // returns 1 on success
2520 unsigned next_index
;
2523 msize_t next_msize
, coalesced_msize
, leftover_msize
;
2526 index
= TINY_INDEX_FOR_PTR(ptr
);
2527 old_msize
= TINY_MSIZE_FOR_BYTES(old_size
);
2528 next_index
= index
+ old_msize
;
2530 if (next_index
>= NUM_TINY_BLOCKS
) {
2533 next_block
= (char *)ptr
+ old_size
;
2535 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2536 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2537 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2540 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2543 is_free
= tiny_meta_header_is_free(next_block
);
2545 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2546 return 0; // next_block is in use;
2548 next_msize
= get_tiny_free_size(next_block
);
2549 if (old_size
+ TINY_BYTES_FOR_MSIZE(next_msize
) < new_size
) {
2550 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2551 return 0; // even with next block, not enough
2554 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2557 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2558 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2559 coalesced_msize
= TINY_MSIZE_FOR_BYTES(new_size
- old_size
+ TINY_QUANTUM
- 1);
2560 leftover_msize
= next_msize
- coalesced_msize
;
2561 if (leftover_msize
) {
2562 /* there's some left, so put the remainder back */
2563 leftover
= (void *)((uintptr_t)next_block
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
));
2565 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover
, leftover_msize
);
2567 set_tiny_meta_header_in_use(ptr
, old_msize
+ coalesced_msize
);
2569 if (LOG(szone
,ptr
)) {
2570 malloc_printf("in tiny_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, old_msize
+ coalesced_msize
);
2573 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2575 // Update this region's bytes in use count
2576 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2577 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2578 node
->bytes_used
= bytes_used
;
2580 // Emptiness discriminant
2581 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2582 /* After this reallocation the region is still sparse, so it must have been even more so before
2583 the reallocation. That implies the region is already correctly marked. Do nothing. */
2585 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2586 recirculation candidates list. */
2587 node
->recirc_suitable
= FALSE
;
2590 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2591 CHECK(szone
, __PRETTY_FUNCTION__
);
2596 tiny_check_region(szone_t
*szone
, region_t region
)
2598 uintptr_t start
, ptr
, region_end
;
2599 boolean_t prev_free
= 0;
2602 free_list_t
*free_head
;
2603 void *follower
, *previous
, *next
;
2604 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
2605 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2608 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2610 /* establish region limits */
2611 start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
2613 region_end
= (uintptr_t)TINY_REGION_END(region
);
2616 * The last region may have a trailing chunk which has not been converted into inuse/freelist
2619 if (region
== tiny_mag_ptr
->mag_last_region
)
2620 region_end
-= tiny_mag_ptr
->mag_bytes_free_at_end
;
2623 * Scan blocks within the region.
2625 while (ptr
< region_end
) {
2627 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
2630 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2631 if (is_free
&& !msize
&& (ptr
== start
)) {
2636 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
2640 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
2647 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
2650 if (msize
> (NUM_TINY_SLOTS
- 1)) {
2651 malloc_printf("*** invariant broken for %p this tiny msize=%d - size is too large\n",
2655 /* move to next block */
2656 ptr
+= TINY_BYTES_FOR_MSIZE(msize
);
2659 * Free blocks must have been coalesced, we cannot have a free block following another
2663 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
2669 * Check the integrity of this block's entry in its freelist.
2671 free_head
= (free_list_t
*)ptr
;
2672 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
2673 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
2674 if (previous
&& !tiny_meta_header_is_free(previous
)) {
2675 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2679 if (next
&& !tiny_meta_header_is_free(next
)) {
2680 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
2685 * Check the free block's trailing size value.
2687 follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
2688 if (((uintptr_t)follower
!= region_end
) && (get_tiny_previous_free_msize(follower
) != msize
)) {
2689 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
2690 "(end marker incorrect) should be %d; in fact %d\n",
2691 ptr
, follower
, TINY_REGION_ADDRESS(region
), region_end
, msize
, get_tiny_previous_free_msize(follower
));
2694 /* move to next block */
2695 ptr
= (uintptr_t)follower
;
2699 * Ensure that we scanned the entire region
2701 if (ptr
!= region_end
) {
2702 malloc_printf("*** invariant broken for region end %p - %p\n", ptr
, region_end
);
2706 * Check the trailing block's integrity.
2708 if (region
== tiny_mag_ptr
->mag_last_region
) {
2709 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2710 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2711 if (is_free
|| (msize
!= 1)) {
2712 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr
, msize
, is_free
);
2719 static kern_return_t
2720 tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
2721 memory_reader_t reader
, vm_range_recorder_t recorder
)
2726 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
2731 vm_range_t admin_range
;
2732 vm_range_t ptr_range
;
2733 unsigned char *mapped_region
;
2734 uint32_t *block_header
;
2736 unsigned block_index
;
2737 unsigned block_limit
;
2742 vm_address_t mag_last_free_ptr
= 0;
2743 msize_t mag_last_free_msize
= 0;
2745 region_hash_generation_t
*trg_ptr
;
2746 err
= reader(task
, (vm_address_t
)szone
->tiny_region_generation
, sizeof(region_hash_generation_t
), (void **)&trg_ptr
);
2747 if (err
) return err
;
2749 num_regions
= trg_ptr
->num_regions_allocated
;
2750 err
= reader(task
, (vm_address_t
)trg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
2751 if (err
) return err
;
2753 for (index
= 0; index
< num_regions
; ++index
) {
2754 region
= regions
[index
];
2755 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
2756 range
.address
= (vm_address_t
)TINY_REGION_ADDRESS(region
);
2757 range
.size
= (vm_size_t
)TINY_REGION_SIZE
;
2758 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
2759 admin_range
.address
= range
.address
+ TINY_METADATA_START
;
2760 admin_range
.size
= TINY_METADATA_SIZE
;
2761 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
2763 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
2764 ptr_range
.address
= range
.address
;
2765 ptr_range
.size
= NUM_TINY_BLOCKS
* TINY_QUANTUM
;
2766 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
2768 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
2769 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
2773 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(mapped_region
);
2774 magazine_t
*tiny_mag_ptr
;
2775 err
= reader(task
, (vm_address_t
)&(szone
->tiny_magazines
[mag_index
]), sizeof(magazine_t
),
2776 (void **)&tiny_mag_ptr
);
2777 if (err
) return err
;
2779 void *mag_last_free
= tiny_mag_ptr
->mag_last_free
;
2780 if (mag_last_free
) {
2781 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(TINY_QUANTUM
- 1);
2782 mag_last_free_msize
= (uintptr_t) mag_last_free
& (TINY_QUANTUM
- 1);
2785 block_header
= (uint32_t *)(mapped_region
+ TINY_METADATA_START
+ sizeof(region_trailer_t
));
2786 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
2788 block_limit
= NUM_TINY_BLOCKS
;
2789 if (region
== tiny_mag_ptr
->mag_last_region
)
2790 block_limit
-= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
2792 while (block_index
< block_limit
) {
2793 vm_size_t block_offset
= TINY_BYTES_FOR_MSIZE(block_index
);
2794 is_free
= !BITARRAY_BIT(in_use
, block_index
);
2796 mapped_ptr
= mapped_region
+ block_offset
;
2798 // mapped_region, the address at which 'range' in 'task' has been
2799 // mapped into our process, is not necessarily aligned to
2800 // TINY_BLOCKS_ALIGN.
2802 // Since the code in get_tiny_free_size() assumes the pointer came
2803 // from a properly aligned tiny region, and mapped_region is not
2804 // necessarily aligned, then do the size calculation directly.
2805 // If the next bit is set in the header bitmap, then the size is one
2806 // quantum. Otherwise, read the size field.
2807 if (!BITARRAY_BIT(block_header
, (block_index
+1)))
2808 msize
= TINY_FREE_SIZE(mapped_ptr
);
2814 } else if (range
.address
+ block_offset
!= mag_last_free_ptr
) {
2816 bit
= block_index
+ 1;
2817 while (! BITARRAY_BIT(block_header
, bit
)) {
2821 buffer
[count
].address
= range
.address
+ block_offset
;
2822 buffer
[count
].size
= TINY_BYTES_FOR_MSIZE(msize
);
2824 if (count
>= MAX_RECORDER_BUFFER
) {
2825 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2829 // Block is not free but it matches mag_last_free_ptr so even
2830 // though it is not marked free in the bitmap, we treat it as if
2831 // it is and move on
2832 msize
= mag_last_free_msize
;
2834 block_index
+= msize
;
2837 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2847 tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2851 grain_t slot
= msize
- 1;
2852 free_list_t
**free_list
= tiny_mag_ptr
->mag_free_list
;
2853 free_list_t
**the_slot
= free_list
+ slot
;
2855 free_list_t
**limit
;
2856 #if defined(__LP64__)
2861 msize_t leftover_msize
;
2862 free_list_t
*leftover_ptr
;
2864 // Assumes we've locked the region
2865 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2867 // Look for an exact match by checking the freelist for this msize.
2871 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2873 next
->previous
= ptr
->previous
;
2875 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
2880 if (LOG(szone
, ptr
)) {
2881 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2884 goto return_tiny_alloc
;
2887 // Mask off the bits representing slots holding free blocks smaller than the
2888 // size we need. If there are no larger free blocks, try allocating from
2889 // the free space at the end of the tiny region.
2890 #if defined(__LP64__)
2891 bitmap
= ((uint64_t *)(tiny_mag_ptr
->mag_bitmap
))[0] & ~ ((1ULL << slot
) - 1);
2893 bitmap
= tiny_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
2896 goto try_tiny_malloc_from_end
;
2898 slot
= BITMAPV_CTZ(bitmap
);
2899 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
2902 if (free_list
< limit
) {
2905 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2908 next
->previous
= ptr
->previous
;
2910 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
2912 this_msize
= get_tiny_free_size(ptr
);
2913 goto add_leftover_and_proceed
;
2916 malloc_printf("in tiny_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
2920 // We are now looking at the last slot, which contains blocks equal to, or
2921 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
2922 // If the last freelist is not empty, and the head contains a block that is
2923 // larger than our request, then the remainder is put back on the free list.
2926 this_msize
= get_tiny_free_size(ptr
);
2927 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2928 if (this_msize
- msize
>= NUM_TINY_SLOTS
) {
2929 // the leftover will go back to the free list, so we optimize by
2930 // modifying the free list rather than a pop and push of the head
2931 leftover_msize
= this_msize
- msize
;
2932 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2933 *limit
= leftover_ptr
;
2935 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
2937 leftover_ptr
->previous
= ptr
->previous
;
2938 leftover_ptr
->next
= ptr
->next
;
2939 set_tiny_meta_header_free(leftover_ptr
, leftover_msize
);
2941 if (LOG(szone
,ptr
)) {
2942 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n",
2943 ptr
, msize
, this_msize
);
2947 goto return_tiny_alloc
;
2950 next
->previous
= ptr
->previous
;
2953 goto add_leftover_and_proceed
;
2957 try_tiny_malloc_from_end
:
2958 // Let's see if we can use tiny_mag_ptr->mag_bytes_free_at_end
2959 if (tiny_mag_ptr
->mag_bytes_free_at_end
>= TINY_BYTES_FOR_MSIZE(msize
)) {
2960 ptr
= (free_list_t
*)((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) -
2961 tiny_mag_ptr
->mag_bytes_free_at_end
);
2962 tiny_mag_ptr
->mag_bytes_free_at_end
-= TINY_BYTES_FOR_MSIZE(msize
);
2963 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2964 // let's add an in use block after ptr to serve as boundary
2965 set_tiny_meta_header_in_use_1((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2969 if (LOG(szone
, ptr
)) {
2970 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr
, msize
);
2973 goto return_tiny_alloc
;
2977 add_leftover_and_proceed
:
2978 if (!this_msize
|| (this_msize
> msize
)) {
2979 leftover_msize
= this_msize
- msize
;
2980 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2982 if (LOG(szone
,ptr
)) {
2983 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2986 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover_ptr
, leftover_msize
);
2991 tiny_mag_ptr
->mag_num_objects
++;
2992 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(this_msize
);
2994 // Update this region's bytes in use count
2995 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2996 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(this_msize
);
2997 node
->bytes_used
= bytes_used
;
2999 // Emptiness discriminant
3000 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
3001 /* After this allocation the region is still sparse, so it must have been even more so before
3002 the allocation. That implies the region is already correctly marked. Do nothing. */
3004 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
3005 recirculation candidates list. */
3006 node
->recirc_suitable
= FALSE
;
3009 if (LOG(szone
,ptr
)) {
3010 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
3014 set_tiny_meta_header_in_use(ptr
, this_msize
);
3016 set_tiny_meta_header_in_use_1(ptr
);
3019 #undef DENSITY_THRESHOLD
3022 static INLINE
void *
3023 tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
3026 mag_index_t mag_index
= mag_get_thread_index(szone
);
3027 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3030 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
3031 szone_error(szone
, 1, "malloc called for magazine index -1", NULL
, NULL
);
3036 szone_error(szone
, 1, "invariant broken (!msize) in allocation (region)", NULL
, NULL
);
3041 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3044 ptr
= tiny_mag_ptr
->mag_last_free
;
3046 if ((((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) == msize
) {
3048 tiny_mag_ptr
->mag_last_free
= NULL
;
3049 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3050 CHECK(szone
, __PRETTY_FUNCTION__
);
3051 ptr
= (void *)((uintptr_t)ptr
& ~ (TINY_QUANTUM
- 1));
3052 if (cleared_requested
) {
3053 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3056 if (LOG(szone
,ptr
)) {
3057 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr
, msize
);
3062 #endif /* TINY_CACHE */
3064 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3066 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3067 CHECK(szone
, __PRETTY_FUNCTION__
);
3068 if (cleared_requested
) {
3069 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3074 if (tiny_get_region_from_depot(szone
, tiny_mag_ptr
, mag_index
)) {
3075 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3077 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3078 CHECK(szone
, __PRETTY_FUNCTION__
);
3079 if (cleared_requested
) {
3080 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3086 ptr
= tiny_malloc_from_region_no_lock(szone
, tiny_mag_ptr
, mag_index
, msize
);
3087 // we don't clear because this freshly allocated space is pristine
3088 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3089 CHECK(szone
, __PRETTY_FUNCTION__
);
3093 static NOINLINE
void
3094 free_tiny_botch(szone_t
*szone
, free_list_t
*ptr
)
3096 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3097 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3098 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3099 szone_error(szone
, 1, "double free", ptr
, NULL
);
3103 free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
)
3107 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
3108 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3110 // ptr is known to be in tiny_region
3112 msize
= TINY_MSIZE_FOR_BYTES(known_size
+ TINY_QUANTUM
- 1);
3114 msize
= get_tiny_meta_header(ptr
, &is_free
);
3116 free_tiny_botch(szone
, ptr
);
3122 malloc_printf("*** free_tiny() block in use is too large: %p\n", ptr
);
3127 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3130 // Depot does not participate in TINY_CACHE since it can't be directly malloc()'d
3131 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3132 if (msize
< TINY_QUANTUM
) { // to see if the bits fit in the last 4 bits
3133 void *ptr2
= tiny_mag_ptr
->mag_last_free
; // Might be NULL
3134 region_t rgn2
= tiny_mag_ptr
->mag_last_free_rgn
;
3136 /* check that we don't already have this pointer in the cache */
3137 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (TINY_QUANTUM
- 1))) {
3138 free_tiny_botch(szone
, ptr
);
3142 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
3143 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
3145 tiny_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
3146 tiny_mag_ptr
->mag_last_free_rgn
= tiny_region
;
3149 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3150 CHECK(szone
, __PRETTY_FUNCTION__
);
3154 msize
= (uintptr_t)ptr2
& (TINY_QUANTUM
- 1);
3155 ptr
= (void *)(((uintptr_t)ptr2
) & ~(TINY_QUANTUM
- 1));
3159 #endif /* TINY_CACHE */
3161 // Now in the time it took to acquire the lock, the region may have migrated
3162 // from one magazine to another. I.e. trailer->mag_index is volatile.
3163 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
3164 // is stale. If so, keep on tryin' ...
3165 region_trailer_t
*trailer
= REGION_TRAILER_FOR_TINY_REGION(tiny_region
);
3166 mag_index_t refreshed_index
;
3168 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
3170 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3172 mag_index
= refreshed_index
;
3173 tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3174 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3177 tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
);
3178 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3179 CHECK(szone
, __PRETTY_FUNCTION__
);
3183 print_tiny_free_list(szone_t
*szone
)
3186 _SIMPLE_STRING b
= _simple_salloc();
3187 mag_index_t mag_index
;
3190 _simple_sappend(b
, "tiny free sizes:\n");
3191 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3193 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
3194 while (slot
< NUM_TINY_SLOTS
) {
3195 ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3197 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== NUM_TINY_SLOTS
-1) ? ">=" : "",
3198 (slot
+1)*TINY_QUANTUM
, free_list_count(szone
, ptr
));
3202 _simple_sappend(b
,"\n");
3204 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3210 print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
)
3212 unsigned counts
[1024];
3213 unsigned in_use
= 0;
3214 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
3215 uintptr_t current
= start
;
3216 uintptr_t limit
= (uintptr_t)TINY_REGION_END(region
) - bytes_at_end
;
3221 uintptr_t pgTot
= 0;
3223 if (region
== HASHRING_REGION_DEALLOCATED
) {
3224 if ((b
= _simple_salloc()) != NULL
) {
3225 _simple_sprintf(b
, "Tiny region [unknown address] was returned to the OS\n");
3226 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3232 memset(counts
, 0, sizeof(counts
));
3233 while (current
< limit
) {
3234 msize
= get_tiny_meta_header((void *)current
, &is_free
);
3235 if (is_free
& !msize
&& (current
== start
)) {
3236 // first block is all free
3237 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3238 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
3241 pgTot
+= (pgHi
- pgLo
);
3246 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
3251 if (msize
> NUM_TINY_SLOTS
)
3252 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current
, msize
);
3257 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3258 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3261 pgTot
+= (pgHi
- pgLo
);
3264 current
+= TINY_BYTES_FOR_MSIZE(msize
);
3266 if ((b
= _simple_salloc()) != NULL
) {
3267 _simple_sprintf(b
, "Tiny region [%p-%p, %y] \t", (void *)start
, TINY_REGION_END(region
), (int)TINY_REGION_SIZE
);
3268 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_TINY_REGION(region
));
3269 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_TINY_REGION(region
));
3271 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
);
3272 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_TINY_REGION(region
)) {
3273 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
3275 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
3277 if (verbose
&& in_use
) {
3278 _simple_sappend(b
, "\n\tSizes in use: ");
3279 for (ci
= 0; ci
< 1024; ci
++)
3281 _simple_sprintf(b
, "%d[%d] ", TINY_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
3283 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3289 tiny_free_list_check(szone_t
*szone
, grain_t slot
)
3291 mag_index_t mag_index
;
3293 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3294 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3295 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3298 free_list_t
*ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3300 free_list_t
*previous
= NULL
;
3303 is_free
= tiny_meta_header_is_free(ptr
);
3305 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3306 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3309 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
3310 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3311 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3314 if (!tiny_region_for_ptr_no_lock(szone
, ptr
)) {
3315 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3316 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3319 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
3320 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3321 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3325 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3329 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3334 /********************* SMALL FREE LIST UTILITIES ************************/
3337 * Mark a block as free. Only the first quantum of a block is marked thusly,
3338 * the remainder are marked "middle".
3341 small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
)
3343 meta_headers
[index
] = msize
| SMALL_IS_FREE
;
3347 * Mark a block as in use. Only the first quantum of a block is marked thusly,
3348 * the remainder are marked "middle".
3351 small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
)
3353 meta_headers
[index
] = msize
;
3357 * Mark a quantum as being the second or later in a block.
3360 small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
)
3362 meta_headers
[index
] = 0;
3366 * Adds an item to the proper free list, and also marks the meta-header of the
3368 * Assumes szone has been locked
3371 small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3373 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3374 free_list_t
*free_ptr
= ptr
;
3375 free_list_t
*free_head
= small_mag_ptr
->mag_free_list
[slot
];
3379 if (LOG(szone
,ptr
)) {
3380 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3382 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
3383 szone_error(szone
, 1, "small_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
3386 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), msize
);
3390 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
3391 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
3392 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
3394 if (!SMALL_PTR_IS_FREE(free_head
)) {
3395 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
3396 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
3399 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
3401 BITMAPN_SET(small_mag_ptr
->mag_bitmap
, slot
);
3403 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
3404 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
3406 small_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
3408 // Store msize at the end of the block denoted by "ptr" (i.e. at a negative offset from "follower")
3409 follower
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
3410 SMALL_PREVIOUS_MSIZE(follower
) = msize
;
3414 * Removes the item pointed to by ptr in the proper free list.
3415 * Assumes szone has been locked
3418 small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3420 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3421 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
3423 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
3424 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
3427 if (LOG(szone
,ptr
)) {
3428 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3433 // The block to remove is the head of the free list
3435 if (small_mag_ptr
->mag_free_list
[slot
] != ptr
) {
3436 szone_error(szone
, 1, "small_free_list_remove_ptr: Internal invariant broken (small_mag_ptr->mag_free_list[slot])", ptr
,
3437 "ptr=%p slot=%d msize=%d small_mag_ptr->mag_free_list[slot]=%p\n",
3438 ptr
, slot
, msize
, (void *)small_mag_ptr
->mag_free_list
[slot
]);
3442 small_mag_ptr
->mag_free_list
[slot
] = next
;
3443 if (!next
) BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
3445 // We know free_ptr is already checksummed, so we don't need to do it
3447 previous
->next
= free_ptr
->next
;
3450 // We know free_ptr is already checksummed, so we don't need to do it
3452 next
->previous
= free_ptr
->previous
;
3457 * small_region_for_ptr_no_lock - Returns the small region containing the pointer,
3458 * or NULL if not found.
3460 static INLINE region_t
3461 small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
3463 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3464 szone
->small_region_generation
->num_regions_allocated
,
3465 szone
->small_region_generation
->num_regions_allocated_shift
,
3466 SMALL_REGION_FOR_PTR(ptr
));
3471 small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
) {
3472 void *last_block
, *previous_block
;
3473 msize_t last_msize
, previous_msize
, last_index
;
3475 last_block
= SMALL_REGION_END(small_mag_ptr
->mag_last_region
) - small_mag_ptr
->mag_bytes_free_at_end
;
3476 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
3478 // It is possible that the block prior to the last block in the region has
3479 // been free'd, but was not coalesced with the free bytes at the end of the
3480 // block, since we treat the bytes at the end of the region as "in use" in
3481 // the meta headers. Attempt to coalesce the last block with the previous
3482 // block, so we don't violate the "no consecutive free blocks" invariant.
3484 // FIXME: If we could calculate the previous small free size in the same
3485 // manner as tiny_previous_preceding_free, it would eliminate the
3486 // index & previous msize checks, which are a guard against reading
3487 // bogus data out of in-use or written-on-freed memory.
3489 // FIXME: Need to investigate how much work would be required to increase
3490 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
3491 // than performing this workaround.
3493 last_index
= SMALL_META_INDEX_FOR_PTR(last_block
);
3494 previous_msize
= SMALL_PREVIOUS_MSIZE(last_block
);
3496 if (last_index
&& (previous_msize
<= last_index
)) {
3497 previous_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3498 if (*SMALL_METADATA_FOR_PTR(previous_block
) == (previous_msize
| SMALL_IS_FREE
)) {
3499 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(last_block
);
3501 small_meta_header_set_middle(meta_headers
, last_index
);
3502 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous_block
, previous_msize
);
3503 last_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3504 last_msize
+= previous_msize
;
3508 // splice last_block into the free list
3509 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3510 small_mag_ptr
->mag_bytes_free_at_end
= 0;
3511 small_mag_ptr
->mag_last_region
= NULL
;
3515 small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3516 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3517 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3518 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3519 uintptr_t current
= start
;
3520 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3521 int total_alloc
= 0;
3523 while (current
< limit
) {
3524 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3525 msize_t msize_and_free
= meta_headers
[index
];
3526 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3527 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3531 malloc_printf("*** small_free_detach_region error with %p: msize=%d is_free =%d\n",
3532 (void *)current
, msize
, is_free
);
3537 small_free_list_remove_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3541 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3547 small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3548 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3549 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3550 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3551 uintptr_t current
= start
;
3552 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3553 size_t total_alloc
= 0;
3555 while (current
< limit
) {
3556 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3557 msize_t msize_and_free
= meta_headers
[index
];
3558 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3559 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3563 malloc_printf("*** small_free_reattach_region error with %p: msize=%d is_free =%d\n",
3564 (void *)current
, msize
, is_free
);
3569 small_free_list_add_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3571 total_alloc
+= SMALL_BYTES_FOR_MSIZE(msize
);
3573 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3579 small_free_scan_depot_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
3580 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3581 uintptr_t current
= start
;
3582 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3583 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(start
);
3584 boolean_t did_advise
= FALSE
;
3586 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
3588 while (current
< limit
) {
3589 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3590 msize_t msize_and_free
= meta_headers
[index
];
3591 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3592 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3594 if (is_free
&& !msize
&& (current
== start
)) {
3596 // first block is all free
3597 malloc_printf("*** small_free_scan_depot_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
3598 (void *)current
, msize
, is_free
);
3600 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3601 uintptr_t pgHi
= trunc_page(start
+ SMALL_REGION_SIZE
- sizeof(msize_t
));
3604 #if TARGET_OS_EMBEDDED
3605 madvise_free_range(szone
, r
, pgLo
, pgHi
, NULL
);
3607 madvise_free_range(szone
, r
, pgLo
, pgHi
);
3615 malloc_printf("*** small_free_scan_depot_madvise_free error with %p: msize=%d is_free =%d\n",
3616 (void *)current
, msize
, is_free
);
3621 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3622 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3625 #if TARGET_OS_EMBEDDED
3626 madvise_free_range(szone
, r
, pgLo
, pgHi
, NULL
);
3628 madvise_free_range(szone
, r
, pgLo
, pgHi
);
3633 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3637 /* Move the node to the tail of the Deopt's recirculation list to delay its re-use. */
3638 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(r
);
3639 recirc_list_extract(szone
, depot_ptr
, node
); // excise node from list
3640 recirc_list_splice_last(szone
, depot_ptr
, node
); // connect to magazine as last node
3645 small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
3647 #warning Tune Depot headroom
3648 if (0 < node
->bytes_used
||
3649 depot_ptr
->recirculation_entries
< (szone
->num_small_magazines
* 2)) {
3653 // disconnect first node from Depot
3654 recirc_list_extract(szone
, depot_ptr
, node
);
3656 // Iterate the region pulling its free entries off the (locked) Depot's free list
3657 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3658 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
3660 if (0 == objects_in_use
) {
3661 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
3662 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
3663 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3664 szone
->small_region_generation
->num_regions_allocated
,
3665 szone
->small_region_generation
->num_regions_allocated_shift
, sparse_region
);
3666 *pSlot
= HASHRING_REGION_DEALLOCATED
;
3667 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3668 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
3669 __sync_fetch_and_add( &(szone
->num_small_regions_dealloc
), 1); // Atomically increment num_small_regions_dealloc
3672 OSAtomicIncrement64( (volatile int64_t *)&(szone
->num_small_regions_dealloc
) );
3674 OSAtomicIncrement32( (volatile int32_t *)&(szone
->num_small_regions_dealloc
) );
3678 // Transfer ownership of the region back to the OS
3679 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
); // Avoid denial of service to Depot while in kernel
3680 deallocate_pages(szone
, sparse_region
, SMALL_REGION_SIZE
, 0);
3681 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
3683 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
); // DTrace USDT Probe
3686 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
3691 small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
3693 // The entire magazine crossed the "emptiness threshold". Transfer a region
3694 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
3695 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
3696 region_trailer_t
*node
= small_mag_ptr
->firstNode
;
3698 while (node
&& !node
->recirc_suitable
) {
3704 malloc_printf("*** small_free_do_recirc_to_depot end of list\n");
3709 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3711 // Deal with unclaimed memory -- mag_bytes_free_at_end
3712 if (sparse_region
== small_mag_ptr
->mag_last_region
&& small_mag_ptr
->mag_bytes_free_at_end
) {
3713 small_finalize_region(szone
, small_mag_ptr
);
3716 // disconnect first node from magazine
3717 recirc_list_extract(szone
, small_mag_ptr
, node
);
3719 // Iterate the region pulling its free entries off its (locked) magazine's free list
3720 int objects_in_use
= small_free_detach_region(szone
, small_mag_ptr
, sparse_region
);
3721 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3723 // hand over the region to the (locked) Depot
3724 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
3725 // this will cause small_free_list_add_ptr called by small_free_reattach_region to use
3726 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
3727 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
3729 // Iterate the region putting its free entries on Depot's free list
3730 size_t bytes_inplay
= small_free_reattach_region(szone
, depot_ptr
, sparse_region
);
3732 small_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
3733 small_mag_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3734 small_mag_ptr
->mag_num_objects
-= objects_in_use
;
3736 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
3737 depot_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3738 depot_ptr
->mag_num_objects
+= objects_in_use
;
3740 // connect to Depot as first node
3741 recirc_list_splice_first(szone
, depot_ptr
, node
);
3743 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
3745 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
3746 small_free_scan_depot_madvise_free(szone
, depot_ptr
, sparse_region
);
3748 // If the region is entirely empty vm_deallocate() it
3749 small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
3751 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3755 small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
3757 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3759 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
3760 if (szone
->num_small_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
3764 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
3765 szone_error(szone
, 1, "small_get_region_from_depot called for magazine index -1", NULL
, NULL
);
3770 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
3772 // Appropriate one of the Depot's regions. Prefer LIFO selection for best cache utilization.
3773 region_trailer_t
*node
= depot_ptr
->firstNode
;
3775 if (NULL
== node
) { // Depot empty?
3776 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3780 // disconnect first node from Depot
3781 recirc_list_extract(szone
, depot_ptr
, node
);
3783 // Iterate the region pulling its free entries off the (locked) Depot's free list
3784 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3785 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
3787 // Transfer ownership of the region
3788 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = mag_index
;
3790 // Iterate the region putting its free entries on its new (locked) magazine's free list
3791 size_t bytes_inplay
= small_free_reattach_region(szone
, small_mag_ptr
, sparse_region
);
3793 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
3794 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3795 depot_ptr
->mag_num_objects
-= objects_in_use
;
3797 small_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
3798 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3799 small_mag_ptr
->mag_num_objects
+= objects_in_use
;
3801 // connect to magazine as first node (it's maximally sparse at this moment)
3802 recirc_list_splice_first(szone
, small_mag_ptr
, node
);
3804 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3806 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
3808 #if !TARGET_OS_EMBEDDED
3809 if (-1 == madvise((void *)sparse_region
, SMALL_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
3810 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
3812 szone_error(szone
, 1, "small_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed", sparse_region
, NULL
);
3821 #warning Tune K and f!
3822 #define K 1.5 // headroom measured in number of 8Mb regions
3823 #define DENSITY_THRESHOLD(a) \
3824 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
3827 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
)
3829 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3830 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
3831 void *original_ptr
= ptr
;
3832 size_t original_size
= SMALL_BYTES_FOR_MSIZE(msize
);
3833 unsigned char *next_block
= ((unsigned char *)ptr
+ original_size
);
3834 msize_t next_index
= index
+ msize
;
3835 msize_t previous_msize
, next_msize
;
3837 boolean_t did_prepend
= FALSE
;
3838 boolean_t did_append
= FALSE
;
3841 if (LOG(szone
,ptr
)) {
3842 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
3845 szone_error(szone
, 1, "trying to free small block that is too small", ptr
,
3846 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
3850 // We try to coalesce this block with the preceeding one
3851 if (index
&& (SMALL_PREVIOUS_MSIZE(ptr
) <= index
)) {
3852 previous_msize
= SMALL_PREVIOUS_MSIZE(ptr
);
3853 if (meta_headers
[index
- previous_msize
] == (previous_msize
| SMALL_IS_FREE
)) {
3854 previous
= (void *)((uintptr_t)ptr
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3855 // previous is really to be coalesced
3858 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
3859 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
3862 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous
, previous_msize
);
3863 small_meta_header_set_middle(meta_headers
, index
);
3865 msize
+= previous_msize
;
3866 index
-= previous_msize
;
3869 // We try to coalesce with the next block
3870 if ((next_block
< SMALL_REGION_END(region
)) && (meta_headers
[next_index
] & SMALL_IS_FREE
)) {
3871 // next block is free, we coalesce
3873 next_msize
= meta_headers
[next_index
] & ~ SMALL_IS_FREE
;
3876 malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n",
3877 ptr
, msize
, next_block
, next_msize
);
3879 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
3880 small_meta_header_set_middle(meta_headers
, next_index
);
3881 msize
+= next_msize
;
3883 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) {
3885 szone_error(szone
, 1, "incorrect size information - block header was damaged", ptr
, NULL
);
3887 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
3890 small_free_list_add_ptr(szone
, small_mag_ptr
, ptr
, msize
);
3891 small_mag_ptr
->mag_num_objects
--;
3892 // we use original_size and not msize to avoid double counting the coalesced blocks
3893 small_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
3895 // Update this region's bytes in use count
3896 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
3897 size_t bytes_used
= node
->bytes_used
- original_size
;
3898 node
->bytes_used
= bytes_used
;
3900 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
3901 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
3902 if (szone
->num_small_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
3904 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3905 // Emptiness discriminant
3906 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
3907 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
3908 recirculation candidates list. */
3909 node
->recirc_suitable
= TRUE
;
3911 /* After this free, we've found the region is still dense, so it must have been even more so before
3912 the free. That implies the region is already correctly marked. Do nothing. */
3915 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
3916 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
3917 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
3919 size_t a
= small_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
3920 size_t u
= small_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
3922 if (a
- u
> ((3 * SMALL_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
))
3923 small_free_do_recirc_to_depot(szone
, small_mag_ptr
, mag_index
);
3927 // Freed to Depot. N.B. Lock on small_magazines[DEPOT_MAGAZINE_INDEX] is already held
3928 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
3929 uintptr_t round_safe
= round_page(safe_ptr
);
3931 uintptr_t safe_extent
= (uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
3932 uintptr_t trunc_extent
= trunc_page(safe_extent
);
3934 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
3935 if (round_safe
< trunc_extent
) { // Safe area covers a page (perhaps many)
3936 if (did_prepend
& did_append
) { // Coalesced preceding with original_ptr *and* with following
3937 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
3938 uintptr_t rnd_safe_follow
=
3939 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
3941 #if TARGET_OS_EMBEDDED
3942 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
), &szone
->last_small_advise
);
3944 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
));
3946 } else if (did_prepend
) { // Coalesced preceding with original_ptr
3947 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
3949 #if TARGET_OS_EMBEDDED
3950 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
, &szone
->last_small_advise
);
3952 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
);
3954 } else if (did_append
) { // Coalesced original_ptr with following
3955 uintptr_t rnd_safe_follow
=
3956 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
3958 #if TARGET_OS_EMBEDDED
3959 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
), &szone
->last_small_advise
);
3961 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
));
3963 } else // Isolated free
3964 #if TARGET_OS_EMBEDDED
3965 madvise_free_range(szone
, region
, round_safe
, trunc_extent
, &szone
->last_small_advise
);
3967 madvise_free_range(szone
, region
, round_safe
, trunc_extent
);
3971 #if !TARGET_OS_EMBEDDED
3972 if (0 < bytes_used
) {
3973 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
3974 so as to avoid thrashing between the Depot's free list and a magazines's free list
3975 with detach_region/reattach_region */
3977 /* Depot'd region is just now empty. Consider return to OS. */
3978 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
3979 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3980 small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
3986 // Allocates from the last region or a freshly allocated region
3988 small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
3990 void *ptr
, *aligned_address
;
3992 // Before anything we transform the mag_bytes_free_at_end - if any - to a regular free block
3993 /* FIXME: last_block needs to be coalesced with previous entry if free, <rdar://5462322> */
3994 if (small_mag_ptr
->mag_bytes_free_at_end
)
3995 small_finalize_region(szone
, small_mag_ptr
);
3997 // time to create a new region
3998 aligned_address
= allocate_pages(szone
, SMALL_REGION_SIZE
, SMALL_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_SMALL
);
3999 if (!aligned_address
)
4002 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
); // DTrace USDT Probe
4004 // Here find the only place in smallville that (infrequently) takes the small_regions_lock.
4005 // Only one thread at a time should be permitted to assess the density of the hash
4006 // ring and adjust if needed.
4007 // Only one thread at a time should be permitted to insert its new region on
4009 // It is safe for all other threads to read the hash ring (hashed_regions) and
4010 // the associated sizes (num_regions_allocated and num_small_regions).
4012 LOCK(szone
->small_regions_lock
);
4013 // Check to see if the hash ring of small regions needs to grow. Try to
4014 // avoid the hash ring becoming too dense.
4015 if (szone
->small_region_generation
->num_regions_allocated
< (2 * szone
->num_small_regions
)) {
4016 region_t
*new_regions
;
4018 size_t new_shift
= szone
->small_region_generation
->num_regions_allocated_shift
; // In/Out parameter
4019 new_regions
= hash_regions_grow_no_lock(szone
, szone
->small_region_generation
->hashed_regions
,
4020 szone
->small_region_generation
->num_regions_allocated
,
4023 // Do not deallocate the current hashed_regions allocation since someone
4024 // may be iterating it. Instead, just leak it.
4026 // Prepare to advance to the "next generation" of the hash ring.
4027 szone
->small_region_generation
->nextgen
->hashed_regions
= new_regions
;
4028 szone
->small_region_generation
->nextgen
->num_regions_allocated
= new_size
;
4029 szone
->small_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
4031 // Throw the switch to atomically advance to the next generation.
4032 szone
->small_region_generation
= szone
->small_region_generation
->nextgen
;
4033 // Ensure everyone sees the advance.
4034 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
4035 __sync_synchronize();
4040 // Tag the region at "aligned_address" as belonging to us,
4041 // and so put it under the protection of the magazine lock we are holding.
4042 // Do this before advertising "aligned_address" on the hash ring(!)
4043 MAGAZINE_INDEX_FOR_SMALL_REGION(aligned_address
) = mag_index
;
4045 // Insert the new region into the hash ring, and update malloc statistics
4046 hash_region_insert_no_lock(szone
->small_region_generation
->hashed_regions
,
4047 szone
->small_region_generation
->num_regions_allocated
,
4048 szone
->small_region_generation
->num_regions_allocated_shift
,
4051 szone
->num_small_regions
++;
4053 UNLOCK(szone
->small_regions_lock
);
4055 small_mag_ptr
->mag_last_region
= aligned_address
;
4056 BYTES_USED_FOR_SMALL_REGION(aligned_address
) = SMALL_BYTES_FOR_MSIZE(msize
);
4057 ptr
= aligned_address
;
4058 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), 0, msize
);
4059 small_mag_ptr
->mag_num_objects
++;
4060 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(msize
);
4061 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
4063 // add a big free block
4064 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
) , msize
, NUM_SMALL_BLOCKS
- msize
);
4065 small_mag_ptr
->mag_bytes_free_at_end
= SMALL_BYTES_FOR_MSIZE(NUM_SMALL_BLOCKS
- msize
);
4067 // connect to magazine as first node (it's maximally sparse at this moment)
4068 recirc_list_splice_first(szone
, small_mag_ptr
, REGION_TRAILER_FOR_SMALL_REGION(aligned_address
));
4073 static INLINE boolean_t
4074 small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
4076 // returns 1 on success
4077 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4079 msize_t old_msize
, new_msize
;
4080 unsigned next_index
;
4082 msize_t next_msize_and_free
;
4084 msize_t next_msize
, leftover_msize
;
4087 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4088 old_msize
= SMALL_MSIZE_FOR_BYTES(old_size
);
4089 new_msize
= SMALL_MSIZE_FOR_BYTES(new_size
+ SMALL_QUANTUM
- 1);
4090 next_index
= index
+ old_msize
;
4092 if (next_index
>= NUM_SMALL_BLOCKS
) {
4095 next_block
= (char *)ptr
+ old_size
;
4098 if ((uintptr_t)next_block
& (SMALL_QUANTUM
- 1)) {
4099 szone_error(szone
, 1, "internal invariant broken in realloc(next_block)", next_block
, NULL
);
4101 if (meta_headers
[index
] != old_msize
)
4102 malloc_printf("*** small_try_realloc_in_place incorrect old %d %d\n",
4103 meta_headers
[index
], old_msize
);
4106 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4107 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4108 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4111 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
4114 next_msize_and_free
= meta_headers
[next_index
];
4115 is_free
= next_msize_and_free
& SMALL_IS_FREE
;
4117 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4118 return 0; // next_block is in use;
4120 next_msize
= next_msize_and_free
& ~ SMALL_IS_FREE
;
4121 if (old_msize
+ next_msize
< new_msize
) {
4122 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4123 return 0; // even with next block, not enough
4126 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
4129 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4130 small_meta_header_set_middle(meta_headers
, next_index
);
4131 leftover_msize
= old_msize
+ next_msize
- new_msize
;
4132 if (leftover_msize
) {
4133 /* there's some left, so put the remainder back */
4134 leftover
= (unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
);
4136 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover
, leftover_msize
);
4139 if (SMALL_BYTES_FOR_MSIZE(new_msize
) > szone
->large_threshold
) {
4140 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize
);
4143 small_meta_header_set_in_use(meta_headers
, index
, new_msize
);
4145 if (LOG(szone
,ptr
)) {
4146 malloc_printf("in szone_realloc(), ptr=%p, msize=%d\n", ptr
, *SMALL_METADATA_FOR_PTR(ptr
));
4149 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4151 // Update this region's bytes in use count
4152 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4153 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4154 node
->bytes_used
= bytes_used
;
4156 // Emptiness discriminant
4157 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4158 /* After this reallocation the region is still sparse, so it must have been even more so before
4159 the reallocation. That implies the region is already correctly marked. Do nothing. */
4161 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
4162 recirculation candidates list. */
4163 node
->recirc_suitable
= FALSE
;
4166 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4167 CHECK(szone
, __PRETTY_FUNCTION__
);
4172 small_check_region(szone_t
*szone
, region_t region
)
4174 unsigned char *ptr
= SMALL_REGION_ADDRESS(region
);
4175 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4176 unsigned char *region_end
= SMALL_REGION_END(region
);
4177 msize_t prev_free
= 0;
4179 msize_t msize_and_free
;
4181 free_list_t
*free_head
;
4182 void *previous
, *next
;
4184 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4185 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4188 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4190 if (region
== small_mag_ptr
->mag_last_region
)
4191 region_end
-= small_mag_ptr
->mag_bytes_free_at_end
;
4193 while (ptr
< region_end
) {
4194 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4195 msize_and_free
= meta_headers
[index
];
4196 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4198 msize
= msize_and_free
;
4200 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
4201 ptr
, szone
->num_small_regions
, region_end
);
4204 if (SMALL_BYTES_FOR_MSIZE(msize
) > szone
->large_threshold
) {
4205 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
4206 ptr
, msize_and_free
);
4209 ptr
+= SMALL_BYTES_FOR_MSIZE(msize
);
4213 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4214 free_head
= (free_list_t
*)ptr
;
4215 follower
= (msize_t
*)FOLLOWING_SMALL_PTR(ptr
, msize
);
4217 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr
, msize
);
4221 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr
);
4224 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
4225 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
4226 if (previous
&& !SMALL_PTR_IS_FREE(previous
)) {
4227 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
4228 ptr
, free_head
->previous
);
4231 if (next
&& !SMALL_PTR_IS_FREE(next
)) {
4232 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr
);
4235 if (SMALL_PREVIOUS_MSIZE(follower
) != msize
) {
4236 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
4237 "(end marker incorrect) should be %d; in fact %d\n",
4238 ptr
, follower
, SMALL_REGION_ADDRESS(region
), region_end
, msize
, SMALL_PREVIOUS_MSIZE(follower
));
4241 ptr
= (unsigned char *)follower
;
4242 prev_free
= SMALL_IS_FREE
;
4248 static kern_return_t
4249 small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
4250 memory_reader_t reader
, vm_range_recorder_t recorder
)
4255 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4260 vm_range_t admin_range
;
4261 vm_range_t ptr_range
;
4262 unsigned char *mapped_region
;
4263 msize_t
*block_header
;
4264 unsigned block_index
;
4265 unsigned block_limit
;
4266 msize_t msize_and_free
;
4268 vm_address_t mag_last_free_ptr
= 0;
4269 msize_t mag_last_free_msize
= 0;
4271 region_hash_generation_t
*srg_ptr
;
4272 err
= reader(task
, (vm_address_t
)szone
->small_region_generation
, sizeof(region_hash_generation_t
), (void **)&srg_ptr
);
4273 if (err
) return err
;
4275 num_regions
= srg_ptr
->num_regions_allocated
;
4276 err
= reader(task
, (vm_address_t
)srg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
4277 if (err
) return err
;
4279 for (index
= 0; index
< num_regions
; ++index
) {
4280 region
= regions
[index
];
4281 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
4282 range
.address
= (vm_address_t
)SMALL_REGION_ADDRESS(region
);
4283 range
.size
= SMALL_REGION_SIZE
;
4284 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4285 admin_range
.address
= range
.address
+ SMALL_METADATA_START
;
4286 admin_range
.size
= SMALL_METADATA_SIZE
;
4287 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
4289 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
4290 ptr_range
.address
= range
.address
;
4291 ptr_range
.size
= NUM_SMALL_BLOCKS
* SMALL_QUANTUM
;
4292 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
4294 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4295 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
4299 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(mapped_region
);
4300 magazine_t
*small_mag_ptr
;
4301 err
= reader(task
, (vm_address_t
)&(szone
->small_magazines
[mag_index
]), sizeof(magazine_t
),
4302 (void **)&small_mag_ptr
);
4303 if (err
) return err
;
4305 void *mag_last_free
= small_mag_ptr
->mag_last_free
;
4306 if (mag_last_free
) {
4307 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4308 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4311 block_header
= (msize_t
*)(mapped_region
+ SMALL_METADATA_START
+ sizeof(region_trailer_t
));
4313 block_limit
= NUM_SMALL_BLOCKS
;
4314 if (region
== small_mag_ptr
->mag_last_region
)
4315 block_limit
-= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
4316 while (block_index
< block_limit
) {
4317 msize_and_free
= block_header
[block_index
];
4318 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4319 if (! (msize_and_free
& SMALL_IS_FREE
) &&
4320 range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
) != mag_last_free_ptr
) {
4322 buffer
[count
].address
= range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
);
4323 buffer
[count
].size
= SMALL_BYTES_FOR_MSIZE(msize
);
4325 if (count
>= MAX_RECORDER_BUFFER
) {
4326 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4330 block_index
+= msize
;
4333 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4343 small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4347 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4348 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4349 free_list_t
**the_slot
= free_list
+ slot
;
4351 free_list_t
**limit
;
4353 msize_t leftover_msize
;
4354 free_list_t
*leftover_ptr
;
4356 // Assumes we've locked the region
4357 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4359 // Look for an exact match by checking the freelist for this msize.
4363 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4365 next
->previous
= ptr
->previous
;
4367 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4371 goto return_small_alloc
;
4374 // Mask off the bits representing slots holding free blocks smaller than
4375 // the size we need. If there are no larger free blocks, try allocating
4376 // from the free space at the end of the small region.
4377 if (szone
->is_largemem
) {
4378 // BITMAPN_CTZ implementation
4379 unsigned idx
= slot
>> 5;
4381 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4382 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4383 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4388 // Check for fallthrough: No bits set in bitmap
4389 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4390 goto try_small_from_end
;
4392 // Start looking at the first set bit, plus 32 bits for every word of
4393 // zeroes or entries that were too small.
4394 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4396 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4398 goto try_small_from_end
;
4400 slot
= BITMAP32_CTZ((&bitmap
));
4402 // FIXME: Explain use of - 1 here, last slot has special meaning
4403 limit
= free_list
+ szone
->num_small_slots
- 1;
4406 if (free_list
< limit
) {
4410 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4413 next
->previous
= ptr
->previous
;
4415 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4417 this_msize
= SMALL_PTR_SIZE(ptr
);
4418 goto add_leftover_and_proceed
;
4421 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4425 // We are now looking at the last slot, which contains blocks equal to, or
4426 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4427 // If the last freelist is not empty, and the head contains a block that is
4428 // larger than our request, then the remainder is put back on the free list.
4432 this_msize
= SMALL_PTR_SIZE(ptr
);
4433 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4434 if (this_msize
- msize
>= szone
->num_small_slots
) {
4435 // the leftover will go back to the free list, so we optimize by
4436 // modifying the free list rather than a pop and push of the head
4437 leftover_msize
= this_msize
- msize
;
4438 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4439 *limit
= leftover_ptr
;
4441 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
4443 leftover_ptr
->previous
= ptr
->previous
;
4444 leftover_ptr
->next
= ptr
->next
;
4445 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(leftover_ptr
),
4446 SMALL_META_INDEX_FOR_PTR(leftover_ptr
), leftover_msize
);
4447 // Store msize at the end of the block denoted by "leftover_ptr" (i.e. at a negative offset from follower)
4448 SMALL_PREVIOUS_MSIZE(FOLLOWING_SMALL_PTR(leftover_ptr
, leftover_msize
)) = leftover_msize
; // Access is safe
4450 if (LOG(szone
,ptr
)) {
4451 malloc_printf("in small_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr
, msize
, this_msize
);
4455 goto return_small_alloc
;
4458 next
->previous
= ptr
->previous
;
4461 goto add_leftover_and_proceed
;
4465 // Let's see if we can use small_mag_ptr->mag_bytes_free_at_end
4466 if (small_mag_ptr
->mag_bytes_free_at_end
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
4467 ptr
= (free_list_t
*)(SMALL_REGION_END(small_mag_ptr
->mag_last_region
) -
4468 small_mag_ptr
->mag_bytes_free_at_end
);
4469 small_mag_ptr
->mag_bytes_free_at_end
-= SMALL_BYTES_FOR_MSIZE(msize
);
4470 if (small_mag_ptr
->mag_bytes_free_at_end
) {
4471 // let's mark this block as in use to serve as boundary
4472 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
),
4473 SMALL_META_INDEX_FOR_PTR((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
)),
4474 SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
));
4477 goto return_small_alloc
;
4481 add_leftover_and_proceed
:
4482 if (this_msize
> msize
) {
4483 leftover_msize
= this_msize
- msize
;
4484 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4486 if (LOG(szone
,ptr
)) {
4487 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
4490 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover_ptr
, leftover_msize
);
4495 small_mag_ptr
->mag_num_objects
++;
4496 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(this_msize
);
4498 // Update this region's bytes in use count
4499 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4500 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(this_msize
);
4501 node
->bytes_used
= bytes_used
;
4503 // Emptiness discriminant
4504 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4505 /* After this allocation the region is still sparse, so it must have been even more so before
4506 the allocation. That implies the region is already correctly marked. Do nothing. */
4508 /* Region has crossed threshold from sparsity to density. Mark in not "suitable" on the
4509 recirculation candidates list. */
4510 node
->recirc_suitable
= FALSE
;
4513 if (LOG(szone
,ptr
)) {
4514 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
4517 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), this_msize
);
4520 #undef DENSITY_THRESHOLD
4523 static INLINE
void *
4524 small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
4527 mag_index_t mag_index
= mag_get_thread_index(szone
);
4528 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4530 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4533 ptr
= (void *)small_mag_ptr
->mag_last_free
;
4535 if ((((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) == msize
) {
4537 small_mag_ptr
->mag_last_free
= NULL
;
4538 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4539 CHECK(szone
, __PRETTY_FUNCTION__
);
4540 ptr
= (void *)((uintptr_t)ptr
& ~ (SMALL_QUANTUM
- 1));
4541 if (cleared_requested
) {
4542 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4546 #endif /* SMALL_CACHE */
4548 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
4550 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4551 CHECK(szone
, __PRETTY_FUNCTION__
);
4552 if (cleared_requested
) {
4553 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4558 if (small_get_region_from_depot(szone
, small_mag_ptr
, mag_index
)) {
4559 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
4561 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4562 CHECK(szone
, __PRETTY_FUNCTION__
);
4563 if (cleared_requested
) {
4564 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4570 ptr
= small_malloc_from_region_no_lock(szone
, small_mag_ptr
, mag_index
, msize
);
4571 // we don't clear because this freshly allocated space is pristine
4572 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4573 CHECK(szone
, __PRETTY_FUNCTION__
);
4577 static NOINLINE
void
4578 free_small_botch(szone_t
*szone
, free_list_t
*ptr
)
4580 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4581 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4582 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4583 szone_error(szone
, 1, "double free", ptr
, NULL
);
4587 free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
)
4590 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4591 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4593 // ptr is known to be in small_region
4595 msize
= SMALL_MSIZE_FOR_BYTES(known_size
+ SMALL_QUANTUM
- 1);
4597 msize
= SMALL_PTR_SIZE(ptr
);
4598 if (SMALL_PTR_IS_FREE(ptr
)) {
4599 free_small_botch(szone
, ptr
);
4604 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4607 // Depot does not participate in SMALL_CACHE since it can't be directly malloc()'d
4608 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4610 void *ptr2
= small_mag_ptr
->mag_last_free
; // Might be NULL
4611 region_t rgn2
= small_mag_ptr
->mag_last_free_rgn
;
4613 /* check that we don't already have this pointer in the cache */
4614 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (SMALL_QUANTUM
- 1))) {
4615 free_small_botch(szone
, ptr
);
4619 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
4620 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
4622 small_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
4623 small_mag_ptr
->mag_last_free_rgn
= small_region
;
4626 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4627 CHECK(szone
, __PRETTY_FUNCTION__
);
4631 msize
= (uintptr_t)ptr2
& (SMALL_QUANTUM
- 1);
4632 ptr
= (void *)(((uintptr_t)ptr2
) & ~(SMALL_QUANTUM
- 1));
4633 small_region
= rgn2
;
4635 #endif /* SMALL_CACHE */
4637 // Now in the time it took to acquire the lock, the region may have migrated
4638 // from one magazine to another. I.e. trailer->mag_index is volatile.
4639 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
4640 // is stale. If so, keep on tryin' ...
4641 region_trailer_t
*trailer
= REGION_TRAILER_FOR_SMALL_REGION(small_region
);
4642 mag_index_t refreshed_index
;
4644 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
4646 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4648 mag_index
= refreshed_index
;
4649 small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4650 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4653 small_free_no_lock(szone
, small_mag_ptr
, mag_index
, small_region
, ptr
, msize
);
4654 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4655 CHECK(szone
, __PRETTY_FUNCTION__
);
4659 print_small_free_list(szone_t
*szone
)
4662 _SIMPLE_STRING b
= _simple_salloc();
4663 mag_index_t mag_index
;
4666 _simple_sappend(b
, "small free sizes:\n");
4667 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4669 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
4670 while (slot
< szone
->num_small_slots
) {
4671 ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
4673 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== szone
->num_small_slots
-1) ? ">=" : "",
4674 (slot
+ 1) * SMALL_QUANTUM
, free_list_count(szone
, ptr
));
4678 _simple_sappend(b
,"\n");
4680 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4686 print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
)
4688 unsigned counts
[1024];
4689 unsigned in_use
= 0;
4690 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(region
);
4691 uintptr_t current
= start
;
4692 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(region
) - bytes_at_end
;
4693 msize_t msize_and_free
;
4697 uintptr_t pgTot
= 0;
4699 if (region
== HASHRING_REGION_DEALLOCATED
) {
4700 if ((b
= _simple_salloc()) != NULL
) {
4701 _simple_sprintf(b
, "Small region [unknown address] was returned to the OS\n");
4702 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4708 memset(counts
, 0, sizeof(counts
));
4709 while (current
< limit
) {
4710 msize_and_free
= *SMALL_METADATA_FOR_PTR(current
);
4711 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4713 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
4716 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4722 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
4723 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
4726 pgTot
+= (pgHi
- pgLo
);
4729 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
4731 if ((b
= _simple_salloc()) != NULL
) {
4732 _simple_sprintf(b
, "Small region [%p-%p, %y] \t", (void *)start
, SMALL_REGION_END(region
), (int)SMALL_REGION_SIZE
);
4733 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_SMALL_REGION(region
));
4734 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_SMALL_REGION(region
));
4736 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
);
4737 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_SMALL_REGION(region
)) {
4738 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
4740 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
4742 if (verbose
&& in_use
) {
4743 _simple_sappend(b
, "\n\tSizes in use: ");
4744 for (ci
= 0; ci
< 1024; ci
++)
4746 _simple_sprintf(b
, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
4748 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4754 small_free_list_check(szone_t
*szone
, grain_t slot
)
4756 mag_index_t mag_index
;
4758 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4759 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4760 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4763 free_list_t
*ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
4764 msize_t msize_and_free
;
4765 free_list_t
*previous
= NULL
;
4768 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
4769 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4770 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4771 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4774 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
4775 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4776 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4779 if (!small_region_for_ptr_no_lock(szone
, ptr
)) {
4780 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4781 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4784 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
4785 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4786 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4790 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4794 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4799 /*******************************************************************************
4800 * Large allocator implementation
4801 ******************************************************************************/
4802 #pragma mark large allocator
4807 large_debug_print(szone_t
*szone
)
4810 large_entry_t
*range
;
4811 _SIMPLE_STRING b
= _simple_salloc();
4814 for (index
= 0, range
= szone
->large_entries
; index
< szone
->num_large_entries
; index
++, range
++)
4816 _simple_sprintf(b
, "%d: %p(%y); ", index
, range
->address
, range
->size
);
4818 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4825 * Scan the hash ring looking for an entry for the given pointer.
4827 static large_entry_t
*
4828 large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
4830 // result only valid with lock held
4831 unsigned num_large_entries
= szone
->num_large_entries
;
4832 unsigned hash_index
;
4834 large_entry_t
*range
;
4836 if (!num_large_entries
)
4839 hash_index
= ((uintptr_t)ptr
>> vm_page_shift
) % num_large_entries
;
4843 range
= szone
->large_entries
+ index
;
4844 if (range
->address
== (vm_address_t
)ptr
)
4846 if (0 == range
->address
)
4847 return NULL
; // end of chain
4849 if (index
== num_large_entries
)
4851 } while (index
!= hash_index
);
4857 large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
)
4859 unsigned num_large_entries
= szone
->num_large_entries
;
4860 unsigned hash_index
= (((uintptr_t)(range
.address
)) >> vm_page_shift
) % num_large_entries
;
4861 unsigned index
= hash_index
;
4862 large_entry_t
*entry
;
4864 // assert(szone->num_large_objects_in_use < szone->num_large_entries); /* must be called with room to spare */
4867 entry
= szone
->large_entries
+ index
;
4868 if (0 == entry
->address
) {
4870 return; // end of chain
4873 if (index
== num_large_entries
)
4875 } while (index
!= hash_index
);
4877 // assert(0); /* must not fallthrough! */
4880 // FIXME: can't we simply swap the (now empty) entry with the last entry on the collision chain for this hash slot?
4882 large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
)
4884 unsigned num_large_entries
= szone
->num_large_entries
;
4885 unsigned hash_index
= entry
- szone
->large_entries
;
4886 unsigned index
= hash_index
;
4887 large_entry_t range
;
4889 // assert(entry->address == 0) /* caller must have cleared *entry */
4893 if (index
== num_large_entries
)
4895 range
= szone
->large_entries
[index
];
4896 if (0 == range
.address
)
4898 szone
->large_entries
[index
].address
= (vm_address_t
)0;
4899 szone
->large_entries
[index
].size
= 0;
4900 szone
->large_entries
[index
].did_madvise_reusable
= FALSE
;
4901 large_entry_insert_no_lock(szone
, range
); // this will reinsert in the
4903 } while (index
!= hash_index
);
4905 // assert(0); /* since entry->address == 0, must not fallthrough! */
4908 // FIXME: num should probably be a size_t, since you can theoretically allocate
4909 // more than 2^32-1 large_threshold objects in 64 bit.
4910 static INLINE large_entry_t
*
4911 large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
)
4913 size_t size
= num
* sizeof(large_entry_t
);
4915 // Note that we allocate memory (via a system call) under a spin lock
4916 // That is certainly evil, however it's very rare in the lifetime of a process
4917 // The alternative would slow down the normal case
4918 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC_LARGE
);
4922 large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
)
4924 size_t size
= num
* sizeof(large_entry_t
);
4926 range_to_deallocate
->address
= (vm_address_t
)entries
;
4927 range_to_deallocate
->size
= round_page(size
);
4930 static large_entry_t
*
4931 large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
)
4933 // sets range_to_deallocate
4934 unsigned old_num_entries
= szone
->num_large_entries
;
4935 large_entry_t
*old_entries
= szone
->large_entries
;
4936 // always an odd number for good hashing
4937 unsigned new_num_entries
= (old_num_entries
) ? old_num_entries
* 2 + 1 :
4938 ((vm_page_size
/ sizeof(large_entry_t
)) - 1);
4939 large_entry_t
*new_entries
= large_entries_alloc_no_lock(szone
, new_num_entries
);
4940 unsigned index
= old_num_entries
;
4941 large_entry_t oldRange
;
4943 // if the allocation of new entries failed, bail
4944 if (new_entries
== NULL
)
4947 szone
->num_large_entries
= new_num_entries
;
4948 szone
->large_entries
= new_entries
;
4950 /* rehash entries into the new list */
4952 oldRange
= old_entries
[index
];
4953 if (oldRange
.address
) {
4954 large_entry_insert_no_lock(szone
, oldRange
);
4959 large_entries_free_no_lock(szone
, old_entries
, old_num_entries
, range_to_deallocate
);
4961 range_to_deallocate
->address
= (vm_address_t
)0;
4962 range_to_deallocate
->size
= 0;
4968 // frees the specific entry in the size table
4969 // returns a range to truly deallocate
4971 large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
)
4975 range
.address
= entry
->address
;
4976 range
.size
= entry
->size
;
4978 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
4979 protect((void *)range
.address
, range
.size
, VM_PROT_READ
| VM_PROT_WRITE
, szone
->debug_flags
);
4980 range
.address
-= vm_page_size
;
4981 range
.size
+= 2 * vm_page_size
;
4986 entry
->did_madvise_reusable
= FALSE
;
4987 large_entries_rehash_after_entry_no_lock(szone
, entry
);
4990 if (large_entry_for_pointer_no_lock(szone
, (void *)range
.address
)) {
4991 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
4992 range
.address
, szone
->num_large_entries
);
4993 large_debug_print(szone
);
5000 static NOINLINE kern_return_t
5001 large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
,
5002 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
5005 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
5007 large_entry_t
*entries
;
5010 large_entry_t entry
;
5012 err
= reader(task
, large_entries_address
, sizeof(large_entry_t
) * num_entries
, (void **)&entries
);
5016 index
= num_entries
;
5017 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
5018 range
.address
= large_entries_address
;
5019 range
.size
= round_page(num_entries
* sizeof(large_entry_t
));
5020 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &range
, 1);
5022 if (type_mask
& (MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
)) {
5024 entry
= entries
[index
];
5025 if (entry
.address
) {
5026 range
.address
= entry
.address
;
5027 range
.size
= entry
.size
;
5028 buffer
[count
++] = range
;
5029 if (count
>= MAX_RECORDER_BUFFER
) {
5030 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
5038 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
5045 large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
,
5046 boolean_t cleared_requested
)
5049 vm_range_t range_to_deallocate
;
5051 large_entry_t large_entry
;
5054 num_pages
= 1; // minimal allocation size for this szone
5055 size
= (size_t)num_pages
<< vm_page_shift
;
5056 range_to_deallocate
.size
= 0;
5057 range_to_deallocate
.address
= 0;
5060 if (size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
) { // Look for a large_entry_t on the death-row cache?
5063 int i
, best
= -1, idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5064 size_t best_size
= SIZE_T_MAX
;
5066 while (1) { // Scan large_entry_cache for best fit, starting with most recent entry
5067 size_t this_size
= szone
->large_entry_cache
[idx
].size
;
5069 if (size
== this_size
) { // size match!
5071 best_size
= this_size
;
5075 if (size
<= this_size
&& this_size
< best_size
) { // improved fit?
5077 best_size
= this_size
;
5080 if (idx
== stop_idx
) // exhausted live ring?
5084 idx
--; // bump idx down
5086 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5089 if (best
> -1 && (best_size
- size
) < size
) { //limit fragmentation to 50%
5090 addr
= (void *)szone
->large_entry_cache
[best
].address
;
5091 boolean_t was_madvised_reusable
= szone
->large_entry_cache
[best
].did_madvise_reusable
;
5093 // Compact live ring to fill entry now vacated at large_entry_cache[best]
5094 // while preserving time-order
5095 if (szone
->large_entry_cache_oldest
< szone
->large_entry_cache_newest
) {
5097 // Ring hasn't wrapped. Fill in from right.
5098 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5099 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5101 szone
->large_entry_cache_newest
--; // Pull in right endpoint.
5103 } else if (szone
->large_entry_cache_newest
< szone
->large_entry_cache_oldest
) {
5105 // Ring has wrapped. Arrange to fill in from the contiguous side.
5106 if (best
<= szone
->large_entry_cache_newest
) {
5108 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5109 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5111 if (0 < szone
->large_entry_cache_newest
)
5112 szone
->large_entry_cache_newest
--;
5114 szone
->large_entry_cache_newest
= LARGE_ENTRY_CACHE_SIZE
- 1;
5117 for ( i
= best
; i
> szone
->large_entry_cache_oldest
; --i
)
5118 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
- 1];
5120 if (szone
->large_entry_cache_oldest
< LARGE_ENTRY_CACHE_SIZE
- 1)
5121 szone
->large_entry_cache_oldest
++;
5123 szone
->large_entry_cache_oldest
= 0;
5127 // By trichotomy, large_entry_cache_newest == large_entry_cache_oldest.
5128 // That implies best == large_entry_cache_newest == large_entry_cache_oldest
5129 // and the ring is now empty.
5130 szone
->large_entry_cache
[best
].address
= 0;
5131 szone
->large_entry_cache
[best
].size
= 0;
5132 szone
->large_entry_cache
[best
].did_madvise_reusable
= FALSE
;
5135 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5136 // density of hash table too high; grow table
5137 // we do that under lock to avoid a race
5138 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5139 if (entries
== NULL
) {
5140 SZONE_UNLOCK(szone
);
5145 large_entry
.address
= (vm_address_t
)addr
;
5146 large_entry
.size
= best_size
;
5147 large_entry
.did_madvise_reusable
= FALSE
;
5148 large_entry_insert_no_lock(szone
, large_entry
);
5150 szone
->num_large_objects_in_use
++;
5151 szone
->num_bytes_in_large_objects
+= best_size
;
5152 if (!was_madvised_reusable
)
5153 szone
->large_entry_cache_hoard_bytes
-= best_size
;
5154 SZONE_UNLOCK(szone
);
5156 if (range_to_deallocate
.size
) {
5157 // we deallocate outside the lock
5158 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5161 // Perform the madvise() outside the lock.
5162 // Typically the madvise() is successful and we'll quickly return from this routine.
5163 // In the unusual case of failure, reacquire the lock to unwind.
5164 if (was_madvised_reusable
&& -1 == madvise(addr
, size
, MADV_FREE_REUSE
)) {
5165 /* -1 return: VM map entry change makes this unfit for reuse. */
5167 szone_error(szone
, 1, "large_malloc madvise(..., MADV_FREE_REUSE) failed", addr
, NULL
);
5171 szone
->num_large_objects_in_use
--;
5172 szone
->num_bytes_in_large_objects
-= large_entry
.size
;
5174 // Re-acquire "entry" after interval just above where we let go the lock.
5175 large_entry_t
*entry
= large_entry_for_pointer_no_lock(szone
, addr
);
5176 if (NULL
== entry
) {
5177 szone_error(szone
, 1, "entry for pointer being discarded from death-row vanished", addr
, NULL
);
5178 SZONE_UNLOCK(szone
);
5181 range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5182 SZONE_UNLOCK(szone
);
5184 if (range_to_deallocate
.size
) {
5185 // we deallocate outside the lock
5186 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5189 /* Fall through to allocate_pages() afresh. */
5191 if (cleared_requested
) {
5192 memset(addr
, 0, size
);
5198 SZONE_UNLOCK(szone
);
5202 range_to_deallocate
.size
= 0;
5203 range_to_deallocate
.address
= 0;
5204 #endif /* LARGE_CACHE */
5206 addr
= allocate_pages(szone
, size
, alignment
, szone
->debug_flags
, VM_MEMORY_MALLOC_LARGE
);
5212 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5213 // density of hash table too high; grow table
5214 // we do that under lock to avoid a race
5215 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5216 if (entries
== NULL
) {
5217 SZONE_UNLOCK(szone
);
5222 large_entry
.address
= (vm_address_t
)addr
;
5223 large_entry
.size
= size
;
5224 large_entry
.did_madvise_reusable
= FALSE
;
5225 large_entry_insert_no_lock(szone
, large_entry
);
5227 szone
->num_large_objects_in_use
++;
5228 szone
->num_bytes_in_large_objects
+= size
;
5229 SZONE_UNLOCK(szone
);
5231 if (range_to_deallocate
.size
) {
5232 // we deallocate outside the lock
5233 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5238 static NOINLINE
void
5239 free_large(szone_t
*szone
, void *ptr
)
5241 // We have established ptr is page-aligned and neither tiny nor small
5242 large_entry_t
*entry
;
5243 vm_range_t vm_range_to_deallocate
;
5246 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5249 #ifndef MADV_CAN_REUSE
5250 #define MADV_CAN_REUSE 9 /* per Francois, for testing until xnu is resubmitted to B&I */
5252 if (entry
->size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
&&
5253 -1 != madvise((void *)(entry
->address
), entry
->size
, MADV_CAN_REUSE
)) { // Put the large_entry_t on the death-row cache?
5254 int idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5255 large_entry_t this_entry
= *entry
; // Make a local copy, "entry" is volatile when lock is let go.
5256 boolean_t reusable
= TRUE
;
5257 boolean_t should_madvise
= szone
->large_entry_cache_hoard_bytes
+ this_entry
.size
> szone
->large_entry_cache_hoard_lmit
;
5260 // [Note that repeated entries in death-row risk vending the same entry subsequently
5261 // to two different malloc() calls. By checking here the (illegal) double free
5262 // is accommodated, matching the behavior of the previous implementation.]
5263 while (1) { // Scan large_entry_cache starting with most recent entry
5264 if (szone
->large_entry_cache
[idx
].address
== entry
->address
) {
5265 szone_error(szone
, 1, "pointer being freed already on death-row", ptr
, NULL
);
5266 SZONE_UNLOCK(szone
);
5270 if (idx
== stop_idx
) // exhausted live ring?
5274 idx
--; // bump idx down
5276 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5279 SZONE_UNLOCK(szone
);
5281 if (szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) { // Are we a purgable zone?
5282 int state
= VM_PURGABLE_NONVOLATILE
; // restore to default condition
5284 if (KERN_SUCCESS
!= vm_purgable_control(mach_task_self(), this_entry
.address
, VM_PURGABLE_SET_STATE
, &state
)) {
5285 malloc_printf("*** can't vm_purgable_control(..., VM_PURGABLE_SET_STATE) for large freed block at %p\n", this_entry
.address
);
5290 if (szone
->large_legacy_reset_mprotect
) { // Linked for Leopard?
5291 // Accomodate Leopard apps that (illegally) mprotect() their own guard pages on large malloc'd allocations
5292 kern_return_t err
= vm_protect(mach_task_self(), (vm_address_t
)(this_entry
.address
), this_entry
.size
,
5293 0, PROT_READ
| PROT_WRITE
);
5295 malloc_printf("*** can't reset protection for large freed block at %p\n", this_entry
.address
);
5300 // madvise(..., MADV_REUSABLE) death-row arrivals if hoarding would exceed large_entry_cache_hoard_lmit
5301 if (should_madvise
) {
5302 // Issue madvise to avoid paging out the dirtied free()'d pages in "entry"
5303 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)0, (void *)(this_entry
.address
), this_entry
.size
); // DTrace USDT Probe
5305 if (-1 == madvise((void *)(this_entry
.address
), this_entry
.size
, MADV_FREE_REUSABLE
)) {
5306 /* -1 return: VM map entry change makes this unfit for reuse. */
5308 szone_error(szone
, 1, "free_large madvise(..., MADV_FREE_REUSABLE) failed", (void *)this_entry
.address
, NULL
);
5316 // Re-acquire "entry" after interval just above where we let go the lock.
5317 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5318 if (NULL
== entry
) {
5319 szone_error(szone
, 1, "entry for pointer being freed from death-row vanished", ptr
, NULL
);
5320 SZONE_UNLOCK(szone
);
5324 // Add "entry" to death-row ring
5326 int idx
= szone
->large_entry_cache_newest
; // Most recently occupied
5330 if (szone
->large_entry_cache_newest
== szone
->large_entry_cache_oldest
&&
5331 0 == szone
->large_entry_cache
[idx
].address
) {
5332 // Ring is empty, idx is good as it stands
5336 // Extend the queue to the "right" by bumping up large_entry_cache_newest
5337 if (idx
== LARGE_ENTRY_CACHE_SIZE
- 1)
5338 idx
= 0; // Wrap index
5340 idx
++; // Bump index
5342 if (idx
== szone
->large_entry_cache_oldest
) { // Fully occupied
5343 // Drop this entry from the cache and deallocate the VM
5344 addr
= szone
->large_entry_cache
[idx
].address
;
5345 adjsize
= szone
->large_entry_cache
[idx
].size
;
5346 if (!szone
->large_entry_cache
[idx
].did_madvise_reusable
)
5347 szone
->large_entry_cache_hoard_bytes
-= adjsize
;
5349 // Using an unoccupied cache slot
5355 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
))
5356 memset((void *)(entry
->address
), 0x55, entry
->size
);
5358 entry
->did_madvise_reusable
= should_madvise
; // Was madvise()'d above?
5359 if (!should_madvise
) // Entered on death-row without madvise() => up the hoard total
5360 szone
->large_entry_cache_hoard_bytes
+= entry
->size
;
5362 szone
->large_entry_cache
[idx
] = *entry
;
5363 szone
->large_entry_cache_newest
= idx
;
5365 szone
->num_large_objects_in_use
--;
5366 szone
->num_bytes_in_large_objects
-= entry
->size
;
5368 (void)large_entry_free_no_lock(szone
, entry
);
5371 SZONE_UNLOCK(szone
);
5375 // Fall through to drop large_entry_cache_oldest from the cache,
5376 // and then deallocate its pages.
5378 // Trim the queue on the "left" by bumping up large_entry_cache_oldest
5379 if (szone
->large_entry_cache_oldest
== LARGE_ENTRY_CACHE_SIZE
- 1)
5380 szone
->large_entry_cache_oldest
= 0;
5382 szone
->large_entry_cache_oldest
++;
5384 // we deallocate_pages, including guard pages, outside the lock
5385 SZONE_UNLOCK(szone
);
5386 deallocate_pages(szone
, (void *)addr
, (size_t)adjsize
, 0);
5389 /* fall through to discard an allocation that is not reusable */
5392 #endif /* LARGE_CACHE */
5394 szone
->num_large_objects_in_use
--;
5395 szone
->num_bytes_in_large_objects
-= entry
->size
;
5397 vm_range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5400 large_debug_print(szone
);
5402 szone_error(szone
, 1, "pointer being freed was not allocated", ptr
, NULL
);
5403 SZONE_UNLOCK(szone
);
5406 SZONE_UNLOCK(szone
); // we release the lock asap
5407 CHECK(szone
, __PRETTY_FUNCTION__
);
5409 // we deallocate_pages, including guard pages, outside the lock
5410 if (vm_range_to_deallocate
.address
) {
5412 // FIXME: large_entry_for_pointer_no_lock() needs the lock held ...
5413 if (large_entry_for_pointer_no_lock(szone
, (void *)vm_range_to_deallocate
.address
)) {
5414 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n",
5415 vm_range_to_deallocate
.address
, szone
->num_large_entries
);
5416 large_debug_print(szone
);
5420 deallocate_pages(szone
, (void *)vm_range_to_deallocate
.address
, (size_t)vm_range_to_deallocate
.size
, 0);
5425 large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
5427 vm_address_t addr
= (vm_address_t
)ptr
+ old_size
;
5428 large_entry_t
*large_entry
;
5432 large_entry
= large_entry_for_pointer_no_lock(szone
, (void *)addr
);
5433 SZONE_UNLOCK(szone
);
5435 if (large_entry
) { // check if "addr = ptr + old_size" is already spoken for
5436 return 0; // large pointer already exists in table - extension is not going to work
5439 new_size
= round_page(new_size
);
5441 * Ask for allocation at a specific address, and mark as realloc
5442 * to request coalescing with previous realloc'ed extensions.
5444 err
= vm_allocate(mach_task_self(), &addr
, new_size
- old_size
, VM_MAKE_TAG(VM_MEMORY_REALLOC
));
5445 if (err
!= KERN_SUCCESS
) {
5450 /* extend existing large entry */
5451 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5453 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
5454 SZONE_UNLOCK(szone
);
5455 return 0; // Bail, leaking "addr"
5458 large_entry
->address
= (vm_address_t
)ptr
;
5459 large_entry
->size
= new_size
;
5460 szone
->num_bytes_in_large_objects
+= new_size
- old_size
;
5461 SZONE_UNLOCK(szone
); // we release the lock asap
5466 /********************* Zone call backs ************************/
5468 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
5470 static NOINLINE
void
5471 szone_free(szone_t
*szone
, void *ptr
)
5473 region_t tiny_region
;
5474 region_t small_region
;
5477 if (LOG(szone
, ptr
))
5478 malloc_printf("in szone_free with %p\n", ptr
);
5483 * Try to free to a tiny region.
5485 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
5486 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
5489 if ((tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
5490 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
5491 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
5494 free_tiny(szone
, ptr
, tiny_region
, 0);
5499 * Try to free to a small region.
5501 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
5502 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
5505 if ((small_region
= small_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
5506 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
5507 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
5510 free_small(szone
, ptr
, small_region
, 0);
5514 /* check that it's a legal large allocation */
5515 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
5516 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
5519 free_large(szone
, ptr
);
5522 static NOINLINE
void
5523 szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
5526 if (LOG(szone
, ptr
))
5527 malloc_printf("in szone_free_definite_size with %p\n", ptr
);
5530 szone_error(szone
, 1, "pointer of size zero being freed", ptr
, NULL
);
5539 * Try to free to a tiny region.
5541 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
5542 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
5545 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5546 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
5547 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
5550 free_tiny(szone
, ptr
, TINY_REGION_FOR_PTR(ptr
), size
);
5555 * Try to free to a small region.
5557 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
5558 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
5561 if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5562 (size
<= szone
->large_threshold
)) {
5563 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
5564 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
5567 free_small(szone
, ptr
, SMALL_REGION_FOR_PTR(ptr
), size
);
5571 /* check that it's a legal large allocation */
5572 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
5573 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
5576 free_large(szone
, ptr
);
5579 static NOINLINE
void *
5580 szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
)
5585 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5587 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5590 ptr
= tiny_malloc_should_clear(szone
, msize
, cleared_requested
);
5591 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5592 (size
<= szone
->large_threshold
)) {
5594 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
5597 ptr
= small_malloc_should_clear(szone
, msize
, cleared_requested
);
5600 size_t num_pages
= round_page(size
) >> vm_page_shift
;
5601 if (num_pages
== 0) /* Overflowed */
5604 ptr
= large_malloc(szone
, num_pages
, 0, cleared_requested
);
5607 if (LOG(szone
, ptr
))
5608 malloc_printf("szone_malloc returned %p\n", ptr
);
5611 * If requested, scribble on allocated memory.
5613 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && ptr
&& !cleared_requested
&& size
)
5614 memset(ptr
, 0xaa, size
);
5619 static NOINLINE
void *
5620 szone_malloc(szone_t
*szone
, size_t size
) {
5621 return szone_malloc_should_clear(szone
, size
, 0);
5624 static NOINLINE
void *
5625 szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
5627 size_t total_bytes
= num_items
* size
;
5629 // Check for overflow of integer multiplication
5630 if (num_items
> 1) {
5631 #if __LP64__ /* size_t is uint64_t */
5632 if ((num_items
| size
) & 0xffffffff00000000ul
) {
5633 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
5634 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
5635 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
5638 #else /* size_t is uint32_t */
5639 if ((num_items
| size
) & 0xffff0000ul
) {
5640 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
5641 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
5642 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
5648 return szone_malloc_should_clear(szone
, total_bytes
, 1);
5651 static NOINLINE
void *
5652 szone_valloc(szone_t
*szone
, size_t size
)
5656 if (size
<= szone
->large_threshold
) {
5657 ptr
= szone_memalign(szone
, vm_page_size
, size
);
5661 num_pages
= round_page(size
) >> vm_page_shift
;
5662 ptr
= large_malloc(szone
, num_pages
, 0, 0);
5666 if (LOG(szone
, ptr
))
5667 malloc_printf("szone_valloc returned %p\n", ptr
);
5672 /* Isolate PIC-base load (for __is_threaded) here. */
5673 static NOINLINE
size_t
5674 szone_size_try_large(szone_t
*szone
, const void *ptr
)
5677 large_entry_t
*entry
;
5680 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5684 SZONE_UNLOCK(szone
);
5686 if (LOG(szone
, ptr
)) {
5687 malloc_printf("szone_size for %p returned %d\n", ptr
, (unsigned)size
);
5693 static NOINLINE
size_t
5694 szone_size(szone_t
*szone
, const void *ptr
)
5697 msize_t msize
, msize_and_free
;
5702 if (LOG(szone
, ptr
)) {
5703 malloc_printf("in szone_size for %p (szone=%p)\n", ptr
, szone
);
5708 * Look for it in a tiny region.
5710 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1))
5712 if (tiny_region_for_ptr_no_lock(szone
, ptr
)) {
5713 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
5715 msize
= get_tiny_meta_header(ptr
, &is_free
);
5720 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
5721 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
5723 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
5727 return TINY_BYTES_FOR_MSIZE(msize
);
5731 * Look for it in a small region.
5733 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1))
5735 if (small_region_for_ptr_no_lock(szone
, ptr
)) {
5736 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
)
5738 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
5739 if (msize_and_free
& SMALL_IS_FREE
)
5743 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5744 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5746 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
5750 return SMALL_BYTES_FOR_MSIZE(msize_and_free
);
5754 * If not page-aligned, it cannot have come from a large allocation.
5756 if ((uintptr_t)ptr
& (vm_page_size
- 1))
5760 * Look for it in a large entry.
5762 return szone_size_try_large(szone
, ptr
);
5765 static NOINLINE
void *
5766 szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
5772 if (LOG(szone
, ptr
)) {
5773 malloc_printf("in szone_realloc for %p, %d\n", ptr
, (unsigned)new_size
);
5777 ptr
= szone_malloc(szone
, new_size
);
5780 old_size
= szone_size(szone
, ptr
);
5782 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
5785 /* we never shrink an allocation */
5786 if (old_size
>= new_size
)
5790 * If the new size suits the tiny allocator and the pointer being resized
5791 * belongs to a tiny region, try to reallocate in-place.
5793 if ((new_size
+ TINY_QUANTUM
- 1) <= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
5794 if (tiny_region_for_ptr_no_lock(szone
, ptr
) != NULL
) {
5795 if (tiny_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5801 * If the new size suits the small allocator and the pointer being resized
5802 * belongs to a small region, and we're not protecting the small allocations
5803 * try to reallocate in-place.
5805 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5806 ((new_size
+ SMALL_QUANTUM
- 1) <= szone
->large_threshold
) &&
5807 (small_region_for_ptr_no_lock(szone
, ptr
) != NULL
)) {
5808 if (small_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5813 * If the allocation's a large allocation, try to reallocate in-place there.
5815 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5816 !(szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) &&
5817 (old_size
> szone
->large_threshold
)) {
5818 if (large_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5824 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
5826 new_ptr
= szone_malloc(szone
, new_size
);
5827 if (new_ptr
== NULL
)
5831 * If the allocation's large enough, try to copy using VM. If that fails, or
5832 * if it's too small, just copy by hand.
5834 if ((old_size
< szone
->vm_copy_threshold
) ||
5835 vm_copy(mach_task_self(), (vm_address_t
)ptr
, old_size
, (vm_address_t
)new_ptr
))
5836 memcpy(new_ptr
, ptr
, old_size
);
5837 szone_free(szone
, ptr
);
5840 if (LOG(szone
, ptr
)) {
5841 malloc_printf("szone_realloc returned %p for %d\n", new_ptr
, (unsigned)new_size
);
5847 static NOINLINE
void *
5848 szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
5850 if ((size
+ alignment
) < size
) // size_t arithmetic wrapped!
5853 // alignment is gauranteed a power of 2 at least as large as sizeof(void *), hence non-zero.
5854 // Since size + alignment didn't wrap, 0 <= size + alignment - 1 < size + alignment
5855 size_t span
= size
+ alignment
- 1;
5857 if (alignment
<= TINY_QUANTUM
) {
5858 return szone_malloc(szone
, size
); // Trivially satisfied by tiny, small, or large
5860 } else if (span
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5861 msize_t mspan
= TINY_MSIZE_FOR_BYTES(span
+ TINY_QUANTUM
- 1);
5862 void *p
= szone_malloc(szone
, span
); // avoids inlining tiny_malloc_should_clear(szone, mspan, 0);
5867 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
5868 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
5870 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5871 msize_t mpad
= TINY_MSIZE_FOR_BYTES(pad
+ TINY_QUANTUM
- 1);
5872 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
5875 void *q
= (void *)(((uintptr_t) p
) + pad
);
5877 // Mark q as a block header and in-use, thus creating two blocks.
5878 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5879 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
5880 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
5881 set_tiny_meta_header_in_use(q
, msize
);
5883 // set_tiny_meta_header_in_use() "reaffirms" the block_header on the *following* block, so
5884 // now set its in_use bit as well. But only if its within the original allocation made above.
5886 BITARRAY_SET(TINY_INUSE_FOR_HEADER(TINY_BLOCK_HEADER_FOR_PTR(q
)), TINY_INDEX_FOR_PTR(q
) + msize
);
5887 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5889 // Give up mpad blocks beginning at p to the tiny free list
5890 // region_t r = TINY_REGION_FOR_PTR(p);
5891 szone_free(szone
, p
); // avoids inlining free_tiny(szone, p, &r);
5893 p
= q
; // advance p to the desired alignment
5897 void *q
= (void *)(((uintptr_t) p
) + TINY_BYTES_FOR_MSIZE(msize
));
5898 // Mark q as block header and in-use, thus creating two blocks.
5899 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5900 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
5901 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
5902 set_tiny_meta_header_in_use(q
, mwaste
);
5903 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5905 // Give up mwaste blocks beginning at q to the tiny free list
5906 // region_t r = TINY_REGION_FOR_PTR(q);
5907 szone_free(szone
, q
); // avoids inlining free_tiny(szone, q, &r);
5910 return p
; // p has the desired size and alignment, and can later be free()'d
5912 } else if ((NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
< size
&& alignment
<= SMALL_QUANTUM
) {
5913 return szone_malloc(szone
, size
); // Trivially satisfied by small or large
5915 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) && (span
<= szone
->large_threshold
)) {
5917 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5918 size
= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
+ TINY_QUANTUM
; // ensure block allocated by small does not have a tiny-possible size
5919 span
= size
+ alignment
- 1;
5922 msize_t mspan
= SMALL_MSIZE_FOR_BYTES(span
+ SMALL_QUANTUM
- 1);
5923 void *p
= szone_malloc(szone
, span
); // avoid inlining small_malloc_should_clear(szone, mspan, 0);
5928 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
5929 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
5931 msize_t msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
5932 msize_t mpad
= SMALL_MSIZE_FOR_BYTES(pad
+ SMALL_QUANTUM
- 1);
5933 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
5936 void *q
= (void *)(((uintptr_t) p
) + pad
);
5938 // Mark q as block header and in-use, thus creating two blocks.
5939 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
5940 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
5941 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
5942 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), mpad
);
5943 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), msize
+ mwaste
);
5944 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5946 // Give up mpad blocks beginning at p to the small free list
5947 // region_t r = SMALL_REGION_FOR_PTR(p);
5948 szone_free(szone
, p
); // avoid inlining free_small(szone, p, &r);
5950 p
= q
; // advance p to the desired alignment
5953 void *q
= (void *)(((uintptr_t) p
) + SMALL_BYTES_FOR_MSIZE(msize
));
5954 // Mark q as block header and in-use, thus creating two blocks.
5955 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
5956 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
5957 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
5958 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), msize
);
5959 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mwaste
);
5960 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5962 // Give up mwaste blocks beginning at q to the small free list
5963 // region_t r = SMALL_REGION_FOR_PTR(q);
5964 szone_free(szone
, q
); // avoid inlining free_small(szone, q, &r);
5967 return p
; // p has the desired size and alignment, and can later be free()'d
5969 } else if (szone
->large_threshold
< size
&& alignment
<= vm_page_size
) {
5970 return szone_malloc(szone
, size
); // Trivially satisfied by large
5973 // ensure block allocated by large does not have a small-possible size
5974 size_t num_pages
= round_page(MAX(szone
->large_threshold
+ 1, size
)) >> vm_page_shift
;
5977 if (num_pages
== 0) /* Overflowed */
5980 p
= large_malloc(szone
, num_pages
, MAX(vm_page_shift
, __builtin_ctz(alignment
)), 0);
5987 // given a size, returns the number of pointers allocated capable of holding
5988 // that size, up to the limit specified by the 'count' argument. These pointers
5989 // are stored in the 'results' array, which must be allocated by the caller.
5990 // may return zero, since this function is only a best attempt at allocating
5991 // the pointers. clients should be prepared to call malloc for any additional
5992 // blocks they need.
5993 static NOINLINE
unsigned
5994 szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
5996 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5998 mag_index_t mag_index
= mag_get_thread_index(szone
);
5999 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
6001 // only bother implementing this for tiny
6002 if (size
> (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
)
6004 // make sure to return objects at least one quantum in size
6008 CHECK(szone
, __PRETTY_FUNCTION__
);
6010 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
6011 // the caller has done so.
6012 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
6014 // with the zone locked, allocate objects from the free list until all
6015 // sufficiently large objects have been exhausted, or we have met our quota
6016 // of objects to allocate.
6017 while (found
< count
) {
6018 void *ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
6025 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6029 /* Try caching the tiny_region and checking if the next ptr hits there. */
6030 static NOINLINE
void
6031 szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
6035 region_t tiny_region
= NULL
;
6038 magazine_t
*tiny_mag_ptr
= NULL
;
6039 mag_index_t mag_index
= -1;
6041 // frees all the pointers in to_be_freed
6042 // note that to_be_freed may be overwritten during the process
6046 CHECK(szone
, __PRETTY_FUNCTION__
);
6047 while (cc
< count
) {
6048 ptr
= to_be_freed
[cc
];
6050 if (NULL
== tiny_region
|| tiny_region
!= TINY_REGION_FOR_PTR(ptr
)) { // region same as last iteration?
6051 if (tiny_mag_ptr
) { // non-NULL iff magazine lock taken
6052 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6053 tiny_mag_ptr
= NULL
;
6056 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
6059 tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6060 REGION_TRAILER_FOR_TINY_REGION(tiny_region
),
6061 MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
));
6062 mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
6066 // this is a tiny pointer
6067 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
6068 break; // pointer to metadata; let the standard free deal with it
6069 msize
= get_tiny_meta_header(ptr
, &is_free
);
6071 break; // a double free; let the standard free deal with it
6073 tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
);
6074 to_be_freed
[cc
] = NULL
;
6076 // No region in this zone claims ptr; let the standard free deal with it
6084 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6085 tiny_mag_ptr
= NULL
;
6088 CHECK(szone
, __PRETTY_FUNCTION__
);
6090 ptr
= to_be_freed
[count
];
6092 szone_free(szone
, ptr
);
6096 // FIXME: Suppose one of the locks is held?
6098 szone_destroy(szone_t
*szone
)
6101 large_entry_t
*large
;
6102 vm_range_t range_to_deallocate
;
6104 /* destroy large entries */
6105 index
= szone
->num_large_entries
;
6107 large
= szone
->large_entries
+ index
;
6108 if (large
->address
) {
6109 // we deallocate_pages, including guard pages
6110 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6113 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6114 if (range_to_deallocate
.size
)
6115 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6117 /* destroy tiny regions */
6118 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
)
6119 if ((HASHRING_OPEN_ENTRY
!= szone
->tiny_region_generation
->hashed_regions
[index
]) &&
6120 (HASHRING_REGION_DEALLOCATED
!= szone
->tiny_region_generation
->hashed_regions
[index
]))
6121 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
[index
], TINY_REGION_SIZE
, 0);
6123 /* destroy small regions */
6124 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
)
6125 if ((HASHRING_OPEN_ENTRY
!= szone
->small_region_generation
->hashed_regions
[index
]) &&
6126 (HASHRING_REGION_DEALLOCATED
!= szone
->small_region_generation
->hashed_regions
[index
]))
6127 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
[index
], SMALL_REGION_SIZE
, 0);
6129 /* destroy region hash rings, if any */
6130 if (szone
->tiny_region_generation
->hashed_regions
!= szone
->initial_tiny_regions
) {
6131 size_t size
= round_page(szone
->tiny_region_generation
->num_regions_allocated
* sizeof(region_t
));
6132 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
, size
, 0);
6134 if (szone
->small_region_generation
->hashed_regions
!= szone
->initial_small_regions
) {
6135 size_t size
= round_page(szone
->small_region_generation
->num_regions_allocated
* sizeof(region_t
));
6136 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
, size
, 0);
6139 /* Now destroy the separate szone region */
6140 if (szone
->cpu_id_key
!= (pthread_key_t
) -1)
6141 (void)pthread_key_delete(szone
->cpu_id_key
);
6142 deallocate_pages(szone
, (void *)&(szone
->tiny_magazines
[-1]), TINY_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6143 deallocate_pages(szone
, (void *)&(szone
->small_magazines
[-1]), SMALL_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6144 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6147 static NOINLINE
size_t
6148 szone_good_size(szone_t
*szone
, size_t size
)
6151 int guard_small
= (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
;
6153 // Find a good size for this tiny allocation.
6154 if (size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6155 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6158 return TINY_BYTES_FOR_MSIZE(msize
);
6161 // Find a good size for this small allocation.
6162 if (!guard_small
&& (size
<= szone
->large_threshold
)) {
6163 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6166 return SMALL_BYTES_FOR_MSIZE(msize
);
6169 // Check for integer overflow on the size, since unlike the two cases above,
6170 // there is no upper bound on allocation size at this point.
6171 if (size
> round_page(size
))
6172 return (size_t)(-1LL);
6175 // It is not acceptable to see a size of zero here, since that means we
6176 // failed to catch a request for zero bytes in the tiny check, or the size
6177 // overflowed to zero during some arithmetic.
6179 malloc_printf("szone_good_size() invariant broken %y\n", size
);
6181 return round_page(size
);
6184 unsigned szone_check_counter
= 0;
6185 unsigned szone_check_start
= 0;
6186 unsigned szone_check_modulo
= 1;
6188 static NOINLINE boolean_t
6189 szone_check_all(szone_t
*szone
, const char *function
)
6193 /* check tiny regions - chould check region count */
6194 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6195 region_t tiny
= szone
->tiny_region_generation
->hashed_regions
[index
];
6197 if (HASHRING_REGION_DEALLOCATED
== tiny
)
6201 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6202 REGION_TRAILER_FOR_TINY_REGION(tiny
), MAGAZINE_INDEX_FOR_TINY_REGION(tiny
));
6204 if (!tiny_check_region(szone
, tiny
)) {
6205 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6206 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6207 szone_error(szone
, 1, "check: tiny region incorrect", NULL
,
6208 "*** tiny region %ld incorrect szone_check_all(%s) counter=%d\n",
6209 index
, function
, szone_check_counter
);
6212 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6215 /* check tiny free lists */
6216 for (index
= 0; index
< NUM_TINY_SLOTS
; ++index
) {
6217 if (!tiny_free_list_check(szone
, index
)) {
6218 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6219 szone_error(szone
, 1, "check: tiny free list incorrect", NULL
,
6220 "*** tiny free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6221 index
, function
, szone_check_counter
);
6226 /* check small regions - could check region count */
6227 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6228 region_t small
= szone
->small_region_generation
->hashed_regions
[index
];
6230 if (HASHRING_REGION_DEALLOCATED
== small
)
6234 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6235 REGION_TRAILER_FOR_SMALL_REGION(small
), MAGAZINE_INDEX_FOR_SMALL_REGION(small
));
6237 if (!small_check_region(szone
, small
)) {
6238 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6239 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6240 szone_error(szone
, 1, "check: small region incorrect", NULL
,
6241 "*** small region %ld incorrect szone_check_all(%s) counter=%d\n",
6242 index
, function
, szone_check_counter
);
6245 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6248 /* check small free lists */
6249 for (index
= 0; index
< szone
->num_small_slots
; ++index
) {
6250 if (!small_free_list_check(szone
, index
)) {
6251 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6252 szone_error(szone
, 1, "check: small free list incorrect", NULL
,
6253 "*** small free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6254 index
, function
, szone_check_counter
);
6263 szone_check(szone_t
*szone
)
6265 if ((++szone_check_counter
% 10000) == 0)
6266 _malloc_printf(ASL_LEVEL_NOTICE
, "at szone_check counter=%d\n", szone_check_counter
);
6268 if (szone_check_counter
< szone_check_start
)
6271 if (szone_check_counter
% szone_check_modulo
)
6274 return szone_check_all(szone
, "");
6277 static kern_return_t
6278 szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
6279 memory_reader_t reader
, vm_range_recorder_t recorder
)
6284 if (!reader
) reader
= _szone_default_reader
;
6286 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
6287 if (err
) return err
;
6289 err
= tiny_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
6290 if (err
) return err
;
6292 err
= small_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
6293 if (err
) return err
;
6295 err
= large_in_use_enumerator(task
, context
, type_mask
,
6296 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
6300 // Following method is deprecated: use scalable_zone_statistics instead
6302 scalable_zone_info(malloc_zone_t
*zone
, unsigned *info_to_fill
, unsigned count
)
6304 szone_t
*szone
= (void *)zone
;
6307 // We do not lock to facilitate debug
6312 mag_index_t mag_index
;
6314 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6315 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6316 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6317 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6323 for (t
= 0, u
= 0, mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6324 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6325 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6326 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6332 info
[8] = szone
->num_large_objects_in_use
;
6333 info
[9] = szone
->num_bytes_in_large_objects
;
6335 info
[10] = 0; // DEPRECATED szone->num_huge_entries;
6336 info
[11] = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6338 info
[12] = szone
->debug_flags
;
6340 info
[0] = info
[4] + info
[6] + info
[8] + info
[10];
6341 info
[1] = info
[5] + info
[7] + info
[9] + info
[11];
6343 info
[3] = (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
6344 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ info
[9] + info
[11];
6346 info
[2] = info
[3] - s
;
6347 memcpy(info_to_fill
, info
, sizeof(unsigned)*count
);
6350 // FIXME: consistent picture requires locking!
6351 static NOINLINE
void
6352 szone_print(szone_t
*szone
, boolean_t verbose
)
6358 scalable_zone_info((void *)szone
, info
, 13);
6359 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6360 "Scalable zone %p: inUse=%d(%y) touched=%y allocated=%y flags=%d\n",
6361 szone
, info
[0], info
[1], info
[2], info
[3], info
[12]);
6362 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6363 "\ttiny=%d(%y) small=%d(%y) large=%d(%y) huge=%d(%y)\n",
6364 info
[4], info
[5], info
[6], info
[7], info
[8], info
[9], info
[10], info
[11]);
6366 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6367 "%d tiny regions:\n", szone
->num_tiny_regions
);
6368 if (szone
->num_tiny_regions_dealloc
)
6369 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6370 "[%d tiny regions have been vm_deallocate'd]\n", szone
->num_tiny_regions_dealloc
);
6371 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6372 region
= szone
->tiny_region_generation
->hashed_regions
[index
];
6373 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
6374 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
6375 print_tiny_region(verbose
, region
, (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
6376 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
6380 print_tiny_free_list(szone
);
6382 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6383 "%d small regions:\n", szone
->num_small_regions
);
6384 if (szone
->num_small_regions_dealloc
)
6385 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6386 "[%d small regions have been vm_deallocate'd]\n", szone
->num_small_regions_dealloc
);
6387 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6388 region
= szone
->small_region_generation
->hashed_regions
[index
];
6389 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
6390 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(region
);
6391 print_small_region(szone
, verbose
, region
,
6392 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
6393 szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
6397 print_small_free_list(szone
);
6401 szone_log(malloc_zone_t
*zone
, void *log_address
)
6403 szone_t
*szone
= (szone_t
*)zone
;
6405 szone
->log_address
= log_address
;
6409 szone_force_lock(szone_t
*szone
)
6413 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
6414 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6416 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
])));
6418 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
6419 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[i
])));
6421 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
])));
6427 szone_force_unlock(szone_t
*szone
)
6431 SZONE_UNLOCK(szone
);
6433 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
6434 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
6437 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
6438 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6443 szone_locked(szone_t
*szone
)
6448 tookLock
= SZONE_TRY_LOCK(szone
);
6451 SZONE_UNLOCK(szone
);
6453 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
6454 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->small_magazines
[i
])));
6457 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
6460 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
6461 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6464 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6470 scalable_zone_statistics(malloc_zone_t
*zone
, malloc_statistics_t
*stats
, unsigned subzone
)
6472 szone_t
*szone
= (szone_t
*)zone
;
6480 mag_index_t mag_index
;
6482 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6483 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6484 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6485 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6488 stats
->blocks_in_use
= t
;
6489 stats
->size_in_use
= u
;
6490 stats
->size_allocated
= (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
;
6491 stats
->max_size_in_use
= stats
->size_allocated
- s
;
6499 mag_index_t mag_index
;
6501 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6502 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6503 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6504 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6507 stats
->blocks_in_use
= t
;
6508 stats
->size_in_use
= u
;
6509 stats
->size_allocated
= (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
;
6510 stats
->max_size_in_use
= stats
->size_allocated
- s
;
6514 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
6515 stats
->size_in_use
= szone
->num_bytes_in_large_objects
;
6516 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
6519 stats
->blocks_in_use
= 0; // DEPRECATED szone->num_huge_entries;
6520 stats
->size_in_use
= 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6521 stats
->max_size_in_use
= stats
->size_allocated
= 0;
6528 szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
6535 mag_index_t mag_index
;
6537 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6538 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6539 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6540 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6543 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6544 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6545 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6546 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6549 large
= szone
->num_bytes_in_large_objects
+ 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6551 stats
->blocks_in_use
= t
+ szone
->num_large_objects_in_use
+ 0; // DEPRECATED szone->num_huge_entries;
6552 stats
->size_in_use
= u
+ large
;
6553 stats
->max_size_in_use
= stats
->size_allocated
=
6554 (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
6555 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ large
;
6556 // Now we account for the untouched areas
6557 stats
->max_size_in_use
-= s
;
6561 legacy_zeroing_large_malloc(szone_t
*szone
, size_t size
) {
6562 if (size
> LARGE_THRESHOLD
) // Leopard and earlier returned a ZFOD range, so ...
6563 return szone_calloc(szone
, 1, size
); // Clear to zero always, ham-handedly touching in each page
6565 return szone_malloc(szone
, size
);
6569 legacy_zeroing_large_valloc(szone_t
*szone
, size_t size
) {
6570 void *p
= szone_valloc(szone
, size
);
6572 // Leopard and earlier returned a ZFOD range, so ...
6573 memset(p
, 0, size
); // Clear to zero always, ham-handedly touching in each page
6577 void zeroify_scalable_zone(malloc_zone_t
*zone
)
6579 szone_t
*szone
= (szone_t
*)zone
;
6582 szone
->basic_zone
.malloc
= (void *)legacy_zeroing_large_malloc
;
6583 szone
->basic_zone
.valloc
= (void *)legacy_zeroing_large_valloc
;
6587 static const struct malloc_introspection_t szone_introspect
= {
6588 (void *)szone_ptr_in_use_enumerator
,
6589 (void *)szone_good_size
,
6590 (void *)szone_check
,
6591 (void *)szone_print
,
6593 (void *)szone_force_lock
,
6594 (void *)szone_force_unlock
,
6595 (void *)szone_statistics
,
6596 (void *)szone_locked
,
6597 }; // marked as const to spare the DATA section
6600 create_scalable_zone(size_t initial_size
, unsigned debug_flags
)
6603 uint64_t hw_memsize
= 0;
6604 size_t uint64_t_size
= sizeof(hw_memsize
);
6608 * Sanity-check our build-time assumptions about the size of a page.
6609 * Since we have sized various things assuming the default page size,
6610 * attempting to determine it dynamically is not useful.
6612 if ((vm_page_size
!= _vm_page_size
) || (vm_page_shift
!= _vm_page_shift
)) {
6613 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
6617 #if defined(__i386__) || defined(__x86_64__)
6618 if (_COMM_PAGE_VERSION_REQD
> (*((short *) _COMM_PAGE_VERSION
))) { // _COMM_PAGE_CPU_NUMBER must be present at runtime
6619 malloc_printf("*** ERROR - comm page version mismatch.\n");
6624 /* get memory for the zone, which is now separate from any region.
6625 add guard pages to prevent walking from any other vm allocations
6626 to here and overwriting the function pointers in basic_zone. */
6627 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6631 /* set up the szone structure */
6633 #warning CHECK_REGIONS enabled
6634 debug_flags
|= CHECK_REGIONS
;
6637 #warning LOG enabled
6638 szone
->log_address
= ~0;
6640 szone
->trg
[0].nextgen
= &(szone
->trg
[1]);
6641 szone
->trg
[1].nextgen
= &(szone
->trg
[0]);
6642 szone
->tiny_region_generation
= &(szone
->trg
[0]);
6644 szone
->tiny_region_generation
->hashed_regions
= szone
->initial_tiny_regions
;
6645 szone
->tiny_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
6646 szone
->tiny_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
6648 szone
->srg
[0].nextgen
= &(szone
->srg
[1]);
6649 szone
->srg
[1].nextgen
= &(szone
->srg
[0]);
6650 szone
->small_region_generation
= &(szone
->srg
[0]);
6652 szone
->small_region_generation
->hashed_regions
= szone
->initial_small_regions
;
6653 szone
->small_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
6654 szone
->small_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
6658 * Initialize variables that size the free list for SMALL allocations based
6659 * upon the amount of memory in the system. Switch to a larger number of
6660 * free list entries at 1GB.
6662 if (0 == sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0) &&
6663 hw_memsize
>= (1ULL << 30)) {
6664 szone
->is_largemem
= 1;
6665 szone
->num_small_slots
= NUM_SMALL_SLOTS_LARGEMEM
;
6666 szone
->large_threshold
= LARGE_THRESHOLD_LARGEMEM
;
6667 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD_LARGEMEM
;
6669 szone
->is_largemem
= 0;
6670 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
6671 szone
->large_threshold
= LARGE_THRESHOLD
;
6672 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
6675 szone
->large_entry_cache_hoard_lmit
= hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
6677 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
6678 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
6679 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
6680 szone
->large_legacy_reset_mprotect
= TRUE
;
6682 szone
->large_legacy_reset_mprotect
= FALSE
;
6685 // Initialize the security token.
6687 szone
->cookie
= ((uintptr_t)arc4random() << 32) | (uintptr_t)arc4random();
6689 szone
->cookie
= arc4random();
6692 szone
->basic_zone
.version
= 6;
6693 szone
->basic_zone
.size
= (void *)szone_size
;
6694 szone
->basic_zone
.malloc
= (void *)szone_malloc
;
6695 szone
->basic_zone
.calloc
= (void *)szone_calloc
;
6696 szone
->basic_zone
.valloc
= (void *)szone_valloc
;
6697 szone
->basic_zone
.free
= (void *)szone_free
;
6698 szone
->basic_zone
.realloc
= (void *)szone_realloc
;
6699 szone
->basic_zone
.destroy
= (void *)szone_destroy
;
6700 szone
->basic_zone
.batch_malloc
= (void *)szone_batch_malloc
;
6701 szone
->basic_zone
.batch_free
= (void *)szone_batch_free
;
6702 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
6703 szone
->basic_zone
.memalign
= (void *)szone_memalign
;
6704 szone
->basic_zone
.free_definite_size
= (void *)szone_free_definite_size
;
6705 szone
->debug_flags
= debug_flags
;
6706 LOCK_INIT(szone
->large_szone_lock
);
6708 #if defined(__ppc__) || defined(__ppc64__)
6710 * In the interest of compatibility for PPC applications executing via Rosetta,
6711 * arrange to zero-fill allocations as occurred by side effect in Leopard and earlier.
6713 zeroify_scalable_zone((malloc_zone_t
*)szone
);
6716 if ((err
= pthread_key_create(&(szone
->cpu_id_key
), NULL
))) {
6717 malloc_printf("*** ERROR -pthread_key_create failure err=%d.\n", err
);
6718 szone
->cpu_id_key
= (pthread_key_t
) -1;
6721 // Query the number of configured processors.
6722 // Uniprocessor case gets just one tiny and one small magazine (whose index is zero). This gives
6723 // the same behavior as the original scalable malloc. MP gets per-CPU magazines
6724 // that scale (way) better.
6725 int nproc
= sysconf(_SC_NPROCESSORS_CONF
);
6726 szone
->num_tiny_magazines
= (nproc
> 1) ? MIN(nproc
, TINY_MAX_MAGAZINES
) : 1;
6728 // FIXME vm_allocate() based on number of configured CPUs
6729 magazine_t
*tiny_magazines
= allocate_pages(NULL
, TINY_MAGAZINE_PAGED_SIZE
, 0,
6730 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6731 if (NULL
== tiny_magazines
)
6734 szone
->tiny_magazines
= &(tiny_magazines
[1]); // szone->tiny_magazines[-1] is the Depot
6736 // The magazines are indexed in [0 .. (num_tiny_magazines - 1)]
6737 // Find the smallest power of 2 that exceeds (num_tiny_magazines - 1)
6738 szone
->num_tiny_magazines_mask_shift
= 0;
6740 while( i
<= (szone
->num_tiny_magazines
- 1) ) {
6741 szone
->num_tiny_magazines_mask_shift
++;
6745 // Now if i <= TINY_MAX_MAGAZINES we'll never access tiny_magazines[] out of bounds.
6746 if (i
> TINY_MAX_MAGAZINES
) {
6747 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
6751 // Reduce i by 1 to obtain a mask covering [0 .. (num_tiny_magazines - 1)]
6752 szone
->num_tiny_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
6753 #if TARGET_OS_EMBEDDED
6754 szone
->last_tiny_advise
= 0;
6757 // Init the tiny_magazine locks
6758 LOCK_INIT(szone
->tiny_regions_lock
);
6759 LOCK_INIT(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
6760 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
6761 LOCK_INIT(szone
->tiny_magazines
[i
].magazine_lock
);
6764 szone
->num_small_magazines
= (nproc
> 1) ? MIN(nproc
, SMALL_MAX_MAGAZINES
) : 1;
6766 // FIXME vm_allocate() based on number of configured CPUs
6767 magazine_t
*small_magazines
= allocate_pages(NULL
, SMALL_MAGAZINE_PAGED_SIZE
, 0,
6768 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6769 if (NULL
== small_magazines
)
6772 szone
->small_magazines
= &(small_magazines
[1]); // szone->small_magazines[-1] is the Depot
6774 // The magazines are indexed in [0 .. (num_small_magazines - 1)]
6775 // Find the smallest power of 2 that exceeds (num_small_magazines - 1)
6776 szone
->num_small_magazines_mask_shift
= 0;
6777 while( i
<= (szone
->num_small_magazines
- 1) ) {
6778 szone
->num_small_magazines_mask_shift
++;
6782 // Now if i <= SMALL_MAX_MAGAZINES we'll never access small_magazines[] out of bounds.
6783 if (i
> SMALL_MAX_MAGAZINES
) {
6784 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
6788 // Reduce i by 1 to obtain a mask covering [0 .. (num_small_magazines - 1)]
6789 szone
->num_small_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
6790 #if TARGET_OS_EMBEDDED
6791 szone
->last_small_advise
= 0;
6794 // Init the small_magazine locks
6795 LOCK_INIT(szone
->small_regions_lock
);
6796 LOCK_INIT(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
6797 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
6798 LOCK_INIT(szone
->small_magazines
[i
].magazine_lock
);
6801 CHECK(szone
, __PRETTY_FUNCTION__
);
6802 return (malloc_zone_t
*)szone
;
6806 // purgeable zones have their own "large" allocation pool, but share "tiny" and "large"
6807 // heaps with a helper_zone identified in the call to create_purgeable_zone()
6810 purgeable_size(szone_t
*szone
, const void *ptr
)
6812 size_t t
= szone_size_try_large(szone
, ptr
);
6817 return szone_size(szone
->helper_zone
, ptr
);
6821 purgeable_malloc(szone_t
*szone
, size_t size
) {
6822 if (size
<= szone
->large_threshold
)
6823 return szone_malloc(szone
->helper_zone
, size
);
6825 return szone_malloc(szone
, size
);
6829 purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
6831 size_t total_bytes
= num_items
* size
;
6833 // Check for overflow of integer multiplication
6834 if (num_items
> 1) {
6835 #if __LP64__ /* size_t is uint64_t */
6836 if ((num_items
| size
) & 0xffffffff00000000ul
) {
6837 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
6838 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
6839 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
6842 #else /* size_t is uint32_t */
6843 if ((num_items
| size
) & 0xffff0000ul
) {
6844 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
6845 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
6846 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
6852 if (total_bytes
<= szone
->large_threshold
)
6853 return szone_calloc(szone
->helper_zone
, 1, total_bytes
);
6855 return szone_calloc(szone
, 1, total_bytes
);
6859 purgeable_valloc(szone_t
*szone
, size_t size
)
6861 if (size
<= szone
->large_threshold
)
6862 return szone_valloc(szone
->helper_zone
, size
);
6864 return szone_valloc(szone
, size
);
6868 purgeable_free(szone_t
*szone
, void *ptr
)
6870 large_entry_t
*entry
;
6873 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6874 SZONE_UNLOCK(szone
);
6876 return free_large(szone
, ptr
);
6878 return szone_free(szone
->helper_zone
, ptr
);
6883 purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
6885 if (size
<= szone
->large_threshold
)
6886 return szone_free_definite_size(szone
->helper_zone
, ptr
, size
);
6888 return szone_free_definite_size(szone
, ptr
, size
);
6892 purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
6894 if (new_size
<= szone
->large_threshold
)
6895 return szone_realloc(szone
->helper_zone
, ptr
, new_size
);
6897 return szone_realloc(szone
, ptr
, new_size
);
6901 purgeable_destroy(szone_t
*szone
)
6903 /* destroy large entries */
6904 size_t index
= szone
->num_large_entries
;
6905 large_entry_t
*large
;
6906 vm_range_t range_to_deallocate
;
6909 large
= szone
->large_entries
+ index
;
6910 if (large
->address
) {
6911 // we deallocate_pages, including guard pages
6912 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6915 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6916 if (range_to_deallocate
.size
)
6917 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6919 /* Now destroy the separate szone region */
6920 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6924 purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
6926 return szone_batch_malloc(szone
->helper_zone
, size
, results
, count
);
6930 purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
6932 return szone_batch_free(szone
->helper_zone
, to_be_freed
, count
);
6936 purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
6938 if (size
<= szone
->large_threshold
)
6939 return szone_memalign(szone
->helper_zone
, alignment
, size
);
6941 return szone_memalign(szone
, alignment
, size
);
6944 static kern_return_t
6945 purgeable_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
6946 memory_reader_t reader
, vm_range_recorder_t recorder
)
6951 if (!reader
) reader
= _szone_default_reader
;
6953 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
6954 if (err
) return err
;
6956 err
= large_in_use_enumerator(task
, context
, type_mask
,
6957 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
6962 purgeable_good_size(szone_t
*szone
, size_t size
)
6964 if (size
<= szone
->large_threshold
)
6965 return szone_good_size(szone
->helper_zone
, size
);
6967 return szone_good_size(szone
, size
);
6971 purgeable_check(szone_t
*szone
)
6977 purgeable_print(szone_t
*szone
, boolean_t verbose
)
6979 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6980 "Scalable zone %p: inUse=%d(%y) flags=%d\n",
6981 szone
, szone
->num_large_objects_in_use
, szone
->num_bytes_in_large_objects
, szone
->debug_flags
);
6985 purgeable_log(malloc_zone_t
*zone
, void *log_address
)
6987 szone_t
*szone
= (szone_t
*)zone
;
6989 szone
->log_address
= log_address
;
6993 purgeable_force_lock(szone_t
*szone
)
6999 purgeable_force_unlock(szone_t
*szone
)
7001 SZONE_UNLOCK(szone
);
7005 purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
7007 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
7008 stats
->size_in_use
= stats
->max_size_in_use
= stats
->size_allocated
= szone
->num_bytes_in_large_objects
;
7012 purgeable_locked(szone_t
*szone
)
7016 tookLock
= SZONE_TRY_LOCK(szone
);
7019 SZONE_UNLOCK(szone
);
7023 static const struct malloc_introspection_t purgeable_introspect
= {
7024 (void *)purgeable_ptr_in_use_enumerator
,
7025 (void *)purgeable_good_size
,
7026 (void *)purgeable_check
,
7027 (void *)purgeable_print
,
7029 (void *)purgeable_force_lock
,
7030 (void *)purgeable_force_unlock
,
7031 (void *)purgeable_statistics
,
7032 (void *)purgeable_locked
,
7033 }; // marked as const to spare the DATA section
7036 create_purgeable_zone(size_t initial_size
, malloc_zone_t
*malloc_default_zone
, unsigned debug_flags
)
7040 /* get memory for the zone, which is now separate from any region.
7041 add guard pages to prevent walking from any other vm allocations
7042 to here and overwriting the function pointers in basic_zone. */
7043 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
7047 /* set up the szone structure */
7049 #warning LOG enabled
7050 szone
->log_address
= ~0;
7053 /* Purgeable zone does not participate in the adaptive "largemem" sizing. */
7054 szone
->is_largemem
= 0;
7055 szone
->large_threshold
= LARGE_THRESHOLD
;
7056 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7059 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7060 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
7061 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
7062 szone
->large_legacy_reset_mprotect
= TRUE
;
7064 szone
->large_legacy_reset_mprotect
= FALSE
;
7067 szone
->basic_zone
.version
= 6;
7068 szone
->basic_zone
.size
= (void *)purgeable_size
;
7069 szone
->basic_zone
.malloc
= (void *)purgeable_malloc
;
7070 szone
->basic_zone
.calloc
= (void *)purgeable_calloc
;
7071 szone
->basic_zone
.valloc
= (void *)purgeable_valloc
;
7072 szone
->basic_zone
.free
= (void *)purgeable_free
;
7073 szone
->basic_zone
.realloc
= (void *)purgeable_realloc
;
7074 szone
->basic_zone
.destroy
= (void *)purgeable_destroy
;
7075 szone
->basic_zone
.batch_malloc
= (void *)purgeable_batch_malloc
;
7076 szone
->basic_zone
.batch_free
= (void *)purgeable_batch_free
;
7077 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&purgeable_introspect
;
7078 szone
->basic_zone
.memalign
= (void *)purgeable_memalign
;
7079 szone
->basic_zone
.free_definite_size
= (void *)purgeable_free_definite_size
;
7081 szone
->debug_flags
= debug_flags
| SCALABLE_MALLOC_PURGEABLE
;
7083 /* Purgeable zone does not support SCALABLE_MALLOC_ADD_GUARD_PAGES. */
7084 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
7085 _malloc_printf(ASL_LEVEL_INFO
, "purgeable zone does not support guard pages\n");
7086 szone
->debug_flags
&= ~SCALABLE_MALLOC_ADD_GUARD_PAGES
;
7089 LOCK_INIT(szone
->large_szone_lock
);
7091 szone
->helper_zone
= (struct szone_s
*)malloc_default_zone
;
7093 CHECK(szone
, __PRETTY_FUNCTION__
);
7094 return (malloc_zone_t
*)szone
;
7098 * For use by CheckFix: create a new zone whose behavior is, apart from
7099 * the use of death-row and per-CPU magazines, that of Leopard.
7101 static NOINLINE
void *
7102 legacy_valloc(szone_t
*szone
, size_t size
)
7107 num_pages
= round_page(size
) >> vm_page_shift
;
7108 ptr
= large_malloc(szone
, num_pages
, 0, TRUE
);
7110 if (LOG(szone
, ptr
))
7111 malloc_printf("legacy_valloc returned %p\n", ptr
);
7117 create_legacy_scalable_zone(size_t initial_size
, unsigned debug_flags
)
7119 malloc_zone_t
*mzone
= create_scalable_zone(initial_size
, debug_flags
);
7120 szone_t
*szone
= (szone_t
*)mzone
;
7125 szone
->is_largemem
= 0;
7126 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
7127 szone
->large_threshold
= LARGE_THRESHOLD
;
7128 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7130 szone
->basic_zone
.valloc
= (void *)legacy_valloc
;
7131 szone
->basic_zone
.free_definite_size
= NULL
;
7136 /********* Support code for emacs unexec ************/
7138 /* History of freezedry version numbers:
7140 * 1) Old malloc (before the scalable malloc implementation in this file
7142 * 2) Original freezedrying code for scalable malloc. This code was apparently
7143 * based on the old freezedrying code and was fundamentally flawed in its
7144 * assumption that tracking allocated memory regions was adequate to fake
7145 * operations on freezedried memory. This doesn't work, since scalable
7146 * malloc does not store flags in front of large page-aligned allocations.
7147 * 3) Original szone-based freezedrying code.
7148 * 4) Fresher malloc with tiny zone
7149 * 5) 32/64bit compatible malloc
7150 * 6) Metadata within 1MB and 8MB region for tiny and small
7152 * No version backward compatibility is provided, but the version number does
7153 * make it possible for malloc_jumpstart() to return an error if the application
7154 * was freezedried with an older version of malloc.
7156 #define MALLOC_FREEZEDRY_VERSION 6
7165 frozen_malloc(szone_t
*zone
, size_t new_size
)
7167 return malloc(new_size
);
7171 frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
)
7173 return calloc(num_items
, size
);
7177 frozen_valloc(szone_t
*zone
, size_t new_size
)
7179 return valloc(new_size
);
7183 frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
)
7185 size_t old_size
= szone_size(zone
, ptr
);
7188 if (new_size
<= old_size
) {
7191 new_ptr
= malloc(new_size
);
7193 memcpy(new_ptr
, ptr
, old_size
);
7199 frozen_free(szone_t
*zone
, void *ptr
)
7204 frozen_destroy(szone_t
*zone
)
7208 /********* Pseudo-private API for emacs unexec ************/
7211 * malloc_freezedry() records all of the szones in use, so that they can be
7212 * partially reconstituted by malloc_jumpstart(). Due to the differences
7213 * between reconstituted memory regions and those created by the szone code,
7214 * care is taken not to reallocate from the freezedried memory, except in the
7215 * case of a non-growing realloc().
7217 * Due to the flexibility provided by the zone registration mechanism, it is
7218 * impossible to implement generic freezedrying for any zone type. This code
7219 * only handles applications that use the szone allocator, so malloc_freezedry()
7220 * returns 0 (error) if any non-szone zones are encountered.
7224 malloc_freezedry(void)
7226 extern unsigned malloc_num_zones
;
7227 extern malloc_zone_t
**malloc_zones
;
7228 malloc_frozen
*data
;
7231 /* Allocate space in which to store the freezedry state. */
7232 data
= (malloc_frozen
*) malloc(sizeof(malloc_frozen
));
7234 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
7235 data
->version
= MALLOC_FREEZEDRY_VERSION
;
7237 /* Allocate the array of szone pointers. */
7238 data
->nszones
= malloc_num_zones
;
7239 data
->szones
= (szone_t
*) calloc(malloc_num_zones
, sizeof(szone_t
));
7242 * Fill in the array of szone structures. They are copied rather than
7243 * referenced, since the originals are likely to be clobbered during malloc
7246 for (i
= 0; i
< malloc_num_zones
; i
++) {
7247 if (strcmp(malloc_zones
[i
]->zone_name
, "DefaultMallocZone")) {
7248 /* Unknown zone type. */
7253 memcpy(&data
->szones
[i
], malloc_zones
[i
], sizeof(szone_t
));
7256 return((uintptr_t)data
);
7260 malloc_jumpstart(uintptr_t cookie
)
7262 malloc_frozen
*data
= (malloc_frozen
*)cookie
;
7265 if (data
->version
!= MALLOC_FREEZEDRY_VERSION
) {
7266 /* Unsupported freezedry version. */
7270 for (i
= 0; i
< data
->nszones
; i
++) {
7271 /* Set function pointers. Even the functions that stay the same must be
7272 * set, since there are no guarantees that they will be mapped to the
7273 * same addresses. */
7274 data
->szones
[i
].basic_zone
.size
= (void *) szone_size
;
7275 data
->szones
[i
].basic_zone
.malloc
= (void *) frozen_malloc
;
7276 data
->szones
[i
].basic_zone
.calloc
= (void *) frozen_calloc
;
7277 data
->szones
[i
].basic_zone
.valloc
= (void *) frozen_valloc
;
7278 data
->szones
[i
].basic_zone
.free
= (void *) frozen_free
;
7279 data
->szones
[i
].basic_zone
.realloc
= (void *) frozen_realloc
;
7280 data
->szones
[i
].basic_zone
.destroy
= (void *) frozen_destroy
;
7281 data
->szones
[i
].basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
7283 /* Register the freezedried zone. */
7284 malloc_zone_register(&data
->szones
[i
].basic_zone
);