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 "scalable_malloc.h"
41 #include "malloc_printf.h"
43 #include "magmallocProvider.h"
45 #include <pthread_internals.h> /* for pthread_lock_t SPI */
46 #include <pthread.h> /* for pthread API */
50 #include <mach/vm_statistics.h>
51 #include <mach/mach_init.h>
52 #include <sys/types.h>
54 #include <sys/sysctl.h>
55 #include <libkern/OSAtomic.h>
56 #include <mach-o/dyld.h> /* for NSVersionOfLinkTimeLibrary() */
58 /********************* DEFINITIONS ************************/
60 #define DEBUG_MALLOC 0 // set to one to debug malloc itself
62 #define DEBUG_CLIENT 0 // set to one to debug malloc client
65 #warning DEBUG_MALLOC ENABLED
68 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) \
70 if (__is_threaded && TRY_LOCK(mag_ptr->magazine_lock)) { \
71 malloc_printf("*** magazine_lock was not set %p in %s\n", \
72 mag_ptr->magazine_lock, fun); \
76 # define INLINE __inline__
77 # define ALWAYSINLINE __attribute__((always_inline))
78 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) {}
81 # define NOINLINE __attribute__((noinline))
83 #if defined(__i386__) || defined(__x86_64__)
84 #define CACHE_ALIGN __attribute__ ((aligned (128) )) /* Future-proofing at 128B */
85 #elif defined(__ppc__) || defined(__ppc64__)
86 #define CACHE_ALIGN __attribute__ ((aligned (128) ))
92 * Access to global variables is slow, so optimise our handling of vm_page_size
95 #define _vm_page_size vm_page_size /* to get to the originals */
96 #define _vm_page_shift vm_page_shift
97 #define vm_page_size 4096 /* our normal working sizes */
98 #define vm_page_shift 12
101 * msize - a type to refer to the number of quanta of a tiny or small
102 * allocation. A tiny block with an msize of 3 would be 3 << SHIFT_TINY_QUANTUM
105 typedef unsigned short msize_t
;
117 typedef unsigned int grain_t
; // N.B. wide enough to index all free slots
119 typedef int mag_index_t
;
121 #define CHECK_REGIONS (1 << 31)
123 #define MAX_RECORDER_BUFFER 256
125 /********************* DEFINITIONS for tiny ************************/
128 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the
129 * szone's hashed_regions pointer.
131 * Each region is laid out as a heap, followed by a header block, all within
132 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header
133 * is 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
135 * The header block is arranged as in struct tiny_region defined just below, and
136 * consists of two bitfields (or bit arrays) interleaved 32 bits by 32 bits.
138 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding
139 * TINY_QUANTUM block within the heap.
141 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
142 * that the corresponding quantum is the first quantum in a block (either in use or free). The
143 * in-use bit is set for the header if the block has been handed out (allocated). If the header
144 * bit is not set, the in-use bit is invalid.
146 * The szone maintains an array of NUM_TINY_SLOTS freelists, each of which is used to hold
147 * free objects of the corresponding quantum size.
149 * A free block is laid out depending on its size, in order to fit all free
150 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
151 * not store their size in the block, instead relying on the header information
152 * to determine their size. Blocks of two or more quanta have room to store
153 * their size in the block, and store it both after the 'next' pointer, and in
154 * the last 2 bytes of the block.
157 * Offset (32-bit mode) (64-bit mode)
163 * Offset (32-bit mode) (64-bit mode)
166 * 0x8 0x10 : size (in quantum counts)
167 * end - 2 end - 2 : size (in quantum counts)
170 * All fields are pointer-sized, except for the size which is an unsigned short.
174 #define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
175 #define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
177 #define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
180 #define NUM_TINY_SLOTS 64 // number of slots for free-lists
182 #define NUM_TINY_SLOTS 32 // number of slots for free-lists
185 #define NUM_TINY_BLOCKS 64520
186 #define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
187 #define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
188 #define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM) // 20
191 * Enough room for the data, followed by the bit arrays (2-bits per block)
192 * plus rounding to the nearest page.
194 #define CEIL_NUM_TINY_BLOCKS_WORDS (((NUM_TINY_BLOCKS + 31) & ~31) >> 5)
195 #define TINY_METADATA_SIZE (sizeof(region_trailer_t) + sizeof(tiny_header_inuse_pair_t) * CEIL_NUM_TINY_BLOCKS_WORDS)
196 #define TINY_REGION_SIZE \
197 ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
199 #define TINY_METADATA_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
202 * Beginning and end pointers for a region's heap.
204 #define TINY_REGION_ADDRESS(region) ((void *)(region))
205 #define TINY_REGION_END(region) ((void *)(((uintptr_t)(region)) + (NUM_TINY_BLOCKS * TINY_QUANTUM)))
208 * Locate the heap base for a pointer known to be within a tiny region.
210 #define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
213 * Convert between byte and msize units.
215 #define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
216 #define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
219 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
221 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
223 #define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
226 * Layout of a tiny region
228 typedef uint32_t tiny_block_t
[4]; // assert(TINY_QUANTUM == sizeof(tiny_block_t))
230 typedef struct tiny_header_inuse_pair
234 } tiny_header_inuse_pair_t
;
236 typedef struct region_trailer
238 struct region_trailer
*prev
;
239 struct region_trailer
*next
;
240 boolean_t recirc_suitable
;
242 mag_index_t mag_index
;
245 typedef struct tiny_region
247 tiny_block_t blocks
[NUM_TINY_BLOCKS
];
249 region_trailer_t trailer
;
251 // The interleaved bit arrays comprising the header and inuse bitfields.
252 // The unused bits of each component in the last pair will be initialized to sentinel values.
253 tiny_header_inuse_pair_t pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
];
255 uint8_t pad
[TINY_REGION_SIZE
- (NUM_TINY_BLOCKS
* sizeof(tiny_block_t
)) - TINY_METADATA_SIZE
];
259 * Per-region meta data for tiny allocator
261 #define REGION_TRAILER_FOR_TINY_REGION(r) (&(((tiny_region_t)(r))->trailer))
262 #define MAGAZINE_INDEX_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->mag_index)
263 #define BYTES_USED_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->bytes_used)
266 * Locate the block header for a pointer known to be within a tiny region.
268 #define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)&(((tiny_region_t)TINY_REGION_FOR_PTR(_p))->pairs))
271 * Locate the inuse map for a given block header pointer.
273 #define TINY_INUSE_FOR_HEADER(_h) ((void *)&(((tiny_header_inuse_pair_t *)(_h))->inuse))
276 * Compute the bitmap index for a pointer known to be within a tiny region.
278 #define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
280 #define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
283 #warning TINY_CACHE turned off
286 #define TINY_REGION_PAYLOAD_BYTES (NUM_TINY_BLOCKS * TINY_QUANTUM)
288 /********************* DEFINITIONS for small ************************/
291 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's hashed_regions
294 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
295 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
296 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
299 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
300 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
302 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
303 * of the corresponding quantum size.
305 * A free block is laid out as:
307 * Offset (32-bit mode) (64-bit mode)
310 * 0x8 0x10 : size (in quantum counts)
311 * end - 2 end - 2 : size (in quantum counts)
314 * All fields are pointer-sized, except for the size which is an unsigned short.
318 #define SMALL_IS_FREE (1 << 15)
320 #define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
321 #define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
323 #define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
326 * The number of slots in the free-list for small blocks. To avoid going to
327 * vm system as often on large memory machines, increase the number of free list
328 * spots above some amount of RAM installed in the system.
330 #define NUM_SMALL_SLOTS 32
331 #define NUM_SMALL_SLOTS_LARGEMEM 256
332 #define SMALL_BITMAP_WORDS 8
335 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
336 * convention msize=0 => msize = (1<<15)
338 #define NUM_SMALL_BLOCKS 16320
339 #define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
340 #define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
341 #define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
343 #define SMALL_METADATA_SIZE (sizeof(region_trailer_t) + NUM_SMALL_BLOCKS * sizeof(msize_t))
344 #define SMALL_REGION_SIZE \
345 ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
347 #define SMALL_METADATA_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
350 * Beginning and end pointers for a region's heap.
352 #define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
353 #define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
356 * Locate the heap base for a pointer known to be within a small region.
358 #define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
361 * Convert between byte and msize units.
363 #define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
364 #define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
366 #define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
369 * Layout of a small region
371 typedef uint32_t small_block_t
[SMALL_QUANTUM
/sizeof(uint32_t)];
373 typedef struct small_region
375 small_block_t blocks
[NUM_SMALL_BLOCKS
];
377 region_trailer_t trailer
;
379 msize_t small_meta_words
[NUM_SMALL_BLOCKS
];
381 uint8_t pad
[SMALL_REGION_SIZE
- (NUM_SMALL_BLOCKS
* sizeof(small_block_t
)) - SMALL_METADATA_SIZE
];
385 * Per-region meta data for small allocator
387 #define REGION_TRAILER_FOR_SMALL_REGION(r) (&(((small_region_t)(r))->trailer))
388 #define MAGAZINE_INDEX_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->mag_index)
389 #define BYTES_USED_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->bytes_used)
392 * Locate the metadata base for a pointer known to be within a small region.
394 #define SMALL_META_HEADER_FOR_PTR(_p) (((small_region_t)SMALL_REGION_FOR_PTR(_p))->small_meta_words)
397 * Compute the metadata index for a pointer known to be within a small region.
399 #define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
402 * Find the metadata word for a pointer known to be within a small region.
404 #define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
407 * Determine whether a pointer known to be within a small region points to memory which is free.
409 #define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
412 * Extract the msize value for a pointer known to be within a small region.
414 #define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
416 #define PROTECT_SMALL 0 // Should be 0: 1 is too slow for normal use
418 #define SMALL_CACHE 1
420 #warning SMALL_CACHE turned off
423 #define SMALL_REGION_PAYLOAD_BYTES (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
425 /************************* DEFINITIONS for large ****************************/
427 #define LARGE_THRESHOLD (15 * 1024) // strictly above this use "large"
428 #define LARGE_THRESHOLD_LARGEMEM (127 * 1024)
430 #if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
431 #error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
434 #if (LARGE_THRESHOLD_LARGEMEM > NUM_SMALL_SLOTS_LARGEMEM * SMALL_QUANTUM)
435 #error LARGE_THRESHOLD_LARGEMEM should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
439 * When all memory is touched after a copy, vm_copy() is always a lose
440 * But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages
443 * This must be larger than LARGE_THRESHOLD
445 #define VM_COPY_THRESHOLD (40 * 1024)
446 #define VM_COPY_THRESHOLD_LARGEMEM (128 * 1024)
449 vm_address_t address
;
451 boolean_t did_madvise_reusable
;
454 #define LARGE_CACHE 1
456 #warning LARGE_CACHE turned off
458 #if defined(__LP64__)
459 #define LARGE_ENTRY_CACHE_SIZE 16
460 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x80000000) /* 2Gb */
462 #define LARGE_ENTRY_CACHE_SIZE 8
463 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x02000000) /* 32Mb */
465 #define LARGE_CACHE_SIZE_ENTRY_LIMIT (LARGE_CACHE_SIZE_LIMIT/LARGE_ENTRY_CACHE_SIZE)
467 /*******************************************************************************
468 * Definitions for region hash
469 ******************************************************************************/
471 typedef void * region_t
;
472 typedef region_t
* rgnhdl_t
; /* A pointer into hashed_regions array. */
474 #define INITIAL_NUM_REGIONS_SHIFT 6 // log2(INITIAL_NUM_REGIONS)
475 #define INITIAL_NUM_REGIONS (1 << INITIAL_NUM_REGIONS_SHIFT) // Must be a power of 2!
476 #define HASHRING_OPEN_ENTRY ((region_t) 0) // Initial value and sentinel marking end of collision chain
477 #define HASHRING_REGION_DEALLOCATED ((region_t)-1) // Region at this slot reclaimed by OS
478 #define HASH_BLOCKS_ALIGN TINY_BLOCKS_ALIGN // MIN( TINY_BLOCKS_ALIGN, SMALL_BLOCKS_ALIGN, ... )
480 typedef struct region_hash_generation
{
481 size_t num_regions_allocated
;
482 size_t num_regions_allocated_shift
; // log2(num_regions_allocated)
483 region_t
*hashed_regions
; // hashed by location
484 struct region_hash_generation
*nextgen
;
485 } region_hash_generation_t
;
487 /*******************************************************************************
488 * Per-processor magazine for tiny and small allocators
489 ******************************************************************************/
491 typedef struct { // vm_allocate()'d, so the array of magazines is page-aligned to begin with.
492 // Take magazine_lock first, Depot lock when needed for recirc, then szone->{tiny,small}_regions_lock when needed for alloc
493 pthread_lock_t magazine_lock CACHE_ALIGN
;
495 // One element deep "death row", optimizes malloc/free/malloc for identical size.
496 void *mag_last_free
; // low SHIFT_{TINY,SMALL}_QUANTUM bits indicate the msize
497 region_t mag_last_free_rgn
; // holds the region for mag_last_free
499 free_list_t
*mag_free_list
[256]; // assert( 256 >= MAX( NUM_TINY_SLOTS, NUM_SMALL_SLOTS_LARGEMEM ))
500 unsigned mag_bitmap
[8]; // assert( sizeof(mag_bitmap) << 3 >= sizeof(mag_free_list)/sizeof(free_list_t) )
502 // the last free region in the last block is treated as a big block in use that is not accounted for
503 size_t mag_bytes_free_at_end
;
504 region_t mag_last_region
; // Valid iff mag_bytes_free_at_end > 0
507 unsigned mag_num_objects
;
508 size_t mag_num_bytes_in_objects
;
509 size_t num_bytes_in_magazine
;
511 // recirculation list -- invariant: all regions owned by this magazine that meet the emptiness criteria
512 // are located nearer to the head of the list than any region that doesn't satisfy that criteria.
513 // Doubly linked list for efficient extraction.
514 unsigned recirculation_entries
;
515 region_trailer_t
*firstNode
;
516 region_trailer_t
*lastNode
;
519 uint64_t pad
[49]; // So sizeof(magazine_t) is 2560 bytes. FIXME: assert this at compile time
521 uint32_t pad
[45]; // So sizeof(magazine_t) is 1280 bytes. FIXME: assert this at compile time
525 #define TINY_MAX_MAGAZINES 16 /* MUST BE A POWER OF 2! */
526 #define TINY_MAGAZINE_PAGED_SIZE \
527 (((sizeof(magazine_t) * (TINY_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
528 ~ (vm_page_size - 1)) /* + 1 for the Depot */
530 #define SMALL_MAX_MAGAZINES 16 /* MUST BE A POWER OF 2! */
531 #define SMALL_MAGAZINE_PAGED_SIZE \
532 (((sizeof(magazine_t) * (SMALL_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
533 ~ (vm_page_size - 1)) /* + 1 for the Depot */
535 #define DEPOT_MAGAZINE_INDEX -1
537 /****************************** zone itself ***********************************/
540 * Note that objects whose adddress are held in pointers here must be pursued
541 * individually in the {tiny,small}_in_use_enumeration() routines. See for
542 * example the treatment of region_hash_generation and tiny_magazines below.
545 typedef struct szone_s
{ // vm_allocate()'d, so page-aligned to begin with.
546 malloc_zone_t basic_zone
;
547 pthread_key_t cpu_id_key
;
548 unsigned debug_flags
;
551 /* Regions for tiny objects */
552 pthread_lock_t tiny_regions_lock CACHE_ALIGN
;
553 size_t num_tiny_regions
;
554 size_t num_tiny_regions_dealloc
;
555 region_hash_generation_t
*tiny_region_generation
;
556 region_hash_generation_t trg
[2];
558 int num_tiny_magazines
;
559 unsigned num_tiny_magazines_mask
;
560 int num_tiny_magazines_mask_shift
;
561 magazine_t
*tiny_magazines
; // array of per-processor magazines
563 /* Regions for small objects */
564 pthread_lock_t small_regions_lock CACHE_ALIGN
;
565 size_t num_small_regions
;
566 size_t num_small_regions_dealloc
;
567 region_hash_generation_t
*small_region_generation
;
568 region_hash_generation_t srg
[2];
570 unsigned num_small_slots
; // determined by physmem size
572 int num_small_magazines
;
573 unsigned num_small_magazines_mask
;
574 int num_small_magazines_mask_shift
;
575 magazine_t
*small_magazines
; // array of per-processor magazines
577 /* large objects: all the rest */
578 pthread_lock_t large_szone_lock CACHE_ALIGN
; // One customer at a time for large
579 unsigned num_large_objects_in_use
;
580 unsigned num_large_entries
;
581 large_entry_t
*large_entries
; // hashed by location; null entries don't count
582 size_t num_bytes_in_large_objects
;
585 int large_entry_cache_oldest
;
586 int large_entry_cache_newest
;
587 large_entry_t large_entry_cache
[LARGE_ENTRY_CACHE_SIZE
]; // "death row" for large malloc/free
588 boolean_t large_legacy_reset_mprotect
;
589 size_t large_entry_cache_hoard_bytes
;
590 size_t large_entry_cache_hoard_lmit
;
593 /* flag and limits pertaining to altered malloc behavior for systems with
594 large amounts of physical memory */
595 unsigned is_largemem
;
596 unsigned large_threshold
;
597 unsigned vm_copy_threshold
;
599 /* security cookie */
602 /* Initial region list */
603 region_t initial_tiny_regions
[INITIAL_NUM_REGIONS
];
604 region_t initial_small_regions
[INITIAL_NUM_REGIONS
];
606 /* The purgeable zone constructed by create_purgeable_zone() would like to hand off tiny and small
607 * allocations to the default scalable zone. Record the latter as the "helper" zone here. */
608 struct szone_s
*helper_zone
;
611 #define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
613 #if DEBUG_MALLOC || DEBUG_CLIENT
614 static void szone_sleep(void);
616 __private_extern__
void malloc_error_break(void);
618 // msg prints after fmt, ...
619 static NOINLINE
void szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
622 static void protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
);
623 static void *allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
,
625 static void deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
);
626 static int madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
);
627 static kern_return_t
_szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
);
629 static INLINE mag_index_t
mag_get_thread_index(szone_t
*szone
) ALWAYSINLINE
;
630 static magazine_t
*mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
,
631 mag_index_t mag_index
);
633 static INLINE rgnhdl_t
hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
)
635 static void hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
);
636 static region_t
*hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
);
637 static region_t
*hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
,
638 size_t *mutable_shift
, size_t *new_size
);
640 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
) ALWAYSINLINE
;
641 static INLINE
uintptr_t free_list_checksum_ptr(szone_t
*szone
, void *p
) ALWAYSINLINE
;
642 static INLINE
void *free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
) ALWAYSINLINE
;
643 static unsigned free_list_count(szone_t
*szone
, free_list_t
*ptr
);
645 static INLINE
void recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
646 static INLINE
void recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
647 static INLINE
void recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
) ALWAYSINLINE
;
649 static INLINE
void BITARRAY_SET(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
650 static INLINE
void BITARRAY_CLR(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
651 static INLINE boolean_t
BITARRAY_BIT(uint32_t *bits
, msize_t index
) ALWAYSINLINE
;
653 static msize_t
get_tiny_free_size(const void *ptr
);
654 static msize_t
get_tiny_previous_free_msize(const void *ptr
);
655 static INLINE msize_t
get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
) ALWAYSINLINE
;
656 static INLINE
void set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
657 static INLINE
void set_tiny_meta_header_in_use_1(const void *ptr
) ALWAYSINLINE
;
658 static INLINE
void set_tiny_meta_header_middle(const void *ptr
) ALWAYSINLINE
;
659 static INLINE
void set_tiny_meta_header_free(const void *ptr
, msize_t msize
) ALWAYSINLINE
;
660 static INLINE boolean_t
tiny_meta_header_is_free(const void *ptr
) ALWAYSINLINE
;
661 static INLINE
void *tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
) ALWAYSINLINE
;
663 static void tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
664 static void tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
);
665 static INLINE region_t
tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
667 static void tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
);
668 static int tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
669 static size_t tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
);
670 static void tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
671 static void tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
672 static void tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
);
673 static boolean_t
tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
);
675 static INLINE
void tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
,
676 void *ptr
, msize_t msize
) ALWAYSINLINE
;
677 static void *tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
679 static boolean_t
tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
680 static boolean_t
tiny_check_region(szone_t
*szone
, region_t region
);
681 static kern_return_t
tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
682 memory_reader_t reader
, vm_range_recorder_t recorder
);
683 static void *tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
,
685 static INLINE
void *tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
686 static INLINE
void free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
) ALWAYSINLINE
;
687 static void print_tiny_free_list(szone_t
*szone
);
688 static void print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
);
689 static boolean_t
tiny_free_list_check(szone_t
*szone
, grain_t slot
);
691 static INLINE
void small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
) ALWAYSINLINE
;
692 static INLINE
void small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
) ALWAYSINLINE
;
693 static INLINE
void small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
) ALWAYSINLINE
;
694 static void small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
695 static void small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
);
696 static INLINE region_t
small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
) ALWAYSINLINE
;
698 static void small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
);
699 static int small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
700 static size_t small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
);
701 static void small_free_scan_depot_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
);
702 static void small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
);
703 static void small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
);
704 static boolean_t
small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
);
705 static INLINE
void small_free_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, region_t region
,
706 void *ptr
, msize_t msize
) ALWAYSINLINE
;
707 static void *small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
709 static boolean_t
small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
);
710 static boolean_t
small_check_region(szone_t
*szone
, region_t region
);
711 static kern_return_t
small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
712 memory_reader_t reader
, vm_range_recorder_t recorder
);
713 static void *small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
,
715 static INLINE
void *small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
) ALWAYSINLINE
;
716 static INLINE
void free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
) ALWAYSINLINE
;
717 static void print_small_free_list(szone_t
*szone
);
718 static void print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
);
719 static boolean_t
small_free_list_check(szone_t
*szone
, grain_t grain
);
722 static void large_debug_print(szone_t
*szone
);
724 static large_entry_t
*large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
);
725 static void large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
);
726 static INLINE
void large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
) ALWAYSINLINE
;
727 static INLINE large_entry_t
*large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
) ALWAYSINLINE
;
728 static void large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
,
729 vm_range_t
*range_to_deallocate
);
730 static large_entry_t
*large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
);
731 static vm_range_t
large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
);
732 static NOINLINE kern_return_t
large_in_use_enumerator(task_t task
, void *context
,
733 unsigned type_mask
, vm_address_t large_entries_address
,
734 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
);
735 static void *large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
, boolean_t cleared_requested
);
736 static NOINLINE
void free_large(szone_t
*szone
, void *ptr
);
737 static INLINE
int large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
) ALWAYSINLINE
;
740 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
742 static NOINLINE
void szone_free(szone_t
*szone
, void *ptr
);
743 static NOINLINE
void *szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
);
744 static NOINLINE
void *szone_malloc(szone_t
*szone
, size_t size
);
745 static NOINLINE
void *szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
746 static NOINLINE
void *szone_valloc(szone_t
*szone
, size_t size
);
747 static NOINLINE
size_t szone_size_try_large(szone_t
*szone
, const void *ptr
);
748 static NOINLINE
size_t szone_size(szone_t
*szone
, const void *ptr
);
749 static NOINLINE
void *szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
750 static NOINLINE
void *szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
751 static NOINLINE
void szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
752 static NOINLINE
unsigned szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
753 static NOINLINE
void szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
754 static void szone_destroy(szone_t
*szone
);
755 static NOINLINE
size_t szone_good_size(szone_t
*szone
, size_t size
);
757 static NOINLINE boolean_t
szone_check_all(szone_t
*szone
, const char *function
);
758 static boolean_t
szone_check(szone_t
*szone
);
759 static kern_return_t
szone_ptr_in_use_enumerator(task_t task
, void *context
,
760 unsigned type_mask
, vm_address_t zone_address
,
761 memory_reader_t reader
, vm_range_recorder_t recorder
);
762 static NOINLINE
void szone_print(szone_t
*szone
, boolean_t verbose
);
763 static void szone_log(malloc_zone_t
*zone
, void *log_address
);
764 static void szone_force_lock(szone_t
*szone
);
765 static void szone_force_unlock(szone_t
*szone
);
766 static boolean_t
szone_locked(szone_t
*szone
);
768 static void szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
770 static void purgeable_free(szone_t
*szone
, void *ptr
);
771 static void *purgeable_malloc(szone_t
*szone
, size_t size
);
772 static void *purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
);
773 static void *purgeable_valloc(szone_t
*szone
, size_t size
);
774 static size_t purgeable_size(szone_t
*szone
, const void *ptr
);
775 static void *purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
);
776 static void *purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
);
777 static void purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
);
778 static unsigned purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
);
779 static void purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
);
780 static void purgeable_destroy(szone_t
*szone
);
781 static size_t purgeable_good_size(szone_t
*szone
, size_t size
);
783 static boolean_t
purgeable_check(szone_t
*szone
);
784 static kern_return_t
purgeable_ptr_in_use_enumerator(task_t task
, void *context
,
785 unsigned type_mask
, vm_address_t zone_address
,
786 memory_reader_t reader
, vm_range_recorder_t recorder
);
787 static void purgeable_print(szone_t
*szone
, boolean_t verbose
);
788 static void purgeable_log(malloc_zone_t
*zone
, void *log_address
);
789 static void purgeable_force_lock(szone_t
*szone
);
790 static void purgeable_force_unlock(szone_t
*szone
);
791 static boolean_t
purgeable_locked(szone_t
*szone
);
793 static void purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
);
795 static void *frozen_malloc(szone_t
*zone
, size_t new_size
);
796 static void *frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
);
797 static void *frozen_valloc(szone_t
*zone
, size_t new_size
);
798 static void *frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
);
799 static void frozen_free(szone_t
*zone
, void *ptr
);
800 static void frozen_destroy(szone_t
*zone
);
802 #define SZONE_LOCK(szone) \
804 LOCK(szone->large_szone_lock); \
807 #define SZONE_UNLOCK(szone) \
809 UNLOCK(szone->large_szone_lock); \
812 #define SZONE_TRY_LOCK(szone) \
813 TRY_LOCK(szone->large_szone_lock);
815 #define SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr) \
817 LOCK(mag_ptr->magazine_lock); \
820 #define SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr) \
822 UNLOCK(mag_ptr->magazine_lock); \
825 #define SZONE_MAGAZINE_PTR_TRY_LOCK(szone, mag_ptr) \
826 TRY_LOCK(mag_ptr->magazine_lock);
829 # define LOG(szone,ptr) \
830 (szone->log_address && (((uintptr_t)szone->log_address == -1) || \
831 (szone->log_address == (void *)(ptr))))
833 # define LOG(szone,ptr) 0
836 #if DEBUG_MALLOC || DEBUG_CLIENT
837 # define CHECK(szone,fun) \
838 if ((szone)->debug_flags & CHECK_REGIONS) \
839 szone_check_all(szone, fun)
841 # define CHECK(szone,fun) \
845 /********************* VERY LOW LEVEL UTILITIES ************************/
847 #if DEBUG_MALLOC || DEBUG_CLIENT
852 if (getenv("MallocErrorSleep")) {
853 _malloc_printf(ASL_LEVEL_NOTICE
, "*** sleeping to help debug\n");
854 sleep(3600); // to help debug
859 extern const char *__crashreporter_info__
;
861 // msg prints after fmt, ...
863 szone_error(szone_t
*szone
, int is_corruption
, const char *msg
, const void *ptr
, const char *fmt
, ...)
866 _SIMPLE_STRING b
= _simple_salloc();
868 if (szone
) SZONE_UNLOCK(szone
); // FIXME: unlock magazine and region locks?
872 _simple_vsprintf(b
, fmt
, ap
);
876 _simple_sprintf(b
, "*** error for object %p: %s\n", ptr
, msg
);
878 _simple_sprintf(b
, "*** error: %s\n", msg
);
880 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b
));
883 * Should only get here if vm_allocate() can't get a single page of
884 * memory, implying _simple_asl_log() would also fail. So we just
885 * print to the file descriptor.
889 _malloc_vprintf(MALLOC_PRINTF_NOLOG
, fmt
, ap
);
893 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error for object %p: %s\n", ptr
, msg
);
895 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** error: %s\n", msg
);
897 _malloc_printf(MALLOC_PRINTF_NOLOG
, "*** set a breakpoint in malloc_error_break to debug\n");
899 malloc_error_break();
901 szone_print(szone
, 1);
907 // Call abort() if this is a memory corruption error and the abort on
908 // corruption flag is set, or if any error should abort.
909 if ((is_corruption
&& (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_CORRUPTION
)) ||
910 (szone
->debug_flags
& SCALABLE_MALLOC_ABORT_ON_ERROR
)) {
911 __crashreporter_info__
= b
? _simple_string(b
) : msg
;
919 protect(void *address
, size_t size
, unsigned protection
, unsigned debug_flags
)
923 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_PRELUDE
)) {
924 err
= vm_protect(mach_task_self(), (vm_address_t
)(uintptr_t)address
- vm_page_size
, vm_page_size
, 0, protection
);
926 malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
927 protection
,(uintptr_t)address
- (1 << vm_page_shift
));
930 if (!(debug_flags
& SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE
)) {
931 err
= vm_protect(mach_task_self(), (vm_address_t
)(uintptr_t)address
+ size
, vm_page_size
, 0, protection
);
933 malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
934 protection
, (uintptr_t)address
+ size
);
940 allocate_pages(szone_t
*szone
, size_t size
, unsigned char align
, unsigned debug_flags
, int vm_page_label
)
942 // align specifies a desired alignment (as a log) or 0 if no alignment requested
944 uintptr_t addr
= 0, aligned_address
;
945 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
946 boolean_t purgeable
= debug_flags
& SCALABLE_MALLOC_PURGEABLE
;
947 size_t allocation_size
= round_page(size
);
949 int flags
= VM_MAKE_TAG(vm_page_label
);
951 if (align
) add_guard_pages
= 0; // too cumbersome to deal with that
952 if (!allocation_size
) allocation_size
= 1 << vm_page_shift
;
953 if (add_guard_pages
) allocation_size
+= 2 * (1 << vm_page_shift
);
954 if (align
) allocation_size
+= (size_t)1 << align
;
955 if (purgeable
) flags
|= VM_FLAGS_PURGABLE
;
957 if (allocation_size
< size
) // size_t arithmetic wrapped!
960 vm_addr
= mmap(0, allocation_size
, PROT_READ
| PROT_WRITE
, MAP_ANON
| MAP_PRIVATE
, flags
, 0);
961 if ((uintptr_t)vm_addr
== -1) {
962 szone_error(szone
, 0, "can't allocate region", NULL
, "*** mmap(size=%lu) failed (error code=%d)\n",
963 allocation_size
, errno
);
966 addr
= (uintptr_t)vm_addr
;
969 aligned_address
= (addr
+ ((uintptr_t)1 << align
) - 1) & ~ (((uintptr_t)1 << align
) - 1);
970 if (aligned_address
!= addr
) {
971 delta
= aligned_address
- addr
;
972 if (munmap((void *)addr
, delta
) == -1)
973 malloc_printf("*** munmap unaligned header failed with %d\n", errno
);
974 addr
= aligned_address
;
975 allocation_size
-= delta
;
977 if (allocation_size
> size
) {
978 if (munmap((void *)(addr
+ size
), allocation_size
- size
) == -1)
979 malloc_printf("*** munmap unaligned footer failed with %d\n", errno
);
982 if (add_guard_pages
) {
983 addr
+= (uintptr_t)1 << vm_page_shift
;
984 protect((void *)addr
, size
, 0, debug_flags
);
990 deallocate_pages(szone_t
*szone
, void *addr
, size_t size
, unsigned debug_flags
)
993 boolean_t add_guard_pages
= debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
;
995 if (add_guard_pages
) {
996 addr
= (void *)((uintptr_t)addr
- (1 << vm_page_shift
));
997 size
+= 2 * (1 << vm_page_shift
);
999 err
= munmap(addr
, size
);
1000 if ((err
== -1) && szone
)
1001 szone_error(szone
, 0, "Can't deallocate_pages region", addr
, NULL
);
1005 madvise_free_range(szone_t
*szone
, region_t r
, uintptr_t pgLo
, uintptr_t pgHi
)
1008 size_t len
= pgHi
- pgLo
;
1011 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
)
1012 memset((void *)pgLo
, 0xed, len
); // Scribble on MADV_FREEd memory
1014 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)r
, (void *)pgLo
, len
); // DTrace USDT Probe
1015 if (-1 == madvise((void *)pgLo
, len
, MADV_FREE_REUSABLE
)) {
1016 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
1018 szone_error(szone
, 1, "madvise_free_range madvise(..., MADV_FREE_REUSABLE) failed", (void *)pgLo
, NULL
);
1025 static kern_return_t
1026 _szone_default_reader(task_t task
, vm_address_t address
, vm_size_t size
, void **ptr
)
1028 *ptr
= (void *)address
;
1032 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1033 // pthread_t's are page aligned, (sometimes even in ascending sequence). These hash well.
1034 // See Knuth TAOCP, Vol. 3.
1036 #define HASH_SELF() \
1037 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 11400714819323198549ULL) >> (64 - szone->num_tiny_magazines_mask_shift)
1039 #define HASH_SELF() \
1040 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 2654435761UL) >> (32 - szone->num_tiny_magazines_mask_shift)
1043 #if defined(__i386__) || defined(__x86_64__)
1044 #define __APPLE_API_PRIVATE
1045 #include <machine/cpu_capabilities.h>
1046 #define _COMM_PAGE_VERSION_REQD 9
1047 #undef __APPLE_API_PRIVATE
1050 * These commpage routines provide fast access to the logical cpu number
1051 * of the calling processor assuming no pre-emption occurs.
1053 #define CPU_NUMBER() (((int (*)()) _COMM_PAGE_CPU_NUMBER)()) /* Zero-based */
1055 static INLINE mag_index_t
1056 mag_get_thread_index(szone_t
*szone
)
1061 return CPU_NUMBER() & (TINY_MAX_MAGAZINES
- 1);
1064 #elif defined(__arm__)
1066 static INLINE mag_index_t
1067 mag_get_thread_index(szone_t
*szone
)
1073 #warning deriving magazine index from pthread_self() [want processor number]
1075 static INLINE mag_index_t
1076 mag_get_thread_index(szone_t
*szone
)
1080 else if ((pthread_key_t
) -1 == szone
->cpu_id_key
) { // In case pthread_key_create() failed.
1083 mag_index_t idx
= (mag_index_t
)(intptr_t)pthread_getspecific(szone
->cpu_id_key
);
1085 // Has this thread been hinted with a non-zero value [i.e. 1 + cpuid()] ?
1086 // If so, bump down the hint to a zero-based magazine index and return it.
1090 // No hint available. Contruct a magazine index for this thread ...
1093 // bump up the hint to exclude zero and try to memorize it ...
1094 pthread_setspecific(szone
->cpu_id_key
, (const void *)((uintptr_t)idx
+ 1));
1096 // and return the (zero-based) magazine index.
1104 mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
, mag_index_t mag_index
)
1106 mag_index_t refreshed_index
;
1107 magazine_t
*mag_ptr
= &(magazines
[mag_index
]);
1109 // Take the lock on entry.
1110 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1112 // Now in the time it took to acquire the lock, the region may have migrated
1113 // from one magazine to another. In which case the magazine lock we obtained
1114 // (namely magazines[mag_index].mag_lock) is stale. If so, keep on tryin' ...
1115 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
1117 SZONE_MAGAZINE_PTR_UNLOCK(szone
, mag_ptr
);
1119 mag_index
= refreshed_index
;
1120 mag_ptr
= &(magazines
[mag_index
]);
1121 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1127 /*******************************************************************************
1128 * Region hash implementation
1130 * This is essentially a duplicate of the existing Large allocator hash, minus
1131 * the ability to remove entries. The two should be combined eventually.
1132 ******************************************************************************/
1133 #pragma mark region hash
1136 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
1139 * FIXME: If consecutive queries of the same region are likely, a one-entry
1140 * cache would likely be a significant performance win here.
1142 static INLINE rgnhdl_t
1143 hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1144 size_t index
, hash_index
;
1150 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1151 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1152 // this hash works really well. See Knuth TAOCP, Vol. 3.
1154 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1156 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1159 entry
= regions
+ index
;
1164 if (++index
== num_entries
)
1166 } while (index
!= hash_index
);
1171 * hash_region_insert_no_lock - Insert a region into the hash ring.
1174 hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1175 size_t index
, hash_index
;
1178 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1179 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1180 // this hash works really well. See Knuth TAOCP, Vol. 3.
1182 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1184 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1187 entry
= regions
+ index
;
1188 if (*entry
== HASHRING_OPEN_ENTRY
|| *entry
== HASHRING_REGION_DEALLOCATED
) {
1192 if (++index
== num_entries
)
1194 } while (index
!= hash_index
);
1198 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
1199 * must be a VM allocation as to avoid recursing between allocating a new small
1200 * region, and asking the small region to allocate space for the new list of
1204 hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
)
1206 size_t size
= num_entries
* sizeof(region_t
);
1208 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC
);
1212 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
1213 * Return the new region and new size to update the szone. Do not deallocate
1214 * the old entries since someone may still be allocating them.
1217 hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
, size_t *mutable_shift
,
1220 // double in size and allocate memory for the regions
1221 *new_size
= old_size
+ old_size
;
1222 *mutable_shift
= *mutable_shift
+ 1;
1223 region_t
*new_regions
= hash_regions_alloc_no_lock(szone
, *new_size
);
1225 // rehash the entries into the new list
1227 for (index
= 0; index
< old_size
; ++index
) {
1228 region_t r
= regions
[index
];
1229 if (r
!= HASHRING_OPEN_ENTRY
&& r
!= HASHRING_REGION_DEALLOCATED
)
1230 hash_region_insert_no_lock(new_regions
, *new_size
, *mutable_shift
, r
);
1235 /********************* FREE LIST UTILITIES ************************/
1237 // A free list entry is comprised of a pair of pointers, previous and next.
1238 // These are used to implement a doubly-linked list, which permits efficient
1241 // Because the free list entries are previously freed objects, a misbehaved
1242 // program may write to a pointer after it has called free() on that pointer,
1243 // either by dereference or buffer overflow from an adjacent pointer. This write
1244 // would then corrupt the free list's previous and next pointers, leading to a
1245 // crash. In order to detect this case, we take advantage of the fact that
1246 // malloc'd pointers are known to be at least 16 byte aligned, and thus have
1247 // at least 4 trailing zero bits.
1249 // When an entry is added to the free list, a checksum of the previous and next
1250 // pointers is calculated and written to the low four bits of the respective
1251 // pointers. Upon detection of an invalid checksum, an error is logged and NULL
1252 // is returned. Since all code which un-checksums pointers checks for a NULL
1253 // return, a potentially crashing or malicious dereference is avoided at the
1254 // cost of leaking the corrupted block, and any subsequent blocks on the free
1255 // list of that size.
1257 static NOINLINE
void
1258 free_list_checksum_botch(szone_t
*szone
, free_list_t
*ptr
)
1260 szone_error(szone
, 1, "incorrect checksum for freed object "
1261 "- object was probably modified after being freed.", ptr
, NULL
);
1264 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
)
1268 chk
= (unsigned char)(ptr
>> 0);
1269 chk
+= (unsigned char)(ptr
>> 8);
1270 chk
+= (unsigned char)(ptr
>> 16);
1271 chk
+= (unsigned char)(ptr
>> 24);
1273 chk
+= (unsigned char)(ptr
>> 32);
1274 chk
+= (unsigned char)(ptr
>> 40);
1275 chk
+= (unsigned char)(ptr
>> 48);
1276 chk
+= (unsigned char)(ptr
>> 56);
1279 return chk
& (uintptr_t)0xF;
1282 static INLINE
uintptr_t
1283 free_list_checksum_ptr(szone_t
*szone
, void *ptr
)
1285 uintptr_t p
= (uintptr_t)ptr
;
1286 return p
| free_list_gen_checksum(p
^ szone
->cookie
);
1289 static INLINE
void *
1290 free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
)
1293 p
.u
= (ptr
->u
>> 4) << 4;
1295 if ((ptr
->u
& (uintptr_t)0xF) != free_list_gen_checksum(p
.u
^ szone
->cookie
))
1297 free_list_checksum_botch(szone
, (free_list_t
*)ptr
);
1304 free_list_count(szone_t
*szone
, free_list_t
*ptr
)
1310 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
1316 recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1318 // excise node from list
1319 if (NULL
== node
->prev
)
1320 mag_ptr
->firstNode
= node
->next
;
1322 node
->prev
->next
= node
->next
;
1324 if (NULL
== node
->next
)
1325 mag_ptr
->lastNode
= node
->prev
;
1327 node
->next
->prev
= node
->prev
;
1329 mag_ptr
->recirculation_entries
--;
1333 recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1335 if (NULL
== mag_ptr
->lastNode
) {
1336 mag_ptr
->firstNode
= node
;
1339 node
->prev
= mag_ptr
->lastNode
;
1340 mag_ptr
->lastNode
->next
= node
;
1342 mag_ptr
->lastNode
= node
;
1344 node
->recirc_suitable
= FALSE
;
1345 mag_ptr
->recirculation_entries
++;
1349 recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1351 if (NULL
== mag_ptr
->firstNode
) {
1352 mag_ptr
->lastNode
= node
;
1355 node
->next
= mag_ptr
->firstNode
;
1356 mag_ptr
->firstNode
->prev
= node
;
1358 mag_ptr
->firstNode
= node
;
1360 node
->recirc_suitable
= FALSE
;
1361 mag_ptr
->recirculation_entries
++;
1364 /* Macros used to manipulate the uint32_t quantity mag_bitmap. */
1366 /* BITMAPV variants are used by tiny. */
1367 #if defined(__LP64__)
1368 // assert(NUM_SLOTS == 64) in which case (slot >> 5) is either 0 or 1
1369 #define BITMAPV_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1370 #define BITMAPV_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1371 #define BITMAPV_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1372 #define BITMAPV_CTZ(bitmap) (__builtin_ctzl(bitmap))
1374 // assert(NUM_SLOTS == 32) in which case (slot >> 5) is always 0, so code it that way
1375 #define BITMAPV_SET(bitmap,slot) (bitmap[0] |= 1 << (slot))
1376 #define BITMAPV_CLR(bitmap,slot) (bitmap[0] &= ~ (1 << (slot)))
1377 #define BITMAPV_BIT(bitmap,slot) ((bitmap[0] >> (slot)) & 1)
1378 #define BITMAPV_CTZ(bitmap) (__builtin_ctz(bitmap))
1381 /* BITMAPN is used by small. (slot >> 5) takes on values from 0 to 7. */
1382 #define BITMAPN_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1383 #define BITMAPN_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1384 #define BITMAPN_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1386 /* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
1387 #define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap[0]))
1389 /********************* TINY FREE LIST UTILITIES ************************/
1391 // We encode the meta-headers as follows:
1392 // Each quantum has an associated set of 2 bits:
1393 // block_header when 1 says this block is the beginning of a block
1394 // in_use when 1 says this block is in use
1395 // so a block in use of size 3 is 1-1 0-X 0-X
1396 // for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
1397 // for a block middle the bits are 0-0
1399 // We store the meta-header bit arrays by interleaving them 32 bits at a time.
1400 // Initial 32 bits of block_header, followed by initial 32 bits of in_use, followed
1401 // by next 32 bits of block_header, followed by next 32 bits of in_use, etc.
1402 // This localizes memory references thereby reducing cache and TLB pressures.
1405 BITARRAY_SET(uint32_t *bits
, msize_t index
)
1407 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1408 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1409 bits
[(index
>> 5) << 1] |= (1 << (index
& 31));
1413 BITARRAY_CLR(uint32_t *bits
, msize_t index
)
1415 bits
[(index
>> 5) << 1] &= ~(1 << (index
& 31));
1418 static INLINE boolean_t
1419 BITARRAY_BIT(uint32_t *bits
, msize_t index
)
1421 return ((bits
[(index
>> 5) << 1]) >> (index
& 31)) & 1;
1425 static INLINE
void bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
) ALWAYSINLINE
;
1428 bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
)
1430 // start >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1431 // (start >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1432 uint32_t *addr
= bits
+ ((start
>> 5) << 1);
1434 uint32_t span
= end
- start
;
1439 addr
[0] &= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1440 addr
[2] &= (0xFFFFFFFFU
<< (end
- 32));
1442 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1443 mask
|= (0xFFFFFFFFU
<< end
);
1450 * Obtain the size of a free tiny block (in msize_t units).
1453 get_tiny_free_size(const void *ptr
)
1455 void *next_block
= (void *)((uintptr_t)ptr
+ TINY_QUANTUM
);
1456 void *region_end
= TINY_REGION_END(TINY_REGION_FOR_PTR(ptr
));
1458 // check whether the next block is outside the tiny region or a block header
1459 // if so, then the size of this block is one, and there is no stored size.
1460 if (next_block
< region_end
)
1462 uint32_t *next_header
= TINY_BLOCK_HEADER_FOR_PTR(next_block
);
1463 msize_t next_index
= TINY_INDEX_FOR_PTR(next_block
);
1465 if (!BITARRAY_BIT(next_header
, next_index
))
1466 return TINY_FREE_SIZE(ptr
);
1472 * Get the size of the previous free block, which is stored in the last two
1473 * bytes of the block. If the previous block is not free, then the result is
1477 get_tiny_previous_free_msize(const void *ptr
)
1479 // check whether the previous block is in the tiny region and a block header
1480 // if so, then the size of the previous block is one, and there is no stored
1482 if (ptr
!= TINY_REGION_FOR_PTR(ptr
))
1484 void *prev_block
= (void *)((uintptr_t)ptr
- TINY_QUANTUM
);
1485 uint32_t *prev_header
= TINY_BLOCK_HEADER_FOR_PTR(prev_block
);
1486 msize_t prev_index
= TINY_INDEX_FOR_PTR(prev_block
);
1487 if (BITARRAY_BIT(prev_header
, prev_index
))
1489 return TINY_PREVIOUS_MSIZE(ptr
);
1491 // don't read possibly unmapped memory before the beginning of the region
1495 static INLINE msize_t
1496 get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
)
1498 // returns msize and is_free
1499 // may return 0 for the msize component (meaning 65536)
1500 uint32_t *block_header
;
1503 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1504 index
= TINY_INDEX_FOR_PTR(ptr
);
1506 msize_t midx
= (index
>> 5) << 1;
1507 uint32_t mask
= 1 << (index
& 31);
1509 if (0 == (block_header
[midx
] & mask
)) // if (!BITARRAY_BIT(block_header, index))
1511 if (0 == (block_header
[midx
+ 1] & mask
)) { // if (!BITARRAY_BIT(in_use, index))
1513 return get_tiny_free_size(ptr
);
1516 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1517 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1518 #if defined(__LP64__)
1519 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1520 // That's guaranteed to be somewhwere in the next 64 bits. And those bits could span three
1521 // uint32_t block_header elements. Collect the bits into a single uint64_t and measure up with ffsl.
1522 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1523 uint32_t bitidx
= index
& 31;
1524 uint64_t word_lo
= addr
[0];
1525 uint64_t word_mid
= addr
[2];
1526 uint64_t word_hi
= addr
[4];
1527 uint64_t word_lomid
= (word_lo
>> bitidx
) | (word_mid
<< (32 - bitidx
));
1528 uint64_t word
= bitidx
? word_lomid
| (word_hi
<< (64 - bitidx
)) : word_lomid
;
1529 uint32_t result
= __builtin_ffsl(word
>> 1);
1531 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1532 // That's guaranteed to be somwhwere in the next 32 bits. And those bits could span two
1533 // uint32_t block_header elements. Collect the bits into a single uint32_t and measure up with ffs.
1534 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1535 uint32_t bitidx
= index
& 31;
1536 uint32_t word
= bitidx
? (addr
[0] >> bitidx
) | (addr
[2] << (32 - bitidx
)) : addr
[0];
1537 uint32_t result
= __builtin_ffs(word
>> 1);
1543 set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
)
1545 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1546 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1547 msize_t clr_msize
= msize
- 1;
1548 msize_t midx
= (index
>> 5) << 1;
1549 uint32_t val
= (1 << (index
& 31));
1552 if (msize
>= NUM_TINY_SLOTS
)
1553 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr
, msize
);
1554 if ((unsigned)index
+ (unsigned)msize
> 0x10000)
1555 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr
, msize
);
1558 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1559 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1561 // bitarray_mclr(block_header, index, end_bit);
1562 // bitarray_mclr(in_use, index, end_bit);
1565 midx
= (index
>> 5) << 1;
1567 unsigned start
= index
& 31;
1568 unsigned end
= start
+ clr_msize
;
1570 #if defined(__LP64__)
1572 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1573 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 64));
1574 block_header
[midx
+ 0] &= mask0
; // clear header
1575 block_header
[midx
+ 1] &= mask0
; // clear in_use
1576 block_header
[midx
+ 2] = 0; // clear header
1577 block_header
[midx
+ 3] = 0; // clear in_use
1578 block_header
[midx
+ 4] &= mask1
; // clear header
1579 block_header
[midx
+ 5] &= mask1
; // clear in_use
1583 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1584 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 32));
1585 block_header
[midx
+ 0] &= mask0
;
1586 block_header
[midx
+ 1] &= mask0
;
1587 block_header
[midx
+ 2] &= mask1
;
1588 block_header
[midx
+ 3] &= mask1
;
1590 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1591 mask
|= (0xFFFFFFFFU
<< end
);
1592 block_header
[midx
+ 0] &= mask
;
1593 block_header
[midx
+ 1] &= mask
;
1596 // we set the block_header bit for the following block to reaffirm next block is a block
1598 midx
= (index
>> 5) << 1;
1599 val
= (1 << (index
& 31));
1600 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize));
1606 mf
= get_tiny_meta_header(ptr
, &ff
);
1608 malloc_printf("setting header for tiny in_use %p : %d\n", ptr
, msize
);
1609 malloc_printf("reading header for tiny %p : %d %d\n", ptr
, mf
, ff
);
1616 set_tiny_meta_header_in_use_1(const void *ptr
) // As above with msize == 1
1618 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1619 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1620 msize_t midx
= (index
>> 5) << 1;
1621 uint32_t val
= (1 << (index
& 31));
1623 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1624 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1627 midx
= (index
>> 5) << 1;
1628 val
= (1 << (index
& 31));
1630 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize))
1634 set_tiny_meta_header_middle(const void *ptr
)
1636 // indicates this block is in the middle of an in use block
1637 uint32_t *block_header
;
1641 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1642 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1643 index
= TINY_INDEX_FOR_PTR(ptr
);
1645 BITARRAY_CLR(block_header
, index
);
1646 BITARRAY_CLR(in_use
, index
);
1650 set_tiny_meta_header_free(const void *ptr
, msize_t msize
)
1652 // !msize is acceptable and means 65536
1653 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1654 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1655 msize_t midx
= (index
>> 5) << 1;
1656 uint32_t val
= (1 << (index
& 31));
1659 if ((unsigned)index
+ (unsigned)msize
> 0x10000) {
1660 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr
, msize
);
1664 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1665 block_header
[midx
+ 1] &= ~val
; // BITARRAY_CLR(in_use, index);
1667 // mark the end of this block if msize is > 1. For msize == 0, the whole
1668 // region is free, so there is no following block. For msize == 1, there is
1669 // no space to write the size on 64 bit systems. The size for 1 quantum
1670 // blocks is computed from the metadata bitmaps.
1672 void *follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
1673 TINY_PREVIOUS_MSIZE(follower
) = msize
;
1674 TINY_FREE_SIZE(ptr
) = msize
;
1677 TINY_FREE_SIZE(ptr
) = msize
;
1681 msize_t mf
= get_tiny_meta_header(ptr
, &ff
);
1682 if ((msize
!= mf
) || !ff
) {
1683 malloc_printf("setting header for tiny free %p : %u\n", ptr
, msize
);
1684 malloc_printf("reading header for tiny %p : %u %u\n", ptr
, mf
, ff
);
1689 static INLINE boolean_t
1690 tiny_meta_header_is_free(const void *ptr
)
1692 uint32_t *block_header
;
1696 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1697 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1698 index
= TINY_INDEX_FOR_PTR(ptr
);
1699 if (!BITARRAY_BIT(block_header
, index
))
1701 return !BITARRAY_BIT(in_use
, index
);
1704 static INLINE
void *
1705 tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
)
1707 // returns the previous block, assuming and verifying it's free
1708 uint32_t *block_header
;
1711 msize_t previous_msize
;
1712 msize_t previous_index
;
1715 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1716 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1717 index
= TINY_INDEX_FOR_PTR(ptr
);
1721 if ((previous_msize
= get_tiny_previous_free_msize(ptr
)) > index
)
1724 previous_index
= index
- previous_msize
;
1725 previous_ptr
= (void *)((uintptr_t)TINY_REGION_FOR_PTR(ptr
) + TINY_BYTES_FOR_MSIZE(previous_index
));
1726 if (!BITARRAY_BIT(block_header
, previous_index
))
1728 if (BITARRAY_BIT(in_use
, previous_index
))
1730 if (get_tiny_free_size(previous_ptr
) != previous_msize
)
1733 // conservative check did match true check
1734 *prev_msize
= previous_msize
;
1735 return previous_ptr
;
1739 * Adds an item to the proper free list, and also marks the meta-header of the
1741 * Assumes szone has been locked
1744 tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1746 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1747 free_list_t
*free_ptr
= ptr
;
1748 free_list_t
*free_head
= tiny_mag_ptr
->mag_free_list
[slot
];
1751 if (LOG(szone
,ptr
)) {
1752 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1754 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
1755 szone_error(szone
, 1, "tiny_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
1758 set_tiny_meta_header_free(ptr
, msize
);
1761 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
1762 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
1763 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
1765 if (! tiny_meta_header_is_free(free_head
)) {
1766 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
1767 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
1770 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
1772 BITMAPV_SET(tiny_mag_ptr
->mag_bitmap
, slot
);
1774 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
1775 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
1777 tiny_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
1781 * Removes the item pointed to by ptr in the proper free list.
1782 * Assumes szone has been locked
1785 tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1787 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1788 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
1790 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
1791 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
1794 if (LOG(szone
,ptr
)) {
1795 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1799 // The block to remove is the head of the free list
1801 if (tiny_mag_ptr
->mag_free_list
[slot
] != ptr
) {
1802 szone_error(szone
, 1, "tiny_free_list_remove_ptr: Internal invariant broken (tiny_mag_ptr->mag_free_list[slot])", ptr
,
1803 "ptr=%p slot=%d msize=%d tiny_mag_ptr->mag_free_list[slot]=%p\n",
1804 ptr
, slot
, msize
, (void *)tiny_mag_ptr
->mag_free_list
[slot
]);
1808 tiny_mag_ptr
->mag_free_list
[slot
] = next
;
1809 if (!next
) BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
1811 // We know free_ptr is already checksummed, so we don't need to do it
1813 previous
->next
= free_ptr
->next
;
1816 // We know free_ptr is already checksummed, so we don't need to do it
1818 next
->previous
= free_ptr
->previous
;
1823 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
1824 * or NULL if not found.
1826 static INLINE region_t
1827 tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
1829 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
1830 szone
->tiny_region_generation
->num_regions_allocated
,
1831 szone
->tiny_region_generation
->num_regions_allocated_shift
,
1832 TINY_REGION_FOR_PTR(ptr
));
1837 tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
) {
1838 void *last_block
, *previous_block
;
1839 uint32_t *last_header
;
1840 msize_t last_msize
, previous_msize
, last_index
;
1842 last_block
= (void *)
1843 ((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) - tiny_mag_ptr
->mag_bytes_free_at_end
);
1844 last_msize
= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
1845 last_header
= TINY_BLOCK_HEADER_FOR_PTR(last_block
);
1846 last_index
= TINY_INDEX_FOR_PTR(last_block
);
1848 // Before anything we transform any remaining mag_bytes_free_at_end into a
1849 // regular free block. We take special care here to update the bitfield
1850 // information, since we are bypassing the normal free codepath. If there
1851 // is more than one quanta worth of memory in mag_bytes_free_at_end, then
1852 // there will be two block headers:
1853 // 1) header for the free space at end, msize = 1
1854 // 2) header inserted by set_tiny_meta_header_in_use after block
1855 // We must clear the second one so that when the free block's size is
1856 // queried, we do not think the block is only 1 quantum in size because
1857 // of the second set header bit.
1858 if (last_index
!= (NUM_TINY_BLOCKS
- 1))
1859 BITARRAY_CLR(last_header
, (last_index
+ 1));
1861 // It is possible that the block prior to the last block in the region has
1862 // been free'd, but was not coalesced with the free bytes at the end of the
1863 // block, since we treat the bytes at the end of the region as "in use" in
1864 // the meta headers. Attempt to coalesce the last block with the previous
1865 // block, so we don't violate the "no consecutive free blocks" invariant.
1867 // FIXME: Need to investigate how much work would be required to increase
1868 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
1869 // than performing this workaround.
1871 previous_block
= tiny_previous_preceding_free(last_block
, &previous_msize
);
1872 if (previous_block
) {
1873 set_tiny_meta_header_middle(last_block
);
1874 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous_block
, previous_msize
);
1875 last_block
= previous_block
;
1876 last_msize
+= previous_msize
;
1879 // splice last_block into the free list
1880 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, last_block
, last_msize
);
1881 tiny_mag_ptr
->mag_bytes_free_at_end
= 0;
1882 tiny_mag_ptr
->mag_last_region
= NULL
;
1886 tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
1887 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1888 uintptr_t current
= start
;
1889 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1892 int total_alloc
= 0;
1894 while (current
< limit
) {
1895 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1896 if (is_free
&& !msize
&& (current
== start
)) {
1897 // first block is all free
1902 malloc_printf("*** tiny_free_detach_region error with %p: msize=%d is_free =%d\n",
1903 (void *)current
, msize
, is_free
);
1908 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
1912 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1918 tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
1919 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1920 uintptr_t current
= start
;
1921 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1924 size_t total_alloc
= 0;
1926 while (current
< limit
) {
1927 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1928 if (is_free
&& !msize
&& (current
== start
)) {
1929 // first block is all free
1934 malloc_printf("*** tiny_free_reattach_region error with %p: msize=%d is_free =%d\n",
1935 (void *)current
, msize
, is_free
);
1940 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
1942 total_alloc
+= TINY_BYTES_FOR_MSIZE(msize
);
1944 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1950 tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
1951 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1952 uintptr_t current
= start
;
1953 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1956 boolean_t did_advise
= FALSE
;
1958 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
1960 while (current
< limit
) {
1961 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1962 if (is_free
&& !msize
&& (current
== start
)) {
1963 // first block is all free
1965 malloc_printf("*** tiny_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
1966 (void *)current
, msize
, is_free
);
1968 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
1969 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
1972 madvise_free_range(szone
, r
, pgLo
, pgHi
);
1979 malloc_printf("*** tiny_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
1980 (void *)current
, msize
, is_free
);
1985 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
1986 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
1989 madvise_free_range(szone
, r
, pgLo
, pgHi
);
1993 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1997 /* Move the node to the tail of the Deopt's recirculation list to delay its re-use. */
1998 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(r
);
1999 recirc_list_extract(szone
, depot_ptr
, node
); // excise node from list
2000 recirc_list_splice_last(szone
, depot_ptr
, node
); // connect to magazine as last node
2005 tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
2007 #warning Tune Depot headroom
2008 if (0 < node
->bytes_used
||
2009 depot_ptr
->recirculation_entries
< (szone
->num_tiny_magazines
* 2)) {
2013 // disconnect node from Depot
2014 recirc_list_extract(szone
, depot_ptr
, node
);
2016 // Iterate the region pulling its free entries off the (locked) Depot's free list
2017 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2018 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2020 if (0 == objects_in_use
) {
2021 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
2022 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
2023 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2024 szone
->tiny_region_generation
->num_regions_allocated
,
2025 szone
->tiny_region_generation
->num_regions_allocated_shift
, sparse_region
);
2026 *pSlot
= HASHRING_REGION_DEALLOCATED
;
2027 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2028 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
2029 __sync_fetch_and_add( &(szone
->num_tiny_regions_dealloc
), 1); // Atomically increment num_tiny_regions_dealloc
2032 OSAtomicIncrement64( (volatile int64_t *)&(szone
->num_tiny_regions_dealloc
) );
2034 OSAtomicIncrement32( (volatile int32_t *)&(szone
->num_tiny_regions_dealloc
) );
2038 // Transfer ownership of the region back to the OS
2039 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
); // Avoid denial of service to Depot while in kernel
2040 deallocate_pages(szone
, sparse_region
, TINY_REGION_SIZE
, 0);
2041 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
2043 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
); // DTrace USDT Probe
2046 szone_error(szone
, 1, "tiny_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
2051 tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2053 // The entire magazine crossed the "emptiness threshold". Transfer a region
2054 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2055 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2056 region_trailer_t
*node
= tiny_mag_ptr
->firstNode
;
2058 while (node
&& !node
->recirc_suitable
) {
2064 malloc_printf("*** tiny_free_do_recirc_to_depot end of list\n");
2069 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2071 // Deal with unclaimed memory -- mag_bytes_free_at_end
2072 if (sparse_region
== tiny_mag_ptr
->mag_last_region
&& tiny_mag_ptr
->mag_bytes_free_at_end
) {
2073 tiny_finalize_region(szone
, tiny_mag_ptr
);
2076 // disconnect "suitable" node from magazine
2077 recirc_list_extract(szone
, tiny_mag_ptr
, node
);
2079 // Iterate the region pulling its free entries off its (locked) magazine's free list
2080 int objects_in_use
= tiny_free_detach_region(szone
, tiny_mag_ptr
, sparse_region
);
2081 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2083 // hand over the region to the (locked) Depot
2084 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2085 // this will cause tiny_free_list_add_ptr called by tiny_free_reattach_region to use
2086 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
2087 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
2089 // Iterate the region putting its free entries on Depot's free list
2090 size_t bytes_inplay
= tiny_free_reattach_region(szone
, depot_ptr
, sparse_region
);
2092 tiny_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2093 tiny_mag_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2094 tiny_mag_ptr
->mag_num_objects
-= objects_in_use
;
2096 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2097 depot_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2098 depot_ptr
->mag_num_objects
+= objects_in_use
;
2100 // connect to Depot as first (MRU) node
2101 recirc_list_splice_first(szone
, depot_ptr
, node
);
2103 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2105 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
2106 tiny_free_scan_madvise_free(szone
, depot_ptr
, sparse_region
);
2108 // If the region is entirely empty vm_deallocate() it
2109 tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
2111 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2115 tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2117 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2119 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2120 if (szone
->num_tiny_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
2124 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
2125 szone_error(szone
, 1, "tiny_get_region_from_depot called for magazine index -1", NULL
, NULL
);
2130 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2132 // Appropriate one of the Depot's regions. Prefer LIFO selection for best cache utilization.
2133 region_trailer_t
*node
= depot_ptr
->firstNode
;
2135 if (NULL
== node
) { // Depot empty?
2136 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2140 // disconnect first node from Depot
2141 recirc_list_extract(szone
, depot_ptr
, node
);
2143 // Iterate the region pulling its free entries off the (locked) Depot's free list
2144 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2145 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2147 // Transfer ownership of the region
2148 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = mag_index
;
2150 // Iterate the region putting its free entries on its new (locked) magazine's free list
2151 size_t bytes_inplay
= tiny_free_reattach_region(szone
, tiny_mag_ptr
, sparse_region
);
2153 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2154 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2155 depot_ptr
->mag_num_objects
-= objects_in_use
;
2157 tiny_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2158 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2159 tiny_mag_ptr
->mag_num_objects
+= objects_in_use
;
2161 // connect to magazine as first node (it's maximally sparse at this moment)
2162 recirc_list_splice_first(szone
, tiny_mag_ptr
, node
);
2164 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2166 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2168 if (-1 == madvise((void *)sparse_region
, TINY_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
2169 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
2171 szone_error(szone
, 1, "tiny_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed", sparse_region
, NULL
);
2179 #warning Tune K and f!
2180 #define K 1.5 // headroom measured in number of 1Mb regions
2181 #define DENSITY_THRESHOLD(a) \
2182 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
2185 tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
, void *ptr
,
2188 void *original_ptr
= ptr
;
2189 size_t original_size
= TINY_BYTES_FOR_MSIZE(msize
);
2190 void *next_block
= ((unsigned char *)ptr
+ original_size
);
2191 msize_t previous_msize
, next_msize
;
2193 free_list_t
*big_free_block
;
2194 free_list_t
*after_next_block
;
2195 free_list_t
*before_next_block
;
2196 boolean_t did_prepend
= FALSE
;
2197 boolean_t did_append
= FALSE
;
2200 if (LOG(szone
,ptr
)) {
2201 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2204 szone_error(szone
, 1, "trying to free tiny block that is too small", ptr
,
2205 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2209 // We try to coalesce this block with the preceeding one
2210 previous
= tiny_previous_preceding_free(ptr
, &previous_msize
);
2213 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
2214 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
2219 // clear the meta_header since this is no longer the start of a block
2220 set_tiny_meta_header_middle(ptr
);
2221 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous
, previous_msize
);
2223 msize
+= previous_msize
;
2225 // We try to coalesce with the next block
2226 if ((next_block
< TINY_REGION_END(region
)) && tiny_meta_header_is_free(next_block
)) {
2228 next_msize
= get_tiny_free_size(next_block
);
2230 if (LOG(szone
, ptr
) || LOG(szone
, next_block
)) {
2231 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
2232 ptr
, msize
, next_block
, next_msize
);
2235 // If we are coalescing with the next block, and the next block is in
2236 // the last slot of the free list, then we optimize this case here to
2237 // avoid removing next_block from the slot (NUM_TINY_SLOTS - 1) and then adding ptr back
2238 // to slot (NUM_TINY_SLOTS - 1).
2239 if (next_msize
>= NUM_TINY_SLOTS
) {
2240 msize
+= next_msize
;
2242 big_free_block
= (free_list_t
*)next_block
;
2243 after_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->next
);
2244 before_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->previous
);
2246 if (!before_next_block
) {
2247 tiny_mag_ptr
->mag_free_list
[NUM_TINY_SLOTS
-1] = ptr
;
2249 before_next_block
->next
.u
= free_list_checksum_ptr(szone
, ptr
);
2252 if (after_next_block
) {
2253 after_next_block
->previous
.u
= free_list_checksum_ptr(szone
, ptr
);
2256 // we don't need to checksum these since they are already checksummed
2257 ((free_list_t
*)ptr
)->previous
= big_free_block
->previous
;
2258 ((free_list_t
*)ptr
)->next
= big_free_block
->next
;
2260 // clear the meta_header to enable coalescing backwards
2261 set_tiny_meta_header_middle(big_free_block
);
2262 set_tiny_meta_header_free(ptr
, msize
);
2264 goto tiny_free_ending
;
2266 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2267 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2268 msize
+= next_msize
;
2271 // The tiny cache already scribbles free blocks as they go through the
2272 // cache, so we do not need to do it here.
2273 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
2274 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
2277 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, ptr
, msize
);
2279 // When in proper debug mode we write on the memory to help debug memory smashers
2281 tiny_mag_ptr
->mag_num_objects
--;
2282 // we use original_size and not msize to avoid double counting the coalesced blocks
2283 tiny_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
2285 // Update this region's bytes in use count
2286 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2287 size_t bytes_used
= node
->bytes_used
- original_size
;
2288 node
->bytes_used
= bytes_used
;
2290 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2291 if (szone
->num_tiny_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
2293 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
2294 // Emptiness discriminant
2295 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2296 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
2297 recirculation candidates list. */
2298 node
->recirc_suitable
= TRUE
;
2300 /* After this free, we've found the region is still dense, so it must have been even more so before
2301 the free. That implies the region is already correctly marked. Do nothing. */
2304 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
2305 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2306 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2307 size_t a
= tiny_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
2308 size_t u
= tiny_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
2310 if (a
- u
> ((3 * TINY_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
))
2311 tiny_free_do_recirc_to_depot(szone
, tiny_mag_ptr
, mag_index
);
2314 // Freed to Depot. N.B. Lock on tiny_magazines[DEPOT_MAGAZINE_INDEX] is already held
2315 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
2316 uintptr_t round_safe
= round_page(safe_ptr
);
2318 uintptr_t safe_extent
= (uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
2319 uintptr_t trunc_extent
= trunc_page(safe_extent
);
2321 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
2322 if (round_safe
< trunc_extent
) { // Safe area covers a page
2323 if (did_prepend
& did_append
) { // Coalesced preceding with original_ptr *and* with following
2324 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
2325 uintptr_t rnd_safe_follow
=
2326 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
2328 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
));
2329 } else if (did_prepend
) { // Coalesced preceding with original_ptr
2330 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
2332 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
);
2333 } else if (did_append
) { // Coalesced original_ptr with following
2334 uintptr_t rnd_safe_follow
=
2335 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
2337 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
));
2338 } else { // Isolated free cannot exceed 496 bytes, thus round_safe == trunc_extent, and so never get here.
2339 /* madvise_free_range(szone, region, round_safe, trunc_extent); */
2343 if (0 < bytes_used
) {
2344 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
2345 so as to avoid thrashing between the Depot's free list and a magazines's free list
2346 with detach_region/reattach_region */
2348 /* Depot'd region is just now empty. Consider return to OS. */
2349 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2350 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2351 tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
); // FIXME: depot_ptr is simply tiny_mag_ptr?
2356 // Allocates from the last region or a freshly allocated region
2358 tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2360 void *ptr
, *aligned_address
;
2362 // Deal with unclaimed memory -- mag_bytes_free_at_end
2363 if (tiny_mag_ptr
->mag_bytes_free_at_end
)
2364 tiny_finalize_region(szone
, tiny_mag_ptr
);
2366 // time to create a new region
2367 aligned_address
= allocate_pages(szone
, TINY_REGION_SIZE
, TINY_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_TINY
);
2368 if (!aligned_address
) // out of memory!
2371 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
); // DTrace USDT Probe
2373 // We set the unused bits of the header in the last pair to be all ones, and those of the inuse to zeroes.
2374 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].header
=
2375 (NUM_TINY_BLOCKS
& 31) ? (0xFFFFFFFFU
<< (NUM_TINY_BLOCKS
& 31)) : 0;
2376 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].inuse
= 0;
2378 // Here find the only place in tinyland that (infrequently) takes the tiny_regions_lock.
2379 // Only one thread at a time should be permitted to assess the density of the hash
2380 // ring and adjust if needed.
2381 // Only one thread at a time should be permitted to insert its new region on
2383 // It is safe for all other threads to read the hash ring (hashed_regions) and
2384 // the associated sizes (num_regions_allocated and num_tiny_regions).
2386 LOCK(szone
->tiny_regions_lock
);
2388 // Check to see if the hash ring of tiny regions needs to grow. Try to
2389 // avoid the hash ring becoming too dense.
2390 if (szone
->tiny_region_generation
->num_regions_allocated
< (2 * szone
->num_tiny_regions
)) {
2391 region_t
*new_regions
;
2393 size_t new_shift
= szone
->tiny_region_generation
->num_regions_allocated_shift
; // In/Out parameter
2394 new_regions
= hash_regions_grow_no_lock(szone
, szone
->tiny_region_generation
->hashed_regions
,
2395 szone
->tiny_region_generation
->num_regions_allocated
,
2398 // Do not deallocate the current hashed_regions allocation since someone may
2399 // be iterating it. Instead, just leak it.
2401 // Prepare to advance to the "next generation" of the hash ring.
2402 szone
->tiny_region_generation
->nextgen
->hashed_regions
= new_regions
;
2403 szone
->tiny_region_generation
->nextgen
->num_regions_allocated
= new_size
;
2404 szone
->tiny_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
2406 // Throw the switch to atomically advance to the next generation.
2407 szone
->tiny_region_generation
= szone
->tiny_region_generation
->nextgen
;
2408 // Ensure everyone sees the advance.
2409 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
2410 __sync_synchronize();
2415 // Tag the region at "aligned_address" as belonging to us,
2416 // and so put it under the protection of the magazine lock we are holding.
2417 // Do this before advertising "aligned_address" on the hash ring(!)
2418 MAGAZINE_INDEX_FOR_TINY_REGION(aligned_address
) = mag_index
;
2420 // Insert the new region into the hash ring, and update malloc statistics
2421 hash_region_insert_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2422 szone
->tiny_region_generation
->num_regions_allocated
,
2423 szone
->tiny_region_generation
->num_regions_allocated_shift
,
2426 szone
->num_tiny_regions
++;
2427 UNLOCK(szone
->tiny_regions_lock
);
2429 tiny_mag_ptr
->mag_last_region
= aligned_address
;
2430 BYTES_USED_FOR_TINY_REGION(aligned_address
) = TINY_BYTES_FOR_MSIZE(msize
);
2431 ptr
= aligned_address
;
2432 set_tiny_meta_header_in_use(ptr
, msize
);
2433 tiny_mag_ptr
->mag_num_objects
++;
2434 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(msize
);
2435 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2437 // We put a header on the last block so that it appears in use (for coalescing, etc...)
2438 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
)));
2439 tiny_mag_ptr
->mag_bytes_free_at_end
= TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS
- msize
);
2441 // connect to magazine as first node (it's maximally sparse at this moment)
2442 recirc_list_splice_first(szone
, tiny_mag_ptr
, REGION_TRAILER_FOR_TINY_REGION(aligned_address
));
2445 if (LOG(szone
,ptr
)) {
2446 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2452 static INLINE boolean_t
2453 tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
2455 // returns 1 on success
2458 unsigned next_index
;
2461 msize_t next_msize
, coalesced_msize
, leftover_msize
;
2464 index
= TINY_INDEX_FOR_PTR(ptr
);
2465 old_msize
= TINY_MSIZE_FOR_BYTES(old_size
);
2466 next_index
= index
+ old_msize
;
2468 if (next_index
>= NUM_TINY_BLOCKS
) {
2471 next_block
= (char *)ptr
+ old_size
;
2473 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2474 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2475 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2478 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2481 is_free
= tiny_meta_header_is_free(next_block
);
2483 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2484 return 0; // next_block is in use;
2486 next_msize
= get_tiny_free_size(next_block
);
2487 if (old_size
+ TINY_BYTES_FOR_MSIZE(next_msize
) < new_size
) {
2488 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2489 return 0; // even with next block, not enough
2492 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2495 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2496 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2497 coalesced_msize
= TINY_MSIZE_FOR_BYTES(new_size
- old_size
+ TINY_QUANTUM
- 1);
2498 leftover_msize
= next_msize
- coalesced_msize
;
2499 if (leftover_msize
) {
2500 /* there's some left, so put the remainder back */
2501 leftover
= (void *)((uintptr_t)next_block
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
));
2503 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover
, leftover_msize
);
2505 set_tiny_meta_header_in_use(ptr
, old_msize
+ coalesced_msize
);
2507 if (LOG(szone
,ptr
)) {
2508 malloc_printf("in tiny_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, old_msize
+ coalesced_msize
);
2511 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2513 // Update this region's bytes in use count
2514 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2515 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2516 node
->bytes_used
= bytes_used
;
2518 // Emptiness discriminant
2519 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2520 /* After this reallocation the region is still sparse, so it must have been even more so before
2521 the reallocation. That implies the region is already correctly marked. Do nothing. */
2523 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2524 recirculation candidates list. */
2525 node
->recirc_suitable
= FALSE
;
2528 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2529 CHECK(szone
, __PRETTY_FUNCTION__
);
2534 tiny_check_region(szone_t
*szone
, region_t region
)
2536 uintptr_t start
, ptr
, region_end
;
2537 boolean_t prev_free
= 0;
2540 free_list_t
*free_head
;
2541 void *follower
, *previous
, *next
;
2542 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
2543 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2546 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2548 /* establish region limits */
2549 start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
2551 region_end
= (uintptr_t)TINY_REGION_END(region
);
2554 * The last region may have a trailing chunk which has not been converted into inuse/freelist
2557 if (region
== tiny_mag_ptr
->mag_last_region
)
2558 region_end
-= tiny_mag_ptr
->mag_bytes_free_at_end
;
2561 * Scan blocks within the region.
2563 while (ptr
< region_end
) {
2565 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
2568 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2569 if (is_free
&& !msize
&& (ptr
== start
)) {
2574 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
2578 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
2585 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
2588 if (msize
> (NUM_TINY_SLOTS
- 1)) {
2589 malloc_printf("*** invariant broken for %p this tiny msize=%d - size is too large\n",
2593 /* move to next block */
2594 ptr
+= TINY_BYTES_FOR_MSIZE(msize
);
2597 * Free blocks must have been coalesced, we cannot have a free block following another
2601 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
2607 * Check the integrity of this block's entry in its freelist.
2609 free_head
= (free_list_t
*)ptr
;
2610 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
2611 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
2612 if (previous
&& !tiny_meta_header_is_free(previous
)) {
2613 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2617 if (next
&& !tiny_meta_header_is_free(next
)) {
2618 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
2623 * Check the free block's trailing size value.
2625 follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
2626 if (((uintptr_t)follower
!= region_end
) && (get_tiny_previous_free_msize(follower
) != msize
)) {
2627 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
2628 "(end marker incorrect) should be %d; in fact %d\n",
2629 ptr
, follower
, TINY_REGION_ADDRESS(region
), region_end
, msize
, get_tiny_previous_free_msize(follower
));
2632 /* move to next block */
2633 ptr
= (uintptr_t)follower
;
2637 * Ensure that we scanned the entire region
2639 if (ptr
!= region_end
) {
2640 malloc_printf("*** invariant broken for region end %p - %p\n", ptr
, region_end
);
2644 * Check the trailing block's integrity.
2646 if (region
== tiny_mag_ptr
->mag_last_region
) {
2647 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2648 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2649 if (is_free
|| (msize
!= 1)) {
2650 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr
, msize
, is_free
);
2657 static kern_return_t
2658 tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
2659 memory_reader_t reader
, vm_range_recorder_t recorder
)
2664 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
2669 vm_range_t admin_range
;
2670 vm_range_t ptr_range
;
2671 unsigned char *mapped_region
;
2672 uint32_t *block_header
;
2674 unsigned block_index
;
2675 unsigned block_limit
;
2680 vm_address_t mag_last_free_ptr
= 0;
2681 msize_t mag_last_free_msize
= 0;
2683 region_hash_generation_t
*trg_ptr
;
2684 err
= reader(task
, (vm_address_t
)szone
->tiny_region_generation
, sizeof(region_hash_generation_t
), (void **)&trg_ptr
);
2685 if (err
) return err
;
2687 num_regions
= trg_ptr
->num_regions_allocated
;
2688 err
= reader(task
, (vm_address_t
)trg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
2689 if (err
) return err
;
2691 for (index
= 0; index
< num_regions
; ++index
) {
2692 region
= regions
[index
];
2693 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
2694 range
.address
= (vm_address_t
)TINY_REGION_ADDRESS(region
);
2695 range
.size
= (vm_size_t
)TINY_REGION_SIZE
;
2696 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
2697 admin_range
.address
= range
.address
+ TINY_METADATA_START
;
2698 admin_range
.size
= TINY_METADATA_SIZE
;
2699 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
2701 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
2702 ptr_range
.address
= range
.address
;
2703 ptr_range
.size
= NUM_TINY_BLOCKS
* TINY_QUANTUM
;
2704 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
2706 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
2707 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
2711 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(mapped_region
);
2712 magazine_t
*tiny_mag_ptr
;
2713 err
= reader(task
, (vm_address_t
)&(szone
->tiny_magazines
[mag_index
]), sizeof(magazine_t
),
2714 (void **)&tiny_mag_ptr
);
2715 if (err
) return err
;
2717 void *mag_last_free
= tiny_mag_ptr
->mag_last_free
;
2718 if (mag_last_free
) {
2719 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(TINY_QUANTUM
- 1);
2720 mag_last_free_msize
= (uintptr_t) mag_last_free
& (TINY_QUANTUM
- 1);
2723 block_header
= (uint32_t *)(mapped_region
+ TINY_METADATA_START
+ sizeof(region_trailer_t
));
2724 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
2726 block_limit
= NUM_TINY_BLOCKS
;
2727 if (region
== tiny_mag_ptr
->mag_last_region
)
2728 block_limit
-= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
2730 while (block_index
< block_limit
) {
2731 vm_size_t block_offset
= TINY_BYTES_FOR_MSIZE(block_index
);
2732 is_free
= !BITARRAY_BIT(in_use
, block_index
);
2734 mapped_ptr
= mapped_region
+ block_offset
;
2736 // mapped_region, the address at which 'range' in 'task' has been
2737 // mapped into our process, is not necessarily aligned to
2738 // TINY_BLOCKS_ALIGN.
2740 // Since the code in get_tiny_free_size() assumes the pointer came
2741 // from a properly aligned tiny region, and mapped_region is not
2742 // necessarily aligned, then do the size calculation directly.
2743 // If the next bit is set in the header bitmap, then the size is one
2744 // quantum. Otherwise, read the size field.
2745 if (!BITARRAY_BIT(block_header
, (block_index
+1)))
2746 msize
= TINY_FREE_SIZE(mapped_ptr
);
2752 } else if (range
.address
+ block_offset
!= mag_last_free_ptr
) {
2754 bit
= block_index
+ 1;
2755 while (! BITARRAY_BIT(block_header
, bit
)) {
2759 buffer
[count
].address
= range
.address
+ block_offset
;
2760 buffer
[count
].size
= TINY_BYTES_FOR_MSIZE(msize
);
2762 if (count
>= MAX_RECORDER_BUFFER
) {
2763 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2767 // Block is not free but it matches mag_last_free_ptr so even
2768 // though it is not marked free in the bitmap, we treat it as if
2769 // it is and move on
2770 msize
= mag_last_free_msize
;
2772 block_index
+= msize
;
2775 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2785 tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2789 grain_t slot
= msize
- 1;
2790 free_list_t
**free_list
= tiny_mag_ptr
->mag_free_list
;
2791 free_list_t
**the_slot
= free_list
+ slot
;
2793 free_list_t
**limit
;
2794 #if defined(__LP64__)
2799 msize_t leftover_msize
;
2800 free_list_t
*leftover_ptr
;
2802 // Assumes we've locked the region
2803 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2805 // Look for an exact match by checking the freelist for this msize.
2809 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2811 next
->previous
= ptr
->previous
;
2813 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
2818 if (LOG(szone
, ptr
)) {
2819 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2822 goto return_tiny_alloc
;
2825 // Mask off the bits representing slots holding free blocks smaller than the
2826 // size we need. If there are no larger free blocks, try allocating from
2827 // the free space at the end of the tiny region.
2828 #if defined(__LP64__)
2829 bitmap
= ((uint64_t *)(tiny_mag_ptr
->mag_bitmap
))[0] & ~ ((1ULL << slot
) - 1);
2831 bitmap
= tiny_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
2834 goto try_tiny_malloc_from_end
;
2836 slot
= BITMAPV_CTZ(bitmap
);
2837 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
2840 if (free_list
< limit
) {
2843 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2846 next
->previous
= ptr
->previous
;
2848 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
2850 this_msize
= get_tiny_free_size(ptr
);
2851 goto add_leftover_and_proceed
;
2854 malloc_printf("in tiny_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
2858 // We are now looking at the last slot, which contains blocks equal to, or
2859 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
2860 // If the last freelist is not empty, and the head contains a block that is
2861 // larger than our request, then the remainder is put back on the free list.
2864 this_msize
= get_tiny_free_size(ptr
);
2865 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2866 if (this_msize
- msize
>= NUM_TINY_SLOTS
) {
2867 // the leftover will go back to the free list, so we optimize by
2868 // modifying the free list rather than a pop and push of the head
2869 leftover_msize
= this_msize
- msize
;
2870 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2871 *limit
= leftover_ptr
;
2873 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
2875 leftover_ptr
->previous
= ptr
->previous
;
2876 leftover_ptr
->next
= ptr
->next
;
2877 set_tiny_meta_header_free(leftover_ptr
, leftover_msize
);
2879 if (LOG(szone
,ptr
)) {
2880 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n",
2881 ptr
, msize
, this_msize
);
2885 goto return_tiny_alloc
;
2888 next
->previous
= ptr
->previous
;
2891 goto add_leftover_and_proceed
;
2895 try_tiny_malloc_from_end
:
2896 // Let's see if we can use tiny_mag_ptr->mag_bytes_free_at_end
2897 if (tiny_mag_ptr
->mag_bytes_free_at_end
>= TINY_BYTES_FOR_MSIZE(msize
)) {
2898 ptr
= (free_list_t
*)((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) -
2899 tiny_mag_ptr
->mag_bytes_free_at_end
);
2900 tiny_mag_ptr
->mag_bytes_free_at_end
-= TINY_BYTES_FOR_MSIZE(msize
);
2901 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2902 // let's add an in use block after ptr to serve as boundary
2903 set_tiny_meta_header_in_use_1((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2907 if (LOG(szone
, ptr
)) {
2908 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr
, msize
);
2911 goto return_tiny_alloc
;
2915 add_leftover_and_proceed
:
2916 if (!this_msize
|| (this_msize
> msize
)) {
2917 leftover_msize
= this_msize
- msize
;
2918 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2920 if (LOG(szone
,ptr
)) {
2921 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2924 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover_ptr
, leftover_msize
);
2929 tiny_mag_ptr
->mag_num_objects
++;
2930 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(this_msize
);
2932 // Update this region's bytes in use count
2933 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2934 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(this_msize
);
2935 node
->bytes_used
= bytes_used
;
2937 // Emptiness discriminant
2938 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2939 /* After this allocation the region is still sparse, so it must have been even more so before
2940 the allocation. That implies the region is already correctly marked. Do nothing. */
2942 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2943 recirculation candidates list. */
2944 node
->recirc_suitable
= FALSE
;
2947 if (LOG(szone
,ptr
)) {
2948 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
2952 set_tiny_meta_header_in_use(ptr
, this_msize
);
2954 set_tiny_meta_header_in_use_1(ptr
);
2957 #undef DENSITY_THRESHOLD
2960 static INLINE
void *
2961 tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
2964 mag_index_t mag_index
= mag_get_thread_index(szone
);
2965 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2968 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
2969 szone_error(szone
, 1, "malloc called for magazine index -1", NULL
, NULL
);
2974 szone_error(szone
, 1, "invariant broken (!msize) in allocation (region)", NULL
, NULL
);
2979 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
2982 ptr
= tiny_mag_ptr
->mag_last_free
;
2984 if ((((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) == msize
) {
2986 tiny_mag_ptr
->mag_last_free
= NULL
;
2987 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2988 CHECK(szone
, __PRETTY_FUNCTION__
);
2989 ptr
= (void *)((uintptr_t)ptr
& ~ (TINY_QUANTUM
- 1));
2990 if (cleared_requested
) {
2991 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
2994 if (LOG(szone
,ptr
)) {
2995 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr
, msize
);
3000 #endif /* TINY_CACHE */
3002 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3004 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3005 CHECK(szone
, __PRETTY_FUNCTION__
);
3006 if (cleared_requested
) {
3007 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3012 if (tiny_get_region_from_depot(szone
, tiny_mag_ptr
, mag_index
)) {
3013 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3015 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3016 CHECK(szone
, __PRETTY_FUNCTION__
);
3017 if (cleared_requested
) {
3018 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3024 ptr
= tiny_malloc_from_region_no_lock(szone
, tiny_mag_ptr
, mag_index
, msize
);
3025 // we don't clear because this freshly allocated space is pristine
3026 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3027 CHECK(szone
, __PRETTY_FUNCTION__
);
3031 static NOINLINE
void
3032 free_tiny_botch(szone_t
*szone
, free_list_t
*ptr
)
3034 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3035 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3036 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3037 szone_error(szone
, 1, "double free", ptr
, NULL
);
3041 free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
)
3045 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
3046 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3048 // ptr is known to be in tiny_region
3050 msize
= TINY_MSIZE_FOR_BYTES(known_size
+ TINY_QUANTUM
- 1);
3052 msize
= get_tiny_meta_header(ptr
, &is_free
);
3054 free_tiny_botch(szone
, ptr
);
3060 malloc_printf("*** free_tiny() block in use is too large: %p\n", ptr
);
3065 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3068 // Depot does not participate in TINY_CACHE since it can't be directly malloc()'d
3069 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3070 if (msize
< TINY_QUANTUM
) { // to see if the bits fit in the last 4 bits
3071 void *ptr2
= tiny_mag_ptr
->mag_last_free
; // Might be NULL
3072 region_t rgn2
= tiny_mag_ptr
->mag_last_free_rgn
;
3074 /* check that we don't already have this pointer in the cache */
3075 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (TINY_QUANTUM
- 1))) {
3076 free_tiny_botch(szone
, ptr
);
3080 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
3081 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
3083 tiny_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
3084 tiny_mag_ptr
->mag_last_free_rgn
= tiny_region
;
3087 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3088 CHECK(szone
, __PRETTY_FUNCTION__
);
3092 msize
= (uintptr_t)ptr2
& (TINY_QUANTUM
- 1);
3093 ptr
= (void *)(((uintptr_t)ptr2
) & ~(TINY_QUANTUM
- 1));
3097 #endif /* TINY_CACHE */
3099 // Now in the time it took to acquire the lock, the region may have migrated
3100 // from one magazine to another. I.e. trailer->mag_index is volatile.
3101 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
3102 // is stale. If so, keep on tryin' ...
3103 region_trailer_t
*trailer
= REGION_TRAILER_FOR_TINY_REGION(tiny_region
);
3104 mag_index_t refreshed_index
;
3106 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
3108 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3110 mag_index
= refreshed_index
;
3111 tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3112 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3115 tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
);
3116 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3117 CHECK(szone
, __PRETTY_FUNCTION__
);
3121 print_tiny_free_list(szone_t
*szone
)
3124 _SIMPLE_STRING b
= _simple_salloc();
3125 mag_index_t mag_index
;
3128 _simple_sappend(b
, "tiny free sizes:\n");
3129 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3131 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
3132 while (slot
< NUM_TINY_SLOTS
) {
3133 ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3135 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== NUM_TINY_SLOTS
-1) ? ">=" : "",
3136 (slot
+1)*TINY_QUANTUM
, free_list_count(szone
, ptr
));
3140 _simple_sappend(b
,"\n");
3142 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3148 print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
)
3150 unsigned counts
[1024];
3151 unsigned in_use
= 0;
3152 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
3153 uintptr_t current
= start
;
3154 uintptr_t limit
= (uintptr_t)TINY_REGION_END(region
) - bytes_at_end
;
3159 uintptr_t pgTot
= 0;
3161 if (region
== HASHRING_REGION_DEALLOCATED
) {
3162 if ((b
= _simple_salloc()) != NULL
) {
3163 _simple_sprintf(b
, "Tiny region [unknown address] was returned to the OS\n");
3164 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3170 memset(counts
, 0, sizeof(counts
));
3171 while (current
< limit
) {
3172 msize
= get_tiny_meta_header((void *)current
, &is_free
);
3173 if (is_free
& !msize
&& (current
== start
)) {
3174 // first block is all free
3175 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3176 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
3179 pgTot
+= (pgHi
- pgLo
);
3184 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
3189 if (msize
> NUM_TINY_SLOTS
)
3190 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current
, msize
);
3195 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3196 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3199 pgTot
+= (pgHi
- pgLo
);
3202 current
+= TINY_BYTES_FOR_MSIZE(msize
);
3204 if ((b
= _simple_salloc()) != NULL
) {
3205 _simple_sprintf(b
, "Tiny region [%p-%p, %y] \t", (void *)start
, TINY_REGION_END(region
), (int)TINY_REGION_SIZE
);
3206 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_TINY_REGION(region
));
3207 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_TINY_REGION(region
));
3209 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
);
3210 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_TINY_REGION(region
)) {
3211 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
3213 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
3215 if (verbose
&& in_use
) {
3216 _simple_sappend(b
, "\n\tSizes in use: ");
3217 for (ci
= 0; ci
< 1024; ci
++)
3219 _simple_sprintf(b
, "%d[%d] ", TINY_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
3221 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3227 tiny_free_list_check(szone_t
*szone
, grain_t slot
)
3229 mag_index_t mag_index
;
3231 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3232 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3233 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3236 free_list_t
*ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3238 free_list_t
*previous
= NULL
;
3241 is_free
= tiny_meta_header_is_free(ptr
);
3243 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3244 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3247 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
3248 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3249 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3252 if (!tiny_region_for_ptr_no_lock(szone
, ptr
)) {
3253 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3254 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3257 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
3258 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3259 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3263 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3267 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3272 /********************* SMALL FREE LIST UTILITIES ************************/
3275 * Mark a block as free. Only the first quantum of a block is marked thusly,
3276 * the remainder are marked "middle".
3279 small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
)
3281 meta_headers
[index
] = msize
| SMALL_IS_FREE
;
3285 * Mark a block as in use. Only the first quantum of a block is marked thusly,
3286 * the remainder are marked "middle".
3289 small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
)
3291 meta_headers
[index
] = msize
;
3295 * Mark a quantum as being the second or later in a block.
3298 small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
)
3300 meta_headers
[index
] = 0;
3304 * Adds an item to the proper free list, and also marks the meta-header of the
3306 * Assumes szone has been locked
3309 small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3311 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3312 free_list_t
*free_ptr
= ptr
;
3313 free_list_t
*free_head
= small_mag_ptr
->mag_free_list
[slot
];
3317 if (LOG(szone
,ptr
)) {
3318 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3320 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
3321 szone_error(szone
, 1, "small_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
3324 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), msize
);
3328 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
3329 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
3330 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
3332 if (!SMALL_PTR_IS_FREE(free_head
)) {
3333 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
3334 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
3337 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
3339 BITMAPN_SET(small_mag_ptr
->mag_bitmap
, slot
);
3341 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
3342 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
3344 small_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
3346 // Store msize at the end of the block denoted by "ptr" (i.e. at a negative offset from "follower")
3347 follower
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
3348 SMALL_PREVIOUS_MSIZE(follower
) = msize
;
3352 * Removes the item pointed to by ptr in the proper free list.
3353 * Assumes szone has been locked
3356 small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3358 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3359 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
3361 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
3362 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
3365 if (LOG(szone
,ptr
)) {
3366 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3371 // The block to remove is the head of the free list
3373 if (small_mag_ptr
->mag_free_list
[slot
] != ptr
) {
3374 szone_error(szone
, 1, "small_free_list_remove_ptr: Internal invariant broken (small_mag_ptr->mag_free_list[slot])", ptr
,
3375 "ptr=%p slot=%d msize=%d small_mag_ptr->mag_free_list[slot]=%p\n",
3376 ptr
, slot
, msize
, (void *)small_mag_ptr
->mag_free_list
[slot
]);
3380 small_mag_ptr
->mag_free_list
[slot
] = next
;
3381 if (!next
) BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
3383 // We know free_ptr is already checksummed, so we don't need to do it
3385 previous
->next
= free_ptr
->next
;
3388 // We know free_ptr is already checksummed, so we don't need to do it
3390 next
->previous
= free_ptr
->previous
;
3395 * small_region_for_ptr_no_lock - Returns the small region containing the pointer,
3396 * or NULL if not found.
3398 static INLINE region_t
3399 small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
3401 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3402 szone
->small_region_generation
->num_regions_allocated
,
3403 szone
->small_region_generation
->num_regions_allocated_shift
,
3404 SMALL_REGION_FOR_PTR(ptr
));
3409 small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
) {
3410 void *last_block
, *previous_block
;
3411 msize_t last_msize
, previous_msize
, last_index
;
3413 last_block
= SMALL_REGION_END(small_mag_ptr
->mag_last_region
) - small_mag_ptr
->mag_bytes_free_at_end
;
3414 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
3416 // It is possible that the block prior to the last block in the region has
3417 // been free'd, but was not coalesced with the free bytes at the end of the
3418 // block, since we treat the bytes at the end of the region as "in use" in
3419 // the meta headers. Attempt to coalesce the last block with the previous
3420 // block, so we don't violate the "no consecutive free blocks" invariant.
3422 // FIXME: If we could calculate the previous small free size in the same
3423 // manner as tiny_previous_preceding_free, it would eliminate the
3424 // index & previous msize checks, which are a guard against reading
3425 // bogus data out of in-use or written-on-freed memory.
3427 // FIXME: Need to investigate how much work would be required to increase
3428 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
3429 // than performing this workaround.
3431 last_index
= SMALL_META_INDEX_FOR_PTR(last_block
);
3432 previous_msize
= SMALL_PREVIOUS_MSIZE(last_block
);
3434 if (last_index
&& (previous_msize
<= last_index
)) {
3435 previous_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3436 if (*SMALL_METADATA_FOR_PTR(previous_block
) == (previous_msize
| SMALL_IS_FREE
)) {
3437 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(last_block
);
3439 small_meta_header_set_middle(meta_headers
, last_index
);
3440 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous_block
, previous_msize
);
3441 last_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3442 last_msize
+= previous_msize
;
3446 // splice last_block into the free list
3447 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3448 small_mag_ptr
->mag_bytes_free_at_end
= 0;
3449 small_mag_ptr
->mag_last_region
= NULL
;
3453 small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3454 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3455 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3456 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3457 uintptr_t current
= start
;
3458 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3459 int total_alloc
= 0;
3461 while (current
< limit
) {
3462 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3463 msize_t msize_and_free
= meta_headers
[index
];
3464 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3465 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3469 malloc_printf("*** small_free_detach_region error with %p: msize=%d is_free =%d\n",
3470 (void *)current
, msize
, is_free
);
3475 small_free_list_remove_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3479 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3485 small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3486 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3487 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3488 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3489 uintptr_t current
= start
;
3490 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3491 size_t total_alloc
= 0;
3493 while (current
< limit
) {
3494 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3495 msize_t msize_and_free
= meta_headers
[index
];
3496 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3497 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3501 malloc_printf("*** small_free_reattach_region error with %p: msize=%d is_free =%d\n",
3502 (void *)current
, msize
, is_free
);
3507 small_free_list_add_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3509 total_alloc
+= SMALL_BYTES_FOR_MSIZE(msize
);
3511 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3517 small_free_scan_depot_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
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 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(start
);
3522 boolean_t did_advise
= FALSE
;
3524 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
3526 while (current
< limit
) {
3527 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3528 msize_t msize_and_free
= meta_headers
[index
];
3529 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3530 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3532 if (is_free
&& !msize
&& (current
== start
)) {
3534 // first block is all free
3535 malloc_printf("*** small_free_scan_depot_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
3536 (void *)current
, msize
, is_free
);
3538 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3539 uintptr_t pgHi
= trunc_page(start
+ SMALL_REGION_SIZE
- sizeof(msize_t
));
3542 madvise_free_range(szone
, r
, pgLo
, pgHi
);
3549 malloc_printf("*** small_free_scan_depot_madvise_free error with %p: msize=%d is_free =%d\n",
3550 (void *)current
, msize
, is_free
);
3555 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3556 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3559 madvise_free_range(szone
, r
, pgLo
, pgHi
);
3563 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3567 /* Move the node to the tail of the Deopt's recirculation list to delay its re-use. */
3568 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(r
);
3569 recirc_list_extract(szone
, depot_ptr
, node
); // excise node from list
3570 recirc_list_splice_last(szone
, depot_ptr
, node
); // connect to magazine as last node
3575 small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
3577 #warning Tune Depot headroom
3578 if (0 < node
->bytes_used
||
3579 depot_ptr
->recirculation_entries
< (szone
->num_small_magazines
* 2)) {
3583 // disconnect first node from Depot
3584 recirc_list_extract(szone
, depot_ptr
, node
);
3586 // Iterate the region pulling its free entries off the (locked) Depot's free list
3587 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3588 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
3590 if (0 == objects_in_use
) {
3591 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
3592 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
3593 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3594 szone
->small_region_generation
->num_regions_allocated
,
3595 szone
->small_region_generation
->num_regions_allocated_shift
, sparse_region
);
3596 *pSlot
= HASHRING_REGION_DEALLOCATED
;
3597 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3598 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
3599 __sync_fetch_and_add( &(szone
->num_small_regions_dealloc
), 1); // Atomically increment num_small_regions_dealloc
3602 OSAtomicIncrement64( (volatile int64_t *)&(szone
->num_small_regions_dealloc
) );
3604 OSAtomicIncrement32( (volatile int32_t *)&(szone
->num_small_regions_dealloc
) );
3608 // Transfer ownership of the region back to the OS
3609 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
); // Avoid denial of service to Depot while in kernel
3610 deallocate_pages(szone
, sparse_region
, SMALL_REGION_SIZE
, 0);
3611 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
3613 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
); // DTrace USDT Probe
3616 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
3621 small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
3623 // The entire magazine crossed the "emptiness threshold". Transfer a region
3624 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
3625 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
3626 region_trailer_t
*node
= small_mag_ptr
->firstNode
;
3628 while (node
&& !node
->recirc_suitable
) {
3634 malloc_printf("*** small_free_do_recirc_to_depot end of list\n");
3639 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3641 // Deal with unclaimed memory -- mag_bytes_free_at_end
3642 if (sparse_region
== small_mag_ptr
->mag_last_region
&& small_mag_ptr
->mag_bytes_free_at_end
) {
3643 small_finalize_region(szone
, small_mag_ptr
);
3646 // disconnect first node from magazine
3647 recirc_list_extract(szone
, small_mag_ptr
, node
);
3649 // Iterate the region pulling its free entries off its (locked) magazine's free list
3650 int objects_in_use
= small_free_detach_region(szone
, small_mag_ptr
, sparse_region
);
3651 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3653 // hand over the region to the (locked) Depot
3654 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
3655 // this will cause small_free_list_add_ptr called by small_free_reattach_region to use
3656 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
3657 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
3659 // Iterate the region putting its free entries on Depot's free list
3660 size_t bytes_inplay
= small_free_reattach_region(szone
, depot_ptr
, sparse_region
);
3662 small_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
3663 small_mag_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3664 small_mag_ptr
->mag_num_objects
-= objects_in_use
;
3666 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
3667 depot_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3668 depot_ptr
->mag_num_objects
+= objects_in_use
;
3670 // connect to Depot as first node
3671 recirc_list_splice_first(szone
, depot_ptr
, node
);
3673 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
3675 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
3676 small_free_scan_depot_madvise_free(szone
, depot_ptr
, sparse_region
);
3678 // If the region is entirely empty vm_deallocate() it
3679 small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
3681 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3685 small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
3687 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3689 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
3690 if (szone
->num_small_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
3694 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
3695 szone_error(szone
, 1, "small_get_region_from_depot called for magazine index -1", NULL
, NULL
);
3700 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
3702 // Appropriate one of the Depot's regions. Prefer LIFO selection for best cache utilization.
3703 region_trailer_t
*node
= depot_ptr
->firstNode
;
3705 if (NULL
== node
) { // Depot empty?
3706 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3710 // disconnect first node from Depot
3711 recirc_list_extract(szone
, depot_ptr
, node
);
3713 // Iterate the region pulling its free entries off the (locked) Depot's free list
3714 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3715 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
3717 // Transfer ownership of the region
3718 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = mag_index
;
3720 // Iterate the region putting its free entries on its new (locked) magazine's free list
3721 size_t bytes_inplay
= small_free_reattach_region(szone
, small_mag_ptr
, sparse_region
);
3723 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
3724 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3725 depot_ptr
->mag_num_objects
-= objects_in_use
;
3727 small_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
3728 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3729 small_mag_ptr
->mag_num_objects
+= objects_in_use
;
3731 // connect to magazine as first node (it's maximally sparse at this moment)
3732 recirc_list_splice_first(szone
, small_mag_ptr
, node
);
3734 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3736 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
3738 if (-1 == madvise((void *)sparse_region
, SMALL_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
3739 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
3741 szone_error(szone
, 1, "small_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed", sparse_region
, NULL
);
3749 #warning Tune K and f!
3750 #define K 1.5 // headroom measured in number of 8Mb regions
3751 #define DENSITY_THRESHOLD(a) \
3752 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
3755 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
)
3757 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3758 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
3759 void *original_ptr
= ptr
;
3760 size_t original_size
= SMALL_BYTES_FOR_MSIZE(msize
);
3761 unsigned char *next_block
= ((unsigned char *)ptr
+ original_size
);
3762 msize_t next_index
= index
+ msize
;
3763 msize_t previous_msize
, next_msize
;
3765 boolean_t did_prepend
= FALSE
;
3766 boolean_t did_append
= FALSE
;
3769 if (LOG(szone
,ptr
)) {
3770 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
3773 szone_error(szone
, 1, "trying to free small block that is too small", ptr
,
3774 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
3778 // We try to coalesce this block with the preceeding one
3779 if (index
&& (SMALL_PREVIOUS_MSIZE(ptr
) <= index
)) {
3780 previous_msize
= SMALL_PREVIOUS_MSIZE(ptr
);
3781 if (meta_headers
[index
- previous_msize
] == (previous_msize
| SMALL_IS_FREE
)) {
3782 previous
= (void *)((uintptr_t)ptr
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3783 // previous is really to be coalesced
3786 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
3787 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
3790 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous
, previous_msize
);
3791 small_meta_header_set_middle(meta_headers
, index
);
3793 msize
+= previous_msize
;
3794 index
-= previous_msize
;
3797 // We try to coalesce with the next block
3798 if ((next_block
< SMALL_REGION_END(region
)) && (meta_headers
[next_index
] & SMALL_IS_FREE
)) {
3799 // next block is free, we coalesce
3801 next_msize
= meta_headers
[next_index
] & ~ SMALL_IS_FREE
;
3804 malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n",
3805 ptr
, msize
, next_block
, next_msize
);
3807 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
3808 small_meta_header_set_middle(meta_headers
, next_index
);
3809 msize
+= next_msize
;
3811 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) {
3813 szone_error(szone
, 1, "incorrect size information - block header was damaged", ptr
, NULL
);
3815 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
3818 small_free_list_add_ptr(szone
, small_mag_ptr
, ptr
, msize
);
3819 small_mag_ptr
->mag_num_objects
--;
3820 // we use original_size and not msize to avoid double counting the coalesced blocks
3821 small_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
3823 // Update this region's bytes in use count
3824 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
3825 size_t bytes_used
= node
->bytes_used
- original_size
;
3826 node
->bytes_used
= bytes_used
;
3828 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
3829 if (szone
->num_small_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
3831 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3832 // Emptiness discriminant
3833 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
3834 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
3835 recirculation candidates list. */
3836 node
->recirc_suitable
= TRUE
;
3838 /* After this free, we've found the region is still dense, so it must have been even more so before
3839 the free. That implies the region is already correctly marked. Do nothing. */
3842 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
3843 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
3844 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
3846 size_t a
= small_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
3847 size_t u
= small_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
3849 if (a
- u
> ((3 * SMALL_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
))
3850 small_free_do_recirc_to_depot(szone
, small_mag_ptr
, mag_index
);
3853 // Freed to Depot. N.B. Lock on small_magazines[DEPOT_MAGAZINE_INDEX] is already held
3854 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
3855 uintptr_t round_safe
= round_page(safe_ptr
);
3857 uintptr_t safe_extent
= (uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
3858 uintptr_t trunc_extent
= trunc_page(safe_extent
);
3860 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
3861 if (round_safe
< trunc_extent
) { // Safe area covers a page (perhaps many)
3862 if (did_prepend
& did_append
) { // Coalesced preceding with original_ptr *and* with following
3863 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
3864 uintptr_t rnd_safe_follow
=
3865 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
3867 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
));
3868 } else if (did_prepend
) { // Coalesced preceding with original_ptr
3869 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
3871 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
);
3872 } else if (did_append
) { // Coalesced original_ptr with following
3873 uintptr_t rnd_safe_follow
=
3874 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
3876 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
));
3877 } else // Isolated free
3878 madvise_free_range(szone
, region
, round_safe
, trunc_extent
);
3881 if (0 < bytes_used
) {
3882 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
3883 so as to avoid thrashing between the Depot's free list and a magazines's free list
3884 with detach_region/reattach_region */
3886 /* Depot'd region is just now empty. Consider return to OS. */
3887 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
3888 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3889 small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
3894 // Allocates from the last region or a freshly allocated region
3896 small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
3898 void *ptr
, *aligned_address
;
3900 // Before anything we transform the mag_bytes_free_at_end - if any - to a regular free block
3901 /* FIXME: last_block needs to be coalesced with previous entry if free, <rdar://5462322> */
3902 if (small_mag_ptr
->mag_bytes_free_at_end
)
3903 small_finalize_region(szone
, small_mag_ptr
);
3905 // time to create a new region
3906 aligned_address
= allocate_pages(szone
, SMALL_REGION_SIZE
, SMALL_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_SMALL
);
3907 if (!aligned_address
)
3910 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
); // DTrace USDT Probe
3912 // Here find the only place in smallville that (infrequently) takes the small_regions_lock.
3913 // Only one thread at a time should be permitted to assess the density of the hash
3914 // ring and adjust if needed.
3915 // Only one thread at a time should be permitted to insert its new region on
3917 // It is safe for all other threads to read the hash ring (hashed_regions) and
3918 // the associated sizes (num_regions_allocated and num_small_regions).
3920 LOCK(szone
->small_regions_lock
);
3921 // Check to see if the hash ring of small regions needs to grow. Try to
3922 // avoid the hash ring becoming too dense.
3923 if (szone
->small_region_generation
->num_regions_allocated
< (2 * szone
->num_small_regions
)) {
3924 region_t
*new_regions
;
3926 size_t new_shift
= szone
->small_region_generation
->num_regions_allocated_shift
; // In/Out parameter
3927 new_regions
= hash_regions_grow_no_lock(szone
, szone
->small_region_generation
->hashed_regions
,
3928 szone
->small_region_generation
->num_regions_allocated
,
3931 // Do not deallocate the current hashed_regions allocation since someone
3932 // may be iterating it. Instead, just leak it.
3934 // Prepare to advance to the "next generation" of the hash ring.
3935 szone
->small_region_generation
->nextgen
->hashed_regions
= new_regions
;
3936 szone
->small_region_generation
->nextgen
->num_regions_allocated
= new_size
;
3937 szone
->small_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
3939 // Throw the switch to atomically advance to the next generation.
3940 szone
->small_region_generation
= szone
->small_region_generation
->nextgen
;
3941 // Ensure everyone sees the advance.
3942 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
3943 __sync_synchronize();
3948 // Tag the region at "aligned_address" as belonging to us,
3949 // and so put it under the protection of the magazine lock we are holding.
3950 // Do this before advertising "aligned_address" on the hash ring(!)
3951 MAGAZINE_INDEX_FOR_SMALL_REGION(aligned_address
) = mag_index
;
3953 // Insert the new region into the hash ring, and update malloc statistics
3954 hash_region_insert_no_lock(szone
->small_region_generation
->hashed_regions
,
3955 szone
->small_region_generation
->num_regions_allocated
,
3956 szone
->small_region_generation
->num_regions_allocated_shift
,
3959 szone
->num_small_regions
++;
3961 UNLOCK(szone
->small_regions_lock
);
3963 small_mag_ptr
->mag_last_region
= aligned_address
;
3964 BYTES_USED_FOR_SMALL_REGION(aligned_address
) = SMALL_BYTES_FOR_MSIZE(msize
);
3965 ptr
= aligned_address
;
3966 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), 0, msize
);
3967 small_mag_ptr
->mag_num_objects
++;
3968 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(msize
);
3969 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3971 // add a big free block
3972 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
) , msize
, NUM_SMALL_BLOCKS
- msize
);
3973 small_mag_ptr
->mag_bytes_free_at_end
= SMALL_BYTES_FOR_MSIZE(NUM_SMALL_BLOCKS
- msize
);
3975 // connect to magazine as first node (it's maximally sparse at this moment)
3976 recirc_list_splice_first(szone
, small_mag_ptr
, REGION_TRAILER_FOR_SMALL_REGION(aligned_address
));
3981 static INLINE boolean_t
3982 small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
3984 // returns 1 on success
3985 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3987 msize_t old_msize
, new_msize
;
3988 unsigned next_index
;
3990 msize_t next_msize_and_free
;
3992 msize_t next_msize
, leftover_msize
;
3995 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
3996 old_msize
= SMALL_MSIZE_FOR_BYTES(old_size
);
3997 new_msize
= SMALL_MSIZE_FOR_BYTES(new_size
+ SMALL_QUANTUM
- 1);
3998 next_index
= index
+ old_msize
;
4000 if (next_index
>= NUM_SMALL_BLOCKS
) {
4003 next_block
= (char *)ptr
+ old_size
;
4006 if ((uintptr_t)next_block
& (SMALL_QUANTUM
- 1)) {
4007 szone_error(szone
, 1, "internal invariant broken in realloc(next_block)", next_block
, NULL
);
4009 if (meta_headers
[index
] != old_msize
)
4010 malloc_printf("*** small_try_realloc_in_place incorrect old %d %d\n",
4011 meta_headers
[index
], old_msize
);
4014 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4015 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4016 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4019 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
4022 next_msize_and_free
= meta_headers
[next_index
];
4023 is_free
= next_msize_and_free
& SMALL_IS_FREE
;
4025 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4026 return 0; // next_block is in use;
4028 next_msize
= next_msize_and_free
& ~ SMALL_IS_FREE
;
4029 if (old_msize
+ next_msize
< new_msize
) {
4030 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4031 return 0; // even with next block, not enough
4034 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
4037 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4038 small_meta_header_set_middle(meta_headers
, next_index
);
4039 leftover_msize
= old_msize
+ next_msize
- new_msize
;
4040 if (leftover_msize
) {
4041 /* there's some left, so put the remainder back */
4042 leftover
= (unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
);
4044 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover
, leftover_msize
);
4047 if (SMALL_BYTES_FOR_MSIZE(new_msize
) > szone
->large_threshold
) {
4048 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize
);
4051 small_meta_header_set_in_use(meta_headers
, index
, new_msize
);
4053 if (LOG(szone
,ptr
)) {
4054 malloc_printf("in szone_realloc(), ptr=%p, msize=%d\n", ptr
, *SMALL_METADATA_FOR_PTR(ptr
));
4057 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4059 // Update this region's bytes in use count
4060 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4061 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4062 node
->bytes_used
= bytes_used
;
4064 // Emptiness discriminant
4065 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4066 /* After this reallocation the region is still sparse, so it must have been even more so before
4067 the reallocation. That implies the region is already correctly marked. Do nothing. */
4069 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
4070 recirculation candidates list. */
4071 node
->recirc_suitable
= FALSE
;
4074 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4075 CHECK(szone
, __PRETTY_FUNCTION__
);
4080 small_check_region(szone_t
*szone
, region_t region
)
4082 unsigned char *ptr
= SMALL_REGION_ADDRESS(region
);
4083 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4084 unsigned char *region_end
= SMALL_REGION_END(region
);
4085 msize_t prev_free
= 0;
4087 msize_t msize_and_free
;
4089 free_list_t
*free_head
;
4090 void *previous
, *next
;
4092 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4093 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4096 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4098 if (region
== small_mag_ptr
->mag_last_region
)
4099 region_end
-= small_mag_ptr
->mag_bytes_free_at_end
;
4101 while (ptr
< region_end
) {
4102 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4103 msize_and_free
= meta_headers
[index
];
4104 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4106 msize
= msize_and_free
;
4108 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
4109 ptr
, szone
->num_small_regions
, region_end
);
4112 if (SMALL_BYTES_FOR_MSIZE(msize
) > szone
->large_threshold
) {
4113 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
4114 ptr
, msize_and_free
);
4117 ptr
+= SMALL_BYTES_FOR_MSIZE(msize
);
4121 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4122 free_head
= (free_list_t
*)ptr
;
4123 follower
= (msize_t
*)FOLLOWING_SMALL_PTR(ptr
, msize
);
4125 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr
, msize
);
4129 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr
);
4132 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
4133 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
4134 if (previous
&& !SMALL_PTR_IS_FREE(previous
)) {
4135 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
4136 ptr
, free_head
->previous
);
4139 if (next
&& !SMALL_PTR_IS_FREE(next
)) {
4140 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr
);
4143 if (SMALL_PREVIOUS_MSIZE(follower
) != msize
) {
4144 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
4145 "(end marker incorrect) should be %d; in fact %d\n",
4146 ptr
, follower
, SMALL_REGION_ADDRESS(region
), region_end
, msize
, SMALL_PREVIOUS_MSIZE(follower
));
4149 ptr
= (unsigned char *)follower
;
4150 prev_free
= SMALL_IS_FREE
;
4156 static kern_return_t
4157 small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
4158 memory_reader_t reader
, vm_range_recorder_t recorder
)
4163 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4168 vm_range_t admin_range
;
4169 vm_range_t ptr_range
;
4170 unsigned char *mapped_region
;
4171 msize_t
*block_header
;
4172 unsigned block_index
;
4173 unsigned block_limit
;
4174 msize_t msize_and_free
;
4176 vm_address_t mag_last_free_ptr
= 0;
4177 msize_t mag_last_free_msize
= 0;
4179 region_hash_generation_t
*srg_ptr
;
4180 err
= reader(task
, (vm_address_t
)szone
->small_region_generation
, sizeof(region_hash_generation_t
), (void **)&srg_ptr
);
4181 if (err
) return err
;
4183 num_regions
= srg_ptr
->num_regions_allocated
;
4184 err
= reader(task
, (vm_address_t
)srg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
4185 if (err
) return err
;
4187 for (index
= 0; index
< num_regions
; ++index
) {
4188 region
= regions
[index
];
4189 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
4190 range
.address
= (vm_address_t
)SMALL_REGION_ADDRESS(region
);
4191 range
.size
= SMALL_REGION_SIZE
;
4192 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4193 admin_range
.address
= range
.address
+ SMALL_METADATA_START
;
4194 admin_range
.size
= SMALL_METADATA_SIZE
;
4195 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
4197 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
4198 ptr_range
.address
= range
.address
;
4199 ptr_range
.size
= NUM_SMALL_BLOCKS
* SMALL_QUANTUM
;
4200 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
4202 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4203 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
4207 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(mapped_region
);
4208 magazine_t
*small_mag_ptr
;
4209 err
= reader(task
, (vm_address_t
)&(szone
->small_magazines
[mag_index
]), sizeof(magazine_t
),
4210 (void **)&small_mag_ptr
);
4211 if (err
) return err
;
4213 void *mag_last_free
= small_mag_ptr
->mag_last_free
;
4214 if (mag_last_free
) {
4215 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4216 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4219 block_header
= (msize_t
*)(mapped_region
+ SMALL_METADATA_START
+ sizeof(region_trailer_t
));
4221 block_limit
= NUM_SMALL_BLOCKS
;
4222 if (region
== small_mag_ptr
->mag_last_region
)
4223 block_limit
-= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
4224 while (block_index
< block_limit
) {
4225 msize_and_free
= block_header
[block_index
];
4226 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4227 if (! (msize_and_free
& SMALL_IS_FREE
) &&
4228 range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
) != mag_last_free_ptr
) {
4230 buffer
[count
].address
= range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
);
4231 buffer
[count
].size
= SMALL_BYTES_FOR_MSIZE(msize
);
4233 if (count
>= MAX_RECORDER_BUFFER
) {
4234 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4238 block_index
+= msize
;
4241 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4251 small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4255 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4256 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4257 free_list_t
**the_slot
= free_list
+ slot
;
4259 free_list_t
**limit
;
4261 msize_t leftover_msize
;
4262 free_list_t
*leftover_ptr
;
4264 // Assumes we've locked the region
4265 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4267 // Look for an exact match by checking the freelist for this msize.
4271 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4273 next
->previous
= ptr
->previous
;
4275 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4279 goto return_small_alloc
;
4282 // Mask off the bits representing slots holding free blocks smaller than
4283 // the size we need. If there are no larger free blocks, try allocating
4284 // from the free space at the end of the small region.
4285 if (szone
->is_largemem
) {
4286 // BITMAPN_CTZ implementation
4287 unsigned idx
= slot
>> 5;
4289 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4290 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4291 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4296 // Check for fallthrough: No bits set in bitmap
4297 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4298 goto try_small_from_end
;
4300 // Start looking at the first set bit, plus 32 bits for every word of
4301 // zeroes or entries that were too small.
4302 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4304 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4306 goto try_small_from_end
;
4308 slot
= BITMAP32_CTZ((&bitmap
));
4310 // FIXME: Explain use of - 1 here, last slot has special meaning
4311 limit
= free_list
+ szone
->num_small_slots
- 1;
4314 if (free_list
< limit
) {
4318 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4321 next
->previous
= ptr
->previous
;
4323 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4325 this_msize
= SMALL_PTR_SIZE(ptr
);
4326 goto add_leftover_and_proceed
;
4329 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4333 // We are now looking at the last slot, which contains blocks equal to, or
4334 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4335 // If the last freelist is not empty, and the head contains a block that is
4336 // larger than our request, then the remainder is put back on the free list.
4340 this_msize
= SMALL_PTR_SIZE(ptr
);
4341 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4342 if (this_msize
- msize
>= szone
->num_small_slots
) {
4343 // the leftover will go back to the free list, so we optimize by
4344 // modifying the free list rather than a pop and push of the head
4345 leftover_msize
= this_msize
- msize
;
4346 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4347 *limit
= leftover_ptr
;
4349 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
4351 leftover_ptr
->previous
= ptr
->previous
;
4352 leftover_ptr
->next
= ptr
->next
;
4353 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(leftover_ptr
),
4354 SMALL_META_INDEX_FOR_PTR(leftover_ptr
), leftover_msize
);
4355 // Store msize at the end of the block denoted by "leftover_ptr" (i.e. at a negative offset from follower)
4356 SMALL_PREVIOUS_MSIZE(FOLLOWING_SMALL_PTR(leftover_ptr
, leftover_msize
)) = leftover_msize
; // Access is safe
4358 if (LOG(szone
,ptr
)) {
4359 malloc_printf("in small_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr
, msize
, this_msize
);
4363 goto return_small_alloc
;
4366 next
->previous
= ptr
->previous
;
4369 goto add_leftover_and_proceed
;
4373 // Let's see if we can use small_mag_ptr->mag_bytes_free_at_end
4374 if (small_mag_ptr
->mag_bytes_free_at_end
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
4375 ptr
= (free_list_t
*)(SMALL_REGION_END(small_mag_ptr
->mag_last_region
) -
4376 small_mag_ptr
->mag_bytes_free_at_end
);
4377 small_mag_ptr
->mag_bytes_free_at_end
-= SMALL_BYTES_FOR_MSIZE(msize
);
4378 if (small_mag_ptr
->mag_bytes_free_at_end
) {
4379 // let's mark this block as in use to serve as boundary
4380 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
),
4381 SMALL_META_INDEX_FOR_PTR((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
)),
4382 SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
));
4385 goto return_small_alloc
;
4389 add_leftover_and_proceed
:
4390 if (this_msize
> msize
) {
4391 leftover_msize
= this_msize
- msize
;
4392 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4394 if (LOG(szone
,ptr
)) {
4395 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
4398 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover_ptr
, leftover_msize
);
4403 small_mag_ptr
->mag_num_objects
++;
4404 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(this_msize
);
4406 // Update this region's bytes in use count
4407 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4408 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(this_msize
);
4409 node
->bytes_used
= bytes_used
;
4411 // Emptiness discriminant
4412 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4413 /* After this allocation the region is still sparse, so it must have been even more so before
4414 the allocation. That implies the region is already correctly marked. Do nothing. */
4416 /* Region has crossed threshold from sparsity to density. Mark in not "suitable" on the
4417 recirculation candidates list. */
4418 node
->recirc_suitable
= FALSE
;
4421 if (LOG(szone
,ptr
)) {
4422 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
4425 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), this_msize
);
4428 #undef DENSITY_THRESHOLD
4431 static INLINE
void *
4432 small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
4435 mag_index_t mag_index
= mag_get_thread_index(szone
);
4436 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4438 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4441 ptr
= (void *)small_mag_ptr
->mag_last_free
;
4443 if ((((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) == msize
) {
4445 small_mag_ptr
->mag_last_free
= NULL
;
4446 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4447 CHECK(szone
, __PRETTY_FUNCTION__
);
4448 ptr
= (void *)((uintptr_t)ptr
& ~ (SMALL_QUANTUM
- 1));
4449 if (cleared_requested
) {
4450 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4454 #endif /* SMALL_CACHE */
4456 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
4458 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4459 CHECK(szone
, __PRETTY_FUNCTION__
);
4460 if (cleared_requested
) {
4461 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4466 if (small_get_region_from_depot(szone
, small_mag_ptr
, mag_index
)) {
4467 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
4469 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4470 CHECK(szone
, __PRETTY_FUNCTION__
);
4471 if (cleared_requested
) {
4472 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4478 ptr
= small_malloc_from_region_no_lock(szone
, small_mag_ptr
, mag_index
, msize
);
4479 // we don't clear because this freshly allocated space is pristine
4480 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4481 CHECK(szone
, __PRETTY_FUNCTION__
);
4485 static NOINLINE
void
4486 free_small_botch(szone_t
*szone
, free_list_t
*ptr
)
4488 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4489 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4490 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4491 szone_error(szone
, 1, "double free", ptr
, NULL
);
4495 free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
)
4498 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4499 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4501 // ptr is known to be in small_region
4503 msize
= SMALL_MSIZE_FOR_BYTES(known_size
+ SMALL_QUANTUM
- 1);
4505 msize
= SMALL_PTR_SIZE(ptr
);
4506 if (SMALL_PTR_IS_FREE(ptr
)) {
4507 free_small_botch(szone
, ptr
);
4512 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4515 // Depot does not participate in SMALL_CACHE since it can't be directly malloc()'d
4516 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4518 void *ptr2
= small_mag_ptr
->mag_last_free
; // Might be NULL
4519 region_t rgn2
= small_mag_ptr
->mag_last_free_rgn
;
4521 /* check that we don't already have this pointer in the cache */
4522 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (SMALL_QUANTUM
- 1))) {
4523 free_small_botch(szone
, ptr
);
4527 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
4528 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
4530 small_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
4531 small_mag_ptr
->mag_last_free_rgn
= small_region
;
4534 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4535 CHECK(szone
, __PRETTY_FUNCTION__
);
4539 msize
= (uintptr_t)ptr2
& (SMALL_QUANTUM
- 1);
4540 ptr
= (void *)(((uintptr_t)ptr2
) & ~(SMALL_QUANTUM
- 1));
4541 small_region
= rgn2
;
4543 #endif /* SMALL_CACHE */
4545 // Now in the time it took to acquire the lock, the region may have migrated
4546 // from one magazine to another. I.e. trailer->mag_index is volatile.
4547 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
4548 // is stale. If so, keep on tryin' ...
4549 region_trailer_t
*trailer
= REGION_TRAILER_FOR_SMALL_REGION(small_region
);
4550 mag_index_t refreshed_index
;
4552 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
4554 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4556 mag_index
= refreshed_index
;
4557 small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4558 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4561 small_free_no_lock(szone
, small_mag_ptr
, mag_index
, small_region
, ptr
, msize
);
4562 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4563 CHECK(szone
, __PRETTY_FUNCTION__
);
4567 print_small_free_list(szone_t
*szone
)
4570 _SIMPLE_STRING b
= _simple_salloc();
4571 mag_index_t mag_index
;
4574 _simple_sappend(b
, "small free sizes:\n");
4575 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4577 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
4578 while (slot
< szone
->num_small_slots
) {
4579 ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
4581 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== szone
->num_small_slots
-1) ? ">=" : "",
4582 (slot
+ 1) * SMALL_QUANTUM
, free_list_count(szone
, ptr
));
4586 _simple_sappend(b
,"\n");
4588 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4594 print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
)
4596 unsigned counts
[1024];
4597 unsigned in_use
= 0;
4598 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(region
);
4599 uintptr_t current
= start
;
4600 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(region
) - bytes_at_end
;
4601 msize_t msize_and_free
;
4605 uintptr_t pgTot
= 0;
4607 if (region
== HASHRING_REGION_DEALLOCATED
) {
4608 if ((b
= _simple_salloc()) != NULL
) {
4609 _simple_sprintf(b
, "Small region [unknown address] was returned to the OS\n");
4610 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4616 memset(counts
, 0, sizeof(counts
));
4617 while (current
< limit
) {
4618 msize_and_free
= *SMALL_METADATA_FOR_PTR(current
);
4619 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4621 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
4624 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4630 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
4631 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
4634 pgTot
+= (pgHi
- pgLo
);
4637 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
4639 if ((b
= _simple_salloc()) != NULL
) {
4640 _simple_sprintf(b
, "Small region [%p-%p, %y] \t", (void *)start
, SMALL_REGION_END(region
), (int)SMALL_REGION_SIZE
);
4641 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_SMALL_REGION(region
));
4642 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_SMALL_REGION(region
));
4644 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
);
4645 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_SMALL_REGION(region
)) {
4646 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
4648 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
4650 if (verbose
&& in_use
) {
4651 _simple_sappend(b
, "\n\tSizes in use: ");
4652 for (ci
= 0; ci
< 1024; ci
++)
4654 _simple_sprintf(b
, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
4656 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4662 small_free_list_check(szone_t
*szone
, grain_t slot
)
4664 mag_index_t mag_index
;
4666 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4667 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4668 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4671 free_list_t
*ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
4672 msize_t msize_and_free
;
4673 free_list_t
*previous
= NULL
;
4676 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
4677 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4678 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4679 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4682 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
4683 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4684 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4687 if (!small_region_for_ptr_no_lock(szone
, ptr
)) {
4688 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4689 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4692 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
4693 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4694 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4698 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4702 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4707 /*******************************************************************************
4708 * Large allocator implementation
4709 ******************************************************************************/
4710 #pragma mark large allocator
4715 large_debug_print(szone_t
*szone
)
4718 large_entry_t
*range
;
4719 _SIMPLE_STRING b
= _simple_salloc();
4722 for (index
= 0, range
= szone
->large_entries
; index
< szone
->num_large_entries
; index
++, range
++)
4724 _simple_sprintf(b
, "%d: %p(%y); ", index
, range
->address
, range
->size
);
4726 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4733 * Scan the hash ring looking for an entry for the given pointer.
4735 static large_entry_t
*
4736 large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
4738 // result only valid with lock held
4739 unsigned num_large_entries
= szone
->num_large_entries
;
4740 unsigned hash_index
;
4742 large_entry_t
*range
;
4744 if (!num_large_entries
)
4747 hash_index
= ((uintptr_t)ptr
>> vm_page_shift
) % num_large_entries
;
4751 range
= szone
->large_entries
+ index
;
4752 if (range
->address
== (vm_address_t
)ptr
)
4754 if (0 == range
->address
)
4755 return NULL
; // end of chain
4757 if (index
== num_large_entries
)
4759 } while (index
!= hash_index
);
4765 large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
)
4767 unsigned num_large_entries
= szone
->num_large_entries
;
4768 unsigned hash_index
= (((uintptr_t)(range
.address
)) >> vm_page_shift
) % num_large_entries
;
4769 unsigned index
= hash_index
;
4770 large_entry_t
*entry
;
4772 // assert(szone->num_large_objects_in_use < szone->num_large_entries); /* must be called with room to spare */
4775 entry
= szone
->large_entries
+ index
;
4776 if (0 == entry
->address
) {
4778 return; // end of chain
4781 if (index
== num_large_entries
)
4783 } while (index
!= hash_index
);
4785 // assert(0); /* must not fallthrough! */
4788 // FIXME: can't we simply swap the (now empty) entry with the last entry on the collision chain for this hash slot?
4790 large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
)
4792 unsigned num_large_entries
= szone
->num_large_entries
;
4793 unsigned hash_index
= entry
- szone
->large_entries
;
4794 unsigned index
= hash_index
;
4795 large_entry_t range
;
4797 // assert(entry->address == 0) /* caller must have cleared *entry */
4801 if (index
== num_large_entries
)
4803 range
= szone
->large_entries
[index
];
4804 if (0 == range
.address
)
4806 szone
->large_entries
[index
].address
= (vm_address_t
)0;
4807 szone
->large_entries
[index
].size
= 0;
4808 szone
->large_entries
[index
].did_madvise_reusable
= FALSE
;
4809 large_entry_insert_no_lock(szone
, range
); // this will reinsert in the
4811 } while (index
!= hash_index
);
4813 // assert(0); /* since entry->address == 0, must not fallthrough! */
4816 // FIXME: num should probably be a size_t, since you can theoretically allocate
4817 // more than 2^32-1 large_threshold objects in 64 bit.
4818 static INLINE large_entry_t
*
4819 large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
)
4821 size_t size
= num
* sizeof(large_entry_t
);
4823 // Note that we allocate memory (via a system call) under a spin lock
4824 // That is certainly evil, however it's very rare in the lifetime of a process
4825 // The alternative would slow down the normal case
4826 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC_LARGE
);
4830 large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
)
4832 size_t size
= num
* sizeof(large_entry_t
);
4834 range_to_deallocate
->address
= (vm_address_t
)entries
;
4835 range_to_deallocate
->size
= round_page(size
);
4838 static large_entry_t
*
4839 large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
)
4841 // sets range_to_deallocate
4842 unsigned old_num_entries
= szone
->num_large_entries
;
4843 large_entry_t
*old_entries
= szone
->large_entries
;
4844 // always an odd number for good hashing
4845 unsigned new_num_entries
= (old_num_entries
) ? old_num_entries
* 2 + 1 :
4846 ((vm_page_size
/ sizeof(large_entry_t
)) - 1);
4847 large_entry_t
*new_entries
= large_entries_alloc_no_lock(szone
, new_num_entries
);
4848 unsigned index
= old_num_entries
;
4849 large_entry_t oldRange
;
4851 // if the allocation of new entries failed, bail
4852 if (new_entries
== NULL
)
4855 szone
->num_large_entries
= new_num_entries
;
4856 szone
->large_entries
= new_entries
;
4858 /* rehash entries into the new list */
4860 oldRange
= old_entries
[index
];
4861 if (oldRange
.address
) {
4862 large_entry_insert_no_lock(szone
, oldRange
);
4867 large_entries_free_no_lock(szone
, old_entries
, old_num_entries
, range_to_deallocate
);
4869 range_to_deallocate
->address
= (vm_address_t
)0;
4870 range_to_deallocate
->size
= 0;
4876 // frees the specific entry in the size table
4877 // returns a range to truly deallocate
4879 large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
)
4883 range
.address
= entry
->address
;
4884 range
.size
= entry
->size
;
4886 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
4887 protect((void *)range
.address
, range
.size
, VM_PROT_READ
| VM_PROT_WRITE
, szone
->debug_flags
);
4888 range
.address
-= vm_page_size
;
4889 range
.size
+= 2 * vm_page_size
;
4894 entry
->did_madvise_reusable
= FALSE
;
4895 large_entries_rehash_after_entry_no_lock(szone
, entry
);
4898 if (large_entry_for_pointer_no_lock(szone
, (void *)range
.address
)) {
4899 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
4900 range
.address
, szone
->num_large_entries
);
4901 large_debug_print(szone
);
4908 static NOINLINE kern_return_t
4909 large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
,
4910 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
4913 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4915 large_entry_t
*entries
;
4918 large_entry_t entry
;
4920 err
= reader(task
, large_entries_address
, sizeof(large_entry_t
) * num_entries
, (void **)&entries
);
4924 index
= num_entries
;
4925 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4926 range
.address
= large_entries_address
;
4927 range
.size
= round_page(num_entries
* sizeof(large_entry_t
));
4928 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &range
, 1);
4930 if (type_mask
& (MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
)) {
4932 entry
= entries
[index
];
4933 if (entry
.address
) {
4934 range
.address
= entry
.address
;
4935 range
.size
= entry
.size
;
4936 buffer
[count
++] = range
;
4937 if (count
>= MAX_RECORDER_BUFFER
) {
4938 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
4946 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
4953 large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
,
4954 boolean_t cleared_requested
)
4957 vm_range_t range_to_deallocate
;
4959 large_entry_t large_entry
;
4962 num_pages
= 1; // minimal allocation size for this szone
4963 size
= (size_t)num_pages
<< vm_page_shift
;
4964 range_to_deallocate
.size
= 0;
4965 range_to_deallocate
.address
= 0;
4968 if (size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
) { // Look for a large_entry_t on the death-row cache?
4971 int i
, best
= -1, idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
4972 size_t best_size
= SIZE_T_MAX
;
4974 while (1) { // Scan large_entry_cache for best fit, starting with most recent entry
4975 size_t this_size
= szone
->large_entry_cache
[idx
].size
;
4977 if (size
== this_size
) { // size match!
4979 best_size
= this_size
;
4983 if (size
<= this_size
&& this_size
< best_size
) { // improved fit?
4985 best_size
= this_size
;
4988 if (idx
== stop_idx
) // exhausted live ring?
4992 idx
--; // bump idx down
4994 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
4997 if (best
> -1 && (best_size
- size
) < size
) { //limit fragmentation to 50%
4998 addr
= (void *)szone
->large_entry_cache
[best
].address
;
4999 boolean_t was_madvised_reusable
= szone
->large_entry_cache
[best
].did_madvise_reusable
;
5001 // Compact live ring to fill entry now vacated at large_entry_cache[best]
5002 // while preserving time-order
5003 if (szone
->large_entry_cache_oldest
< szone
->large_entry_cache_newest
) {
5005 // Ring hasn't wrapped. Fill in from right.
5006 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5007 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5009 szone
->large_entry_cache_newest
--; // Pull in right endpoint.
5011 } else if (szone
->large_entry_cache_newest
< szone
->large_entry_cache_oldest
) {
5013 // Ring has wrapped. Arrange to fill in from the contiguous side.
5014 if (best
<= szone
->large_entry_cache_newest
) {
5016 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5017 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5019 if (0 < szone
->large_entry_cache_newest
)
5020 szone
->large_entry_cache_newest
--;
5022 szone
->large_entry_cache_newest
= LARGE_ENTRY_CACHE_SIZE
- 1;
5025 for ( i
= best
; i
> szone
->large_entry_cache_oldest
; --i
)
5026 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
- 1];
5028 if (szone
->large_entry_cache_oldest
< LARGE_ENTRY_CACHE_SIZE
- 1)
5029 szone
->large_entry_cache_oldest
++;
5031 szone
->large_entry_cache_oldest
= 0;
5035 // By trichotomy, large_entry_cache_newest == large_entry_cache_oldest.
5036 // That implies best == large_entry_cache_newest == large_entry_cache_oldest
5037 // and the ring is now empty.
5038 szone
->large_entry_cache
[best
].address
= 0;
5039 szone
->large_entry_cache
[best
].size
= 0;
5040 szone
->large_entry_cache
[best
].did_madvise_reusable
= FALSE
;
5043 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5044 // density of hash table too high; grow table
5045 // we do that under lock to avoid a race
5046 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5047 if (entries
== NULL
) {
5048 SZONE_UNLOCK(szone
);
5053 large_entry
.address
= (vm_address_t
)addr
;
5054 large_entry
.size
= best_size
;
5055 large_entry
.did_madvise_reusable
= FALSE
;
5056 large_entry_insert_no_lock(szone
, large_entry
);
5058 szone
->num_large_objects_in_use
++;
5059 szone
->num_bytes_in_large_objects
+= best_size
;
5060 if (!was_madvised_reusable
)
5061 szone
->large_entry_cache_hoard_bytes
-= best_size
;
5062 SZONE_UNLOCK(szone
);
5064 if (range_to_deallocate
.size
) {
5065 // we deallocate outside the lock
5066 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5069 // Perform the madvise() outside the lock.
5070 // Typically the madvise() is successful and we'll quickly return from this routine.
5071 // In the unusual case of failure, reacquire the lock to unwind.
5072 if (was_madvised_reusable
&& -1 == madvise(addr
, size
, MADV_FREE_REUSE
)) {
5073 /* -1 return: VM map entry change makes this unfit for reuse. */
5075 szone_error(szone
, 1, "large_malloc madvise(..., MADV_FREE_REUSE) failed", addr
, NULL
);
5079 szone
->num_large_objects_in_use
--;
5080 szone
->num_bytes_in_large_objects
-= large_entry
.size
;
5082 // Re-acquire "entry" after interval just above where we let go the lock.
5083 large_entry_t
*entry
= large_entry_for_pointer_no_lock(szone
, addr
);
5084 if (NULL
== entry
) {
5085 szone_error(szone
, 1, "entry for pointer being discarded from death-row vanished", addr
, NULL
);
5086 SZONE_UNLOCK(szone
);
5089 range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5090 SZONE_UNLOCK(szone
);
5092 if (range_to_deallocate
.size
) {
5093 // we deallocate outside the lock
5094 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5097 /* Fall through to allocate_pages() afresh. */
5099 if (cleared_requested
) {
5100 memset(addr
, 0, size
);
5106 SZONE_UNLOCK(szone
);
5110 range_to_deallocate
.size
= 0;
5111 range_to_deallocate
.address
= 0;
5112 #endif /* LARGE_CACHE */
5114 addr
= allocate_pages(szone
, size
, alignment
, szone
->debug_flags
, VM_MEMORY_MALLOC_LARGE
);
5120 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5121 // density of hash table too high; grow table
5122 // we do that under lock to avoid a race
5123 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5124 if (entries
== NULL
) {
5125 SZONE_UNLOCK(szone
);
5130 large_entry
.address
= (vm_address_t
)addr
;
5131 large_entry
.size
= size
;
5132 large_entry
.did_madvise_reusable
= FALSE
;
5133 large_entry_insert_no_lock(szone
, large_entry
);
5135 szone
->num_large_objects_in_use
++;
5136 szone
->num_bytes_in_large_objects
+= size
;
5137 SZONE_UNLOCK(szone
);
5139 if (range_to_deallocate
.size
) {
5140 // we deallocate outside the lock
5141 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5146 static NOINLINE
void
5147 free_large(szone_t
*szone
, void *ptr
)
5149 // We have established ptr is page-aligned and neither tiny nor small
5150 large_entry_t
*entry
;
5151 vm_range_t vm_range_to_deallocate
;
5154 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5157 #ifndef MADV_CAN_REUSE
5158 #define MADV_CAN_REUSE 9 /* per Francois, for testing until xnu is resubmitted to B&I */
5160 if (entry
->size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
&&
5161 -1 != madvise((void *)(entry
->address
), entry
->size
, MADV_CAN_REUSE
)) { // Put the large_entry_t on the death-row cache?
5162 int idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5163 large_entry_t this_entry
= *entry
; // Make a local copy, "entry" is volatile when lock is let go.
5164 boolean_t reusable
= TRUE
;
5165 boolean_t should_madvise
= szone
->large_entry_cache_hoard_bytes
+ this_entry
.size
> szone
->large_entry_cache_hoard_lmit
;
5168 // [Note that repeated entries in death-row risk vending the same entry subsequently
5169 // to two different malloc() calls. By checking here the (illegal) double free
5170 // is accommodated, matching the behavior of the previous implementation.]
5171 while (1) { // Scan large_entry_cache starting with most recent entry
5172 if (szone
->large_entry_cache
[idx
].address
== entry
->address
) {
5173 szone_error(szone
, 1, "pointer being freed already on death-row", ptr
, NULL
);
5174 SZONE_UNLOCK(szone
);
5178 if (idx
== stop_idx
) // exhausted live ring?
5182 idx
--; // bump idx down
5184 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5187 SZONE_UNLOCK(szone
);
5189 if (szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) { // Are we a purgable zone?
5190 int state
= VM_PURGABLE_NONVOLATILE
; // restore to default condition
5192 if (KERN_SUCCESS
!= vm_purgable_control(mach_task_self(), this_entry
.address
, VM_PURGABLE_SET_STATE
, &state
)) {
5193 malloc_printf("*** can't vm_purgable_control(..., VM_PURGABLE_SET_STATE) for large freed block at %p\n", this_entry
.address
);
5198 if (szone
->large_legacy_reset_mprotect
) { // Linked for Leopard?
5199 // Accomodate Leopard apps that (illegally) mprotect() their own guard pages on large malloc'd allocations
5200 kern_return_t err
= vm_protect(mach_task_self(), (vm_address_t
)(this_entry
.address
), this_entry
.size
,
5201 0, PROT_READ
| PROT_WRITE
);
5203 malloc_printf("*** can't reset protection for large freed block at %p\n", this_entry
.address
);
5208 // madvise(..., MADV_REUSABLE) death-row arrivals if hoarding would exceed large_entry_cache_hoard_lmit
5209 if (should_madvise
) {
5210 // Issue madvise to avoid paging out the dirtied free()'d pages in "entry"
5211 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)0, (void *)(this_entry
.address
), this_entry
.size
); // DTrace USDT Probe
5213 if (-1 == madvise((void *)(this_entry
.address
), this_entry
.size
, MADV_FREE_REUSABLE
)) {
5214 /* -1 return: VM map entry change makes this unfit for reuse. */
5216 szone_error(szone
, 1, "free_large madvise(..., MADV_FREE_REUSABLE) failed", (void *)this_entry
.address
, NULL
);
5224 // Re-acquire "entry" after interval just above where we let go the lock.
5225 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5226 if (NULL
== entry
) {
5227 szone_error(szone
, 1, "entry for pointer being freed from death-row vanished", ptr
, NULL
);
5228 SZONE_UNLOCK(szone
);
5232 // Add "entry" to death-row ring
5234 int idx
= szone
->large_entry_cache_newest
; // Most recently occupied
5238 if (szone
->large_entry_cache_newest
== szone
->large_entry_cache_oldest
&&
5239 0 == szone
->large_entry_cache
[idx
].address
) {
5240 // Ring is empty, idx is good as it stands
5244 // Extend the queue to the "right" by bumping up large_entry_cache_newest
5245 if (idx
== LARGE_ENTRY_CACHE_SIZE
- 1)
5246 idx
= 0; // Wrap index
5248 idx
++; // Bump index
5250 if (idx
== szone
->large_entry_cache_oldest
) { // Fully occupied
5251 // Drop this entry from the cache and deallocate the VM
5252 addr
= szone
->large_entry_cache
[idx
].address
;
5253 adjsize
= szone
->large_entry_cache
[idx
].size
;
5254 if (!szone
->large_entry_cache
[idx
].did_madvise_reusable
)
5255 szone
->large_entry_cache_hoard_bytes
-= adjsize
;
5257 // Using an unoccupied cache slot
5263 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
))
5264 memset((void *)(entry
->address
), 0x55, entry
->size
);
5266 entry
->did_madvise_reusable
= should_madvise
; // Was madvise()'d above?
5267 if (!should_madvise
) // Entered on death-row without madvise() => up the hoard total
5268 szone
->large_entry_cache_hoard_bytes
+= entry
->size
;
5270 szone
->large_entry_cache
[idx
] = *entry
;
5271 szone
->large_entry_cache_newest
= idx
;
5273 szone
->num_large_objects_in_use
--;
5274 szone
->num_bytes_in_large_objects
-= entry
->size
;
5276 (void)large_entry_free_no_lock(szone
, entry
);
5279 SZONE_UNLOCK(szone
);
5283 // Fall through to drop large_entry_cache_oldest from the cache,
5284 // and then deallocate its pages.
5286 // Trim the queue on the "left" by bumping up large_entry_cache_oldest
5287 if (szone
->large_entry_cache_oldest
== LARGE_ENTRY_CACHE_SIZE
- 1)
5288 szone
->large_entry_cache_oldest
= 0;
5290 szone
->large_entry_cache_oldest
++;
5292 // we deallocate_pages, including guard pages, outside the lock
5293 SZONE_UNLOCK(szone
);
5294 deallocate_pages(szone
, (void *)addr
, (size_t)adjsize
, 0);
5297 /* fall through to discard an allocation that is not reusable */
5300 #endif /* LARGE_CACHE */
5302 szone
->num_large_objects_in_use
--;
5303 szone
->num_bytes_in_large_objects
-= entry
->size
;
5305 vm_range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5308 large_debug_print(szone
);
5310 szone_error(szone
, 1, "pointer being freed was not allocated", ptr
, NULL
);
5311 SZONE_UNLOCK(szone
);
5314 SZONE_UNLOCK(szone
); // we release the lock asap
5315 CHECK(szone
, __PRETTY_FUNCTION__
);
5317 // we deallocate_pages, including guard pages, outside the lock
5318 if (vm_range_to_deallocate
.address
) {
5320 // FIXME: large_entry_for_pointer_no_lock() needs the lock held ...
5321 if (large_entry_for_pointer_no_lock(szone
, (void *)vm_range_to_deallocate
.address
)) {
5322 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n",
5323 vm_range_to_deallocate
.address
, szone
->num_large_entries
);
5324 large_debug_print(szone
);
5328 deallocate_pages(szone
, (void *)vm_range_to_deallocate
.address
, (size_t)vm_range_to_deallocate
.size
, 0);
5333 large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
5335 vm_address_t addr
= (vm_address_t
)ptr
+ old_size
;
5336 large_entry_t
*large_entry
;
5340 large_entry
= large_entry_for_pointer_no_lock(szone
, (void *)addr
);
5341 SZONE_UNLOCK(szone
);
5343 if (large_entry
) { // check if "addr = ptr + old_size" is already spoken for
5344 return 0; // large pointer already exists in table - extension is not going to work
5347 new_size
= round_page(new_size
);
5349 * Ask for allocation at a specific address, and mark as realloc
5350 * to request coalescing with previous realloc'ed extensions.
5352 err
= vm_allocate(mach_task_self(), &addr
, new_size
- old_size
, VM_MAKE_TAG(VM_MEMORY_REALLOC
));
5353 if (err
!= KERN_SUCCESS
) {
5358 /* extend existing large entry */
5359 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5361 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
5362 SZONE_UNLOCK(szone
);
5363 return 0; // Bail, leaking "addr"
5366 large_entry
->address
= (vm_address_t
)ptr
;
5367 large_entry
->size
= new_size
;
5368 szone
->num_bytes_in_large_objects
+= new_size
- old_size
;
5369 SZONE_UNLOCK(szone
); // we release the lock asap
5374 /********************* Zone call backs ************************/
5376 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
5378 static NOINLINE
void
5379 szone_free(szone_t
*szone
, void *ptr
)
5381 region_t tiny_region
;
5382 region_t small_region
;
5385 if (LOG(szone
, ptr
))
5386 malloc_printf("in szone_free with %p\n", ptr
);
5391 * Try to free to a tiny region.
5393 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
5394 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
5397 if ((tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
5398 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
5399 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
5402 free_tiny(szone
, ptr
, tiny_region
, 0);
5407 * Try to free to a small region.
5409 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
5410 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
5413 if ((small_region
= small_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
5414 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
5415 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
5418 free_small(szone
, ptr
, small_region
, 0);
5422 /* check that it's a legal large allocation */
5423 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
5424 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
5427 free_large(szone
, ptr
);
5430 static NOINLINE
void
5431 szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
5434 if (LOG(szone
, ptr
))
5435 malloc_printf("in szone_free_definite_size with %p\n", ptr
);
5438 szone_error(szone
, 1, "pointer of size zero being freed", ptr
, NULL
);
5447 * Try to free to a tiny region.
5449 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
5450 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
5453 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5454 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
5455 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
5458 free_tiny(szone
, ptr
, TINY_REGION_FOR_PTR(ptr
), size
);
5463 * Try to free to a small region.
5465 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
5466 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
5469 if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5470 (size
<= szone
->large_threshold
)) {
5471 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
5472 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
5475 free_small(szone
, ptr
, SMALL_REGION_FOR_PTR(ptr
), size
);
5479 /* check that it's a legal large allocation */
5480 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
5481 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
5484 free_large(szone
, ptr
);
5487 static NOINLINE
void *
5488 szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
)
5493 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5495 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5498 ptr
= tiny_malloc_should_clear(szone
, msize
, cleared_requested
);
5499 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5500 (size
<= szone
->large_threshold
)) {
5502 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
5505 ptr
= small_malloc_should_clear(szone
, msize
, cleared_requested
);
5508 size_t num_pages
= round_page(size
) >> vm_page_shift
;
5509 if (num_pages
== 0) /* Overflowed */
5512 ptr
= large_malloc(szone
, num_pages
, 0, cleared_requested
);
5515 if (LOG(szone
, ptr
))
5516 malloc_printf("szone_malloc returned %p\n", ptr
);
5519 * If requested, scribble on allocated memory.
5521 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && ptr
&& !cleared_requested
&& size
)
5522 memset(ptr
, 0xaa, size
);
5527 static NOINLINE
void *
5528 szone_malloc(szone_t
*szone
, size_t size
) {
5529 return szone_malloc_should_clear(szone
, size
, 0);
5532 static NOINLINE
void *
5533 szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
5535 size_t total_bytes
= num_items
* size
;
5537 // Check for overflow of integer multiplication
5538 if (num_items
> 1) {
5539 #if __LP64__ /* size_t is uint64_t */
5540 if ((num_items
| size
) & 0xffffffff00000000ul
) {
5541 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
5542 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
5543 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
5546 #else /* size_t is uint32_t */
5547 if ((num_items
| size
) & 0xffff0000ul
) {
5548 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
5549 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
5550 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
5556 return szone_malloc_should_clear(szone
, total_bytes
, 1);
5559 static NOINLINE
void *
5560 szone_valloc(szone_t
*szone
, size_t size
)
5564 if (size
<= szone
->large_threshold
) {
5565 ptr
= szone_memalign(szone
, vm_page_size
, size
);
5569 num_pages
= round_page(size
) >> vm_page_shift
;
5570 ptr
= large_malloc(szone
, num_pages
, 0, 0);
5574 if (LOG(szone
, ptr
))
5575 malloc_printf("szone_valloc returned %p\n", ptr
);
5580 /* Isolate PIC-base load (for __is_threaded) here. */
5581 static NOINLINE
size_t
5582 szone_size_try_large(szone_t
*szone
, const void *ptr
)
5585 large_entry_t
*entry
;
5588 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5592 SZONE_UNLOCK(szone
);
5594 if (LOG(szone
, ptr
)) {
5595 malloc_printf("szone_size for %p returned %d\n", ptr
, (unsigned)size
);
5601 static NOINLINE
size_t
5602 szone_size(szone_t
*szone
, const void *ptr
)
5605 msize_t msize
, msize_and_free
;
5610 if (LOG(szone
, ptr
)) {
5611 malloc_printf("in szone_size for %p (szone=%p)\n", ptr
, szone
);
5616 * Look for it in a tiny region.
5618 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1))
5620 if (tiny_region_for_ptr_no_lock(szone
, ptr
)) {
5621 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
5623 msize
= get_tiny_meta_header(ptr
, &is_free
);
5628 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
5629 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
5631 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
5635 return TINY_BYTES_FOR_MSIZE(msize
);
5639 * Look for it in a small region.
5641 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1))
5643 if (small_region_for_ptr_no_lock(szone
, ptr
)) {
5644 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
)
5646 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
5647 if (msize_and_free
& SMALL_IS_FREE
)
5651 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5652 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5654 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
5658 return SMALL_BYTES_FOR_MSIZE(msize_and_free
);
5662 * If not page-aligned, it cannot have come from a large allocation.
5664 if ((uintptr_t)ptr
& (vm_page_size
- 1))
5668 * Look for it in a large entry.
5670 return szone_size_try_large(szone
, ptr
);
5673 static NOINLINE
void *
5674 szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
5680 if (LOG(szone
, ptr
)) {
5681 malloc_printf("in szone_realloc for %p, %d\n", ptr
, (unsigned)new_size
);
5685 ptr
= szone_malloc(szone
, new_size
);
5688 old_size
= szone_size(szone
, ptr
);
5690 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
5693 /* we never shrink an allocation */
5694 if (old_size
>= new_size
)
5698 * If the new size suits the tiny allocator and the pointer being resized
5699 * belongs to a tiny region, try to reallocate in-place.
5701 if ((new_size
+ TINY_QUANTUM
- 1) <= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
5702 if (tiny_region_for_ptr_no_lock(szone
, ptr
) != NULL
) {
5703 if (tiny_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5709 * If the new size suits the small allocator and the pointer being resized
5710 * belongs to a small region, and we're not protecting the small allocations
5711 * try to reallocate in-place.
5713 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5714 ((new_size
+ SMALL_QUANTUM
- 1) <= szone
->large_threshold
) &&
5715 (small_region_for_ptr_no_lock(szone
, ptr
) != NULL
)) {
5716 if (small_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5721 * If the allocation's a large allocation, try to reallocate in-place there.
5723 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5724 !(szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) &&
5725 (old_size
> szone
->large_threshold
)) {
5726 if (large_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5732 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
5734 new_ptr
= szone_malloc(szone
, new_size
);
5735 if (new_ptr
== NULL
)
5739 * If the allocation's large enough, try to copy using VM. If that fails, or
5740 * if it's too small, just copy by hand.
5742 if ((old_size
< szone
->vm_copy_threshold
) ||
5743 vm_copy(mach_task_self(), (vm_address_t
)ptr
, old_size
, (vm_address_t
)new_ptr
))
5744 memcpy(new_ptr
, ptr
, old_size
);
5745 szone_free(szone
, ptr
);
5748 if (LOG(szone
, ptr
)) {
5749 malloc_printf("szone_realloc returned %p for %d\n", new_ptr
, (unsigned)new_size
);
5755 static NOINLINE
void *
5756 szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
5758 if ((size
+ alignment
) < size
) // size_t arithmetic wrapped!
5761 // alignment is gauranteed a power of 2 at least as large as sizeof(void *), hence non-zero.
5762 // Since size + alignment didn't wrap, 0 <= size + alignment - 1 < size + alignment
5763 size_t span
= size
+ alignment
- 1;
5765 if (alignment
<= TINY_QUANTUM
) {
5766 return szone_malloc(szone
, size
); // Trivially satisfied by tiny, small, or large
5768 } else if (span
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5769 msize_t mspan
= TINY_MSIZE_FOR_BYTES(span
+ TINY_QUANTUM
- 1);
5770 void *p
= szone_malloc(szone
, span
); // avoids inlining tiny_malloc_should_clear(szone, mspan, 0);
5775 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
5776 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
5778 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5779 msize_t mpad
= TINY_MSIZE_FOR_BYTES(pad
+ TINY_QUANTUM
- 1);
5780 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
5783 void *q
= (void *)(((uintptr_t) p
) + pad
);
5785 // Mark q as a block header and in-use, thus creating two blocks.
5786 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5787 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
5788 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
5789 set_tiny_meta_header_in_use(q
, msize
);
5791 // set_tiny_meta_header_in_use() "reaffirms" the block_header on the *following* block, so
5792 // now set its in_use bit as well. But only if its within the original allocation made above.
5794 BITARRAY_SET(TINY_INUSE_FOR_HEADER(TINY_BLOCK_HEADER_FOR_PTR(q
)), TINY_INDEX_FOR_PTR(q
) + msize
);
5795 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5797 // Give up mpad blocks beginning at p to the tiny free list
5798 // region_t r = TINY_REGION_FOR_PTR(p);
5799 szone_free(szone
, p
); // avoids inlining free_tiny(szone, p, &r);
5801 p
= q
; // advance p to the desired alignment
5805 void *q
= (void *)(((uintptr_t) p
) + TINY_BYTES_FOR_MSIZE(msize
));
5806 // Mark q as block header and in-use, thus creating two blocks.
5807 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5808 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
5809 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
5810 set_tiny_meta_header_in_use(q
, mwaste
);
5811 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5813 // Give up mwaste blocks beginning at q to the tiny free list
5814 // region_t r = TINY_REGION_FOR_PTR(q);
5815 szone_free(szone
, q
); // avoids inlining free_tiny(szone, q, &r);
5818 return p
; // p has the desired size and alignment, and can later be free()'d
5820 } else if ((NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
< size
&& alignment
<= SMALL_QUANTUM
) {
5821 return szone_malloc(szone
, size
); // Trivially satisfied by small or large
5823 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) && (span
<= szone
->large_threshold
)) {
5825 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5826 size
= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
+ TINY_QUANTUM
; // ensure block allocated by small does not have a tiny-possible size
5827 span
= size
+ alignment
- 1;
5830 msize_t mspan
= SMALL_MSIZE_FOR_BYTES(span
+ SMALL_QUANTUM
- 1);
5831 void *p
= szone_malloc(szone
, span
); // avoid inlining small_malloc_should_clear(szone, mspan, 0);
5836 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
5837 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
5839 msize_t msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
5840 msize_t mpad
= SMALL_MSIZE_FOR_BYTES(pad
+ SMALL_QUANTUM
- 1);
5841 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
5844 void *q
= (void *)(((uintptr_t) p
) + pad
);
5846 // Mark q as block header and in-use, thus creating two blocks.
5847 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
5848 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
5849 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
5850 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), mpad
);
5851 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), msize
+ mwaste
);
5852 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5854 // Give up mpad blocks beginning at p to the small free list
5855 // region_t r = SMALL_REGION_FOR_PTR(p);
5856 szone_free(szone
, p
); // avoid inlining free_small(szone, p, &r);
5858 p
= q
; // advance p to the desired alignment
5861 void *q
= (void *)(((uintptr_t) p
) + SMALL_BYTES_FOR_MSIZE(msize
));
5862 // Mark q as block header and in-use, thus creating two blocks.
5863 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
5864 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
5865 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
5866 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), msize
);
5867 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mwaste
);
5868 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5870 // Give up mwaste blocks beginning at q to the small free list
5871 // region_t r = SMALL_REGION_FOR_PTR(q);
5872 szone_free(szone
, q
); // avoid inlining free_small(szone, q, &r);
5875 return p
; // p has the desired size and alignment, and can later be free()'d
5877 } else if (szone
->large_threshold
< size
&& alignment
<= vm_page_size
) {
5878 return szone_malloc(szone
, size
); // Trivially satisfied by large
5881 // ensure block allocated by large does not have a small-possible size
5882 size_t num_pages
= round_page(MAX(szone
->large_threshold
+ 1, size
)) >> vm_page_shift
;
5885 if (num_pages
== 0) /* Overflowed */
5888 p
= large_malloc(szone
, num_pages
, MAX(vm_page_shift
, __builtin_ctz(alignment
)), 0);
5895 // given a size, returns the number of pointers allocated capable of holding
5896 // that size, up to the limit specified by the 'count' argument. These pointers
5897 // are stored in the 'results' array, which must be allocated by the caller.
5898 // may return zero, since this function is only a best attempt at allocating
5899 // the pointers. clients should be prepared to call malloc for any additional
5900 // blocks they need.
5901 static NOINLINE
unsigned
5902 szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
5904 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5906 mag_index_t mag_index
= mag_get_thread_index(szone
);
5907 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
5909 // only bother implementing this for tiny
5910 if (size
> (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
)
5912 // make sure to return objects at least one quantum in size
5916 CHECK(szone
, __PRETTY_FUNCTION__
);
5918 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
5919 // the caller has done so.
5920 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
5922 // with the zone locked, allocate objects from the free list until all
5923 // sufficiently large objects have been exhausted, or we have met our quota
5924 // of objects to allocate.
5925 while (found
< count
) {
5926 void *ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
5933 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5937 /* Try caching the tiny_region and checking if the next ptr hits there. */
5938 static NOINLINE
void
5939 szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
5943 region_t tiny_region
= NULL
;
5946 magazine_t
*tiny_mag_ptr
= NULL
;
5947 mag_index_t mag_index
= -1;
5949 // frees all the pointers in to_be_freed
5950 // note that to_be_freed may be overwritten during the process
5954 CHECK(szone
, __PRETTY_FUNCTION__
);
5955 while (cc
< count
) {
5956 ptr
= to_be_freed
[cc
];
5958 if (NULL
== tiny_region
|| tiny_region
!= TINY_REGION_FOR_PTR(ptr
)) { // region same as last iteration?
5959 if (tiny_mag_ptr
) { // non-NULL iff magazine lock taken
5960 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5961 tiny_mag_ptr
= NULL
;
5964 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
5967 tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5968 REGION_TRAILER_FOR_TINY_REGION(tiny_region
),
5969 MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
));
5970 mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
5974 // this is a tiny pointer
5975 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
5976 break; // pointer to metadata; let the standard free deal with it
5977 msize
= get_tiny_meta_header(ptr
, &is_free
);
5979 break; // a double free; let the standard free deal with it
5981 tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
);
5982 to_be_freed
[cc
] = NULL
;
5984 // No region in this zone claims ptr; let the standard free deal with it
5992 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5993 tiny_mag_ptr
= NULL
;
5996 CHECK(szone
, __PRETTY_FUNCTION__
);
5998 ptr
= to_be_freed
[count
];
6000 szone_free(szone
, ptr
);
6004 // FIXME: Suppose one of the locks is held?
6006 szone_destroy(szone_t
*szone
)
6009 large_entry_t
*large
;
6010 vm_range_t range_to_deallocate
;
6012 /* destroy large entries */
6013 index
= szone
->num_large_entries
;
6015 large
= szone
->large_entries
+ index
;
6016 if (large
->address
) {
6017 // we deallocate_pages, including guard pages
6018 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6021 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6022 if (range_to_deallocate
.size
)
6023 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6025 /* destroy tiny regions */
6026 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
)
6027 if ((HASHRING_OPEN_ENTRY
!= szone
->tiny_region_generation
->hashed_regions
[index
]) &&
6028 (HASHRING_REGION_DEALLOCATED
!= szone
->tiny_region_generation
->hashed_regions
[index
]))
6029 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
[index
], TINY_REGION_SIZE
, 0);
6031 /* destroy small regions */
6032 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
)
6033 if ((HASHRING_OPEN_ENTRY
!= szone
->small_region_generation
->hashed_regions
[index
]) &&
6034 (HASHRING_REGION_DEALLOCATED
!= szone
->small_region_generation
->hashed_regions
[index
]))
6035 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
[index
], SMALL_REGION_SIZE
, 0);
6037 /* destroy region hash rings, if any */
6038 if (szone
->tiny_region_generation
->hashed_regions
!= szone
->initial_tiny_regions
) {
6039 size_t size
= round_page(szone
->tiny_region_generation
->num_regions_allocated
* sizeof(region_t
));
6040 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
, size
, 0);
6042 if (szone
->small_region_generation
->hashed_regions
!= szone
->initial_small_regions
) {
6043 size_t size
= round_page(szone
->small_region_generation
->num_regions_allocated
* sizeof(region_t
));
6044 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
, size
, 0);
6047 /* Now destroy the separate szone region */
6048 if (szone
->cpu_id_key
!= (pthread_key_t
) -1)
6049 (void)pthread_key_delete(szone
->cpu_id_key
);
6050 deallocate_pages(szone
, (void *)&(szone
->tiny_magazines
[-1]), TINY_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6051 deallocate_pages(szone
, (void *)&(szone
->small_magazines
[-1]), SMALL_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6052 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6055 static NOINLINE
size_t
6056 szone_good_size(szone_t
*szone
, size_t size
)
6059 int guard_small
= (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
;
6061 // Find a good size for this tiny allocation.
6062 if (size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6063 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6066 return TINY_BYTES_FOR_MSIZE(msize
);
6069 // Find a good size for this small allocation.
6070 if (!guard_small
&& (size
<= szone
->large_threshold
)) {
6071 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6074 return SMALL_BYTES_FOR_MSIZE(msize
);
6077 // Check for integer overflow on the size, since unlike the two cases above,
6078 // there is no upper bound on allocation size at this point.
6079 if (size
> round_page(size
))
6080 return (size_t)(-1LL);
6083 // It is not acceptable to see a size of zero here, since that means we
6084 // failed to catch a request for zero bytes in the tiny check, or the size
6085 // overflowed to zero during some arithmetic.
6087 malloc_printf("szone_good_size() invariant broken %y\n", size
);
6089 return round_page(size
);
6092 unsigned szone_check_counter
= 0;
6093 unsigned szone_check_start
= 0;
6094 unsigned szone_check_modulo
= 1;
6096 static NOINLINE boolean_t
6097 szone_check_all(szone_t
*szone
, const char *function
)
6101 /* check tiny regions - chould check region count */
6102 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6103 region_t tiny
= szone
->tiny_region_generation
->hashed_regions
[index
];
6105 if (HASHRING_REGION_DEALLOCATED
== tiny
)
6109 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6110 REGION_TRAILER_FOR_TINY_REGION(tiny
), MAGAZINE_INDEX_FOR_TINY_REGION(tiny
));
6112 if (!tiny_check_region(szone
, tiny
)) {
6113 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6114 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6115 szone_error(szone
, 1, "check: tiny region incorrect", NULL
,
6116 "*** tiny region %ld incorrect szone_check_all(%s) counter=%d\n",
6117 index
, function
, szone_check_counter
);
6120 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6123 /* check tiny free lists */
6124 for (index
= 0; index
< NUM_TINY_SLOTS
; ++index
) {
6125 if (!tiny_free_list_check(szone
, index
)) {
6126 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6127 szone_error(szone
, 1, "check: tiny free list incorrect", NULL
,
6128 "*** tiny free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6129 index
, function
, szone_check_counter
);
6134 /* check small regions - could check region count */
6135 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6136 region_t small
= szone
->small_region_generation
->hashed_regions
[index
];
6138 if (HASHRING_REGION_DEALLOCATED
== small
)
6142 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6143 REGION_TRAILER_FOR_SMALL_REGION(small
), MAGAZINE_INDEX_FOR_SMALL_REGION(small
));
6145 if (!small_check_region(szone
, small
)) {
6146 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6147 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6148 szone_error(szone
, 1, "check: small region incorrect", NULL
,
6149 "*** small region %ld incorrect szone_check_all(%s) counter=%d\n",
6150 index
, function
, szone_check_counter
);
6153 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6156 /* check small free lists */
6157 for (index
= 0; index
< szone
->num_small_slots
; ++index
) {
6158 if (!small_free_list_check(szone
, index
)) {
6159 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6160 szone_error(szone
, 1, "check: small free list incorrect", NULL
,
6161 "*** small free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6162 index
, function
, szone_check_counter
);
6171 szone_check(szone_t
*szone
)
6173 if ((++szone_check_counter
% 10000) == 0)
6174 _malloc_printf(ASL_LEVEL_NOTICE
, "at szone_check counter=%d\n", szone_check_counter
);
6176 if (szone_check_counter
< szone_check_start
)
6179 if (szone_check_counter
% szone_check_modulo
)
6182 return szone_check_all(szone
, "");
6185 static kern_return_t
6186 szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
6187 memory_reader_t reader
, vm_range_recorder_t recorder
)
6192 if (!reader
) reader
= _szone_default_reader
;
6194 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
6195 if (err
) return err
;
6197 err
= tiny_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
6198 if (err
) return err
;
6200 err
= small_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
6201 if (err
) return err
;
6203 err
= large_in_use_enumerator(task
, context
, type_mask
,
6204 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
6208 // Following method is deprecated: use scalable_zone_statistics instead
6210 scalable_zone_info(malloc_zone_t
*zone
, unsigned *info_to_fill
, unsigned count
)
6212 szone_t
*szone
= (void *)zone
;
6215 // We do not lock to facilitate debug
6220 mag_index_t mag_index
;
6222 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6223 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6224 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6225 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6231 for (t
= 0, u
= 0, mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6232 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6233 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6234 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6240 info
[8] = szone
->num_large_objects_in_use
;
6241 info
[9] = szone
->num_bytes_in_large_objects
;
6243 info
[10] = 0; // DEPRECATED szone->num_huge_entries;
6244 info
[11] = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6246 info
[12] = szone
->debug_flags
;
6248 info
[0] = info
[4] + info
[6] + info
[8] + info
[10];
6249 info
[1] = info
[5] + info
[7] + info
[9] + info
[11];
6251 info
[3] = (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
6252 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ info
[9] + info
[11];
6254 info
[2] = info
[3] - s
;
6255 memcpy(info_to_fill
, info
, sizeof(unsigned)*count
);
6258 // FIXME: consistent picture requires locking!
6259 static NOINLINE
void
6260 szone_print(szone_t
*szone
, boolean_t verbose
)
6266 scalable_zone_info((void *)szone
, info
, 13);
6267 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6268 "Scalable zone %p: inUse=%d(%y) touched=%y allocated=%y flags=%d\n",
6269 szone
, info
[0], info
[1], info
[2], info
[3], info
[12]);
6270 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6271 "\ttiny=%d(%y) small=%d(%y) large=%d(%y) huge=%d(%y)\n",
6272 info
[4], info
[5], info
[6], info
[7], info
[8], info
[9], info
[10], info
[11]);
6274 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6275 "%d tiny regions:\n", szone
->num_tiny_regions
);
6276 if (szone
->num_tiny_regions_dealloc
)
6277 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6278 "[%d tiny regions have been vm_deallocate'd]\n", szone
->num_tiny_regions_dealloc
);
6279 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6280 region
= szone
->tiny_region_generation
->hashed_regions
[index
];
6281 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
6282 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
6283 print_tiny_region(verbose
, region
, (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
6284 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
6288 print_tiny_free_list(szone
);
6290 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6291 "%d small regions:\n", szone
->num_small_regions
);
6292 if (szone
->num_small_regions_dealloc
)
6293 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6294 "[%d small regions have been vm_deallocate'd]\n", szone
->num_small_regions_dealloc
);
6295 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6296 region
= szone
->small_region_generation
->hashed_regions
[index
];
6297 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
6298 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(region
);
6299 print_small_region(szone
, verbose
, region
,
6300 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
6301 szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
6305 print_small_free_list(szone
);
6309 szone_log(malloc_zone_t
*zone
, void *log_address
)
6311 szone_t
*szone
= (szone_t
*)zone
;
6313 szone
->log_address
= log_address
;
6317 szone_force_lock(szone_t
*szone
)
6321 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
6322 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6324 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
])));
6326 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
6327 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[i
])));
6329 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
])));
6335 szone_force_unlock(szone_t
*szone
)
6339 SZONE_UNLOCK(szone
);
6341 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
6342 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
6345 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
6346 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6351 szone_locked(szone_t
*szone
)
6356 tookLock
= SZONE_TRY_LOCK(szone
);
6359 SZONE_UNLOCK(szone
);
6361 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
6362 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->small_magazines
[i
])));
6365 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
6368 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
6369 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6372 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6378 scalable_zone_statistics(malloc_zone_t
*zone
, malloc_statistics_t
*stats
, unsigned subzone
)
6380 szone_t
*szone
= (szone_t
*)zone
;
6388 mag_index_t mag_index
;
6390 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6391 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6392 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6393 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6396 stats
->blocks_in_use
= t
;
6397 stats
->size_in_use
= u
;
6398 stats
->size_allocated
= (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
;
6399 stats
->max_size_in_use
= stats
->size_allocated
- s
;
6407 mag_index_t mag_index
;
6409 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6410 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6411 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6412 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6415 stats
->blocks_in_use
= t
;
6416 stats
->size_in_use
= u
;
6417 stats
->size_allocated
= (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
;
6418 stats
->max_size_in_use
= stats
->size_allocated
- s
;
6422 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
6423 stats
->size_in_use
= szone
->num_bytes_in_large_objects
;
6424 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
6427 stats
->blocks_in_use
= 0; // DEPRECATED szone->num_huge_entries;
6428 stats
->size_in_use
= 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6429 stats
->max_size_in_use
= stats
->size_allocated
= 0;
6436 szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
6443 mag_index_t mag_index
;
6445 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6446 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6447 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6448 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6451 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6452 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6453 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6454 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6457 large
= szone
->num_bytes_in_large_objects
+ 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6459 stats
->blocks_in_use
= t
+ szone
->num_large_objects_in_use
+ 0; // DEPRECATED szone->num_huge_entries;
6460 stats
->size_in_use
= u
+ large
;
6461 stats
->max_size_in_use
= stats
->size_allocated
=
6462 (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
6463 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ large
;
6464 // Now we account for the untouched areas
6465 stats
->max_size_in_use
-= s
;
6469 legacy_zeroing_large_malloc(szone_t
*szone
, size_t size
) {
6470 if (size
> LARGE_THRESHOLD
) // Leopard and earlier returned a ZFOD range, so ...
6471 return szone_calloc(szone
, 1, size
); // Clear to zero always, ham-handedly touching in each page
6473 return szone_malloc(szone
, size
);
6477 legacy_zeroing_large_valloc(szone_t
*szone
, size_t size
) {
6478 void *p
= szone_valloc(szone
, size
);
6480 // Leopard and earlier returned a ZFOD range, so ...
6481 memset(p
, 0, size
); // Clear to zero always, ham-handedly touching in each page
6485 void zeroify_scalable_zone(malloc_zone_t
*zone
)
6487 szone_t
*szone
= (szone_t
*)zone
;
6490 szone
->basic_zone
.malloc
= (void *)legacy_zeroing_large_malloc
;
6491 szone
->basic_zone
.valloc
= (void *)legacy_zeroing_large_valloc
;
6495 static const struct malloc_introspection_t szone_introspect
= {
6496 (void *)szone_ptr_in_use_enumerator
,
6497 (void *)szone_good_size
,
6498 (void *)szone_check
,
6499 (void *)szone_print
,
6501 (void *)szone_force_lock
,
6502 (void *)szone_force_unlock
,
6503 (void *)szone_statistics
,
6504 (void *)szone_locked
,
6505 }; // marked as const to spare the DATA section
6508 create_scalable_zone(size_t initial_size
, unsigned debug_flags
)
6511 uint64_t hw_memsize
= 0;
6512 size_t uint64_t_size
= sizeof(hw_memsize
);
6516 * Sanity-check our build-time assumptions about the size of a page.
6517 * Since we have sized various things assuming the default page size,
6518 * attempting to determine it dynamically is not useful.
6520 if ((vm_page_size
!= _vm_page_size
) || (vm_page_shift
!= _vm_page_shift
)) {
6521 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
6525 #if defined(__i386__) || defined(__x86_64__)
6526 if (_COMM_PAGE_VERSION_REQD
> (*((short *) _COMM_PAGE_VERSION
))) { // _COMM_PAGE_CPU_NUMBER must be present at runtime
6527 malloc_printf("*** ERROR - comm page version mismatch.\n");
6532 /* get memory for the zone, which is now separate from any region.
6533 add guard pages to prevent walking from any other vm allocations
6534 to here and overwriting the function pointers in basic_zone. */
6535 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6539 /* set up the szone structure */
6541 #warning CHECK_REGIONS enabled
6542 debug_flags
|= CHECK_REGIONS
;
6545 #warning LOG enabled
6546 szone
->log_address
= ~0;
6548 szone
->trg
[0].nextgen
= &(szone
->trg
[1]);
6549 szone
->trg
[1].nextgen
= &(szone
->trg
[0]);
6550 szone
->tiny_region_generation
= &(szone
->trg
[0]);
6552 szone
->tiny_region_generation
->hashed_regions
= szone
->initial_tiny_regions
;
6553 szone
->tiny_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
6554 szone
->tiny_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
6556 szone
->srg
[0].nextgen
= &(szone
->srg
[1]);
6557 szone
->srg
[1].nextgen
= &(szone
->srg
[0]);
6558 szone
->small_region_generation
= &(szone
->srg
[0]);
6560 szone
->small_region_generation
->hashed_regions
= szone
->initial_small_regions
;
6561 szone
->small_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
6562 szone
->small_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
6566 * Initialize variables that size the free list for SMALL allocations based
6567 * upon the amount of memory in the system. Switch to a larger number of
6568 * free list entries at 1GB.
6570 if (0 == sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0) &&
6571 hw_memsize
>= (1ULL << 30)) {
6572 szone
->is_largemem
= 1;
6573 szone
->num_small_slots
= NUM_SMALL_SLOTS_LARGEMEM
;
6574 szone
->large_threshold
= LARGE_THRESHOLD_LARGEMEM
;
6575 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD_LARGEMEM
;
6577 szone
->is_largemem
= 0;
6578 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
6579 szone
->large_threshold
= LARGE_THRESHOLD
;
6580 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
6583 szone
->large_entry_cache_hoard_lmit
= hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
6585 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
6586 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
6587 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
6588 szone
->large_legacy_reset_mprotect
= TRUE
;
6590 szone
->large_legacy_reset_mprotect
= FALSE
;
6593 // Initialize the security token.
6595 szone
->cookie
= ((uintptr_t)arc4random() << 32) | (uintptr_t)arc4random();
6597 szone
->cookie
= arc4random();
6600 szone
->basic_zone
.version
= 6;
6601 szone
->basic_zone
.size
= (void *)szone_size
;
6602 szone
->basic_zone
.malloc
= (void *)szone_malloc
;
6603 szone
->basic_zone
.calloc
= (void *)szone_calloc
;
6604 szone
->basic_zone
.valloc
= (void *)szone_valloc
;
6605 szone
->basic_zone
.free
= (void *)szone_free
;
6606 szone
->basic_zone
.realloc
= (void *)szone_realloc
;
6607 szone
->basic_zone
.destroy
= (void *)szone_destroy
;
6608 szone
->basic_zone
.batch_malloc
= (void *)szone_batch_malloc
;
6609 szone
->basic_zone
.batch_free
= (void *)szone_batch_free
;
6610 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
6611 szone
->basic_zone
.memalign
= (void *)szone_memalign
;
6612 szone
->basic_zone
.free_definite_size
= (void *)szone_free_definite_size
;
6613 szone
->debug_flags
= debug_flags
;
6614 LOCK_INIT(szone
->large_szone_lock
);
6616 #if defined(__ppc__) || defined(__ppc64__)
6618 * In the interest of compatibility for PPC applications executing via Rosetta,
6619 * arrange to zero-fill allocations as occurred by side effect in Leopard and earlier.
6621 zeroify_scalable_zone((malloc_zone_t
*)szone
);
6624 if ((err
= pthread_key_create(&(szone
->cpu_id_key
), NULL
))) {
6625 malloc_printf("*** ERROR -pthread_key_create failure err=%d.\n", err
);
6626 szone
->cpu_id_key
= (pthread_key_t
) -1;
6629 // Query the number of configured processors.
6630 // Uniprocessor case gets just one tiny and one small magazine (whose index is zero). This gives
6631 // the same behavior as the original scalable malloc. MP gets per-CPU magazines
6632 // that scale (way) better.
6633 int nproc
= sysconf(_SC_NPROCESSORS_CONF
);
6634 szone
->num_tiny_magazines
= (nproc
> 1) ? MIN(nproc
, TINY_MAX_MAGAZINES
) : 1;
6636 // FIXME vm_allocate() based on number of configured CPUs
6637 magazine_t
*tiny_magazines
= allocate_pages(NULL
, TINY_MAGAZINE_PAGED_SIZE
, 0,
6638 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6639 if (NULL
== tiny_magazines
)
6642 szone
->tiny_magazines
= &(tiny_magazines
[1]); // szone->tiny_magazines[-1] is the Depot
6644 // The magazines are indexed in [0 .. (num_tiny_magazines - 1)]
6645 // Find the smallest power of 2 that exceeds (num_tiny_magazines - 1)
6646 szone
->num_tiny_magazines_mask_shift
= 0;
6648 while( i
<= (szone
->num_tiny_magazines
- 1) ) {
6649 szone
->num_tiny_magazines_mask_shift
++;
6653 // Now if i <= TINY_MAX_MAGAZINES we'll never access tiny_magazines[] out of bounds.
6654 if (i
> TINY_MAX_MAGAZINES
) {
6655 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
6659 // Reduce i by 1 to obtain a mask covering [0 .. (num_tiny_magazines - 1)]
6660 szone
->num_tiny_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
6662 // Init the tiny_magazine locks
6663 LOCK_INIT(szone
->tiny_regions_lock
);
6664 LOCK_INIT(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
6665 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
6666 LOCK_INIT(szone
->tiny_magazines
[i
].magazine_lock
);
6669 szone
->num_small_magazines
= (nproc
> 1) ? MIN(nproc
, SMALL_MAX_MAGAZINES
) : 1;
6671 // FIXME vm_allocate() based on number of configured CPUs
6672 magazine_t
*small_magazines
= allocate_pages(NULL
, SMALL_MAGAZINE_PAGED_SIZE
, 0,
6673 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6674 if (NULL
== small_magazines
)
6677 szone
->small_magazines
= &(small_magazines
[1]); // szone->small_magazines[-1] is the Depot
6679 // The magazines are indexed in [0 .. (num_small_magazines - 1)]
6680 // Find the smallest power of 2 that exceeds (num_small_magazines - 1)
6681 szone
->num_small_magazines_mask_shift
= 0;
6682 while( i
<= (szone
->num_small_magazines
- 1) ) {
6683 szone
->num_small_magazines_mask_shift
++;
6687 // Now if i <= SMALL_MAX_MAGAZINES we'll never access small_magazines[] out of bounds.
6688 if (i
> SMALL_MAX_MAGAZINES
) {
6689 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
6693 // Reduce i by 1 to obtain a mask covering [0 .. (num_small_magazines - 1)]
6694 szone
->num_small_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
6696 // Init the small_magazine locks
6697 LOCK_INIT(szone
->small_regions_lock
);
6698 LOCK_INIT(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
6699 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
6700 LOCK_INIT(szone
->small_magazines
[i
].magazine_lock
);
6703 CHECK(szone
, __PRETTY_FUNCTION__
);
6704 return (malloc_zone_t
*)szone
;
6708 // purgeable zones have their own "large" allocation pool, but share "tiny" and "large"
6709 // heaps with a helper_zone identified in the call to create_purgeable_zone()
6712 purgeable_size(szone_t
*szone
, const void *ptr
)
6714 size_t t
= szone_size_try_large(szone
, ptr
);
6719 return szone_size(szone
->helper_zone
, ptr
);
6723 purgeable_malloc(szone_t
*szone
, size_t size
) {
6724 if (size
<= szone
->large_threshold
)
6725 return szone_malloc(szone
->helper_zone
, size
);
6727 return szone_malloc(szone
, size
);
6731 purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
6733 size_t total_bytes
= num_items
* size
;
6735 // Check for overflow of integer multiplication
6736 if (num_items
> 1) {
6737 #if __LP64__ /* size_t is uint64_t */
6738 if ((num_items
| size
) & 0xffffffff00000000ul
) {
6739 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
6740 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
6741 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
6744 #else /* size_t is uint32_t */
6745 if ((num_items
| size
) & 0xffff0000ul
) {
6746 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
6747 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
6748 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
6754 if (total_bytes
<= szone
->large_threshold
)
6755 return szone_calloc(szone
->helper_zone
, 1, total_bytes
);
6757 return szone_calloc(szone
, 1, total_bytes
);
6761 purgeable_valloc(szone_t
*szone
, size_t size
)
6763 if (size
<= szone
->large_threshold
)
6764 return szone_valloc(szone
->helper_zone
, size
);
6766 return szone_valloc(szone
, size
);
6770 purgeable_free(szone_t
*szone
, void *ptr
)
6772 large_entry_t
*entry
;
6775 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6776 SZONE_UNLOCK(szone
);
6778 return free_large(szone
, ptr
);
6780 return szone_free(szone
->helper_zone
, ptr
);
6785 purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
6787 if (size
<= szone
->large_threshold
)
6788 return szone_free_definite_size(szone
->helper_zone
, ptr
, size
);
6790 return szone_free_definite_size(szone
, ptr
, size
);
6794 purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
6796 if (new_size
<= szone
->large_threshold
)
6797 return szone_realloc(szone
->helper_zone
, ptr
, new_size
);
6799 return szone_realloc(szone
, ptr
, new_size
);
6803 purgeable_destroy(szone_t
*szone
)
6805 /* destroy large entries */
6806 size_t index
= szone
->num_large_entries
;
6807 large_entry_t
*large
;
6808 vm_range_t range_to_deallocate
;
6811 large
= szone
->large_entries
+ index
;
6812 if (large
->address
) {
6813 // we deallocate_pages, including guard pages
6814 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6817 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6818 if (range_to_deallocate
.size
)
6819 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6821 /* Now destroy the separate szone region */
6822 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6826 purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
6828 return szone_batch_malloc(szone
->helper_zone
, size
, results
, count
);
6832 purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
6834 return szone_batch_free(szone
->helper_zone
, to_be_freed
, count
);
6838 purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
6840 if (size
<= szone
->large_threshold
)
6841 return szone_memalign(szone
->helper_zone
, alignment
, size
);
6843 return szone_memalign(szone
, alignment
, size
);
6846 static kern_return_t
6847 purgeable_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
6848 memory_reader_t reader
, vm_range_recorder_t recorder
)
6853 if (!reader
) reader
= _szone_default_reader
;
6855 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
6856 if (err
) return err
;
6858 err
= large_in_use_enumerator(task
, context
, type_mask
,
6859 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
6864 purgeable_good_size(szone_t
*szone
, size_t size
)
6866 if (size
<= szone
->large_threshold
)
6867 return szone_good_size(szone
->helper_zone
, size
);
6869 return szone_good_size(szone
, size
);
6873 purgeable_check(szone_t
*szone
)
6879 purgeable_print(szone_t
*szone
, boolean_t verbose
)
6881 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6882 "Scalable zone %p: inUse=%d(%y) flags=%d\n",
6883 szone
, szone
->num_large_objects_in_use
, szone
->num_bytes_in_large_objects
, szone
->debug_flags
);
6887 purgeable_log(malloc_zone_t
*zone
, void *log_address
)
6889 szone_t
*szone
= (szone_t
*)zone
;
6891 szone
->log_address
= log_address
;
6895 purgeable_force_lock(szone_t
*szone
)
6901 purgeable_force_unlock(szone_t
*szone
)
6903 SZONE_UNLOCK(szone
);
6907 purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
6909 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
6910 stats
->size_in_use
= stats
->max_size_in_use
= stats
->size_allocated
= szone
->num_bytes_in_large_objects
;
6914 purgeable_locked(szone_t
*szone
)
6918 tookLock
= SZONE_TRY_LOCK(szone
);
6921 SZONE_UNLOCK(szone
);
6925 static const struct malloc_introspection_t purgeable_introspect
= {
6926 (void *)purgeable_ptr_in_use_enumerator
,
6927 (void *)purgeable_good_size
,
6928 (void *)purgeable_check
,
6929 (void *)purgeable_print
,
6931 (void *)purgeable_force_lock
,
6932 (void *)purgeable_force_unlock
,
6933 (void *)purgeable_statistics
,
6934 (void *)purgeable_locked
,
6935 }; // marked as const to spare the DATA section
6938 create_purgeable_zone(size_t initial_size
, malloc_zone_t
*malloc_default_zone
, unsigned debug_flags
)
6942 /* get memory for the zone, which is now separate from any region.
6943 add guard pages to prevent walking from any other vm allocations
6944 to here and overwriting the function pointers in basic_zone. */
6945 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6949 /* set up the szone structure */
6951 #warning LOG enabled
6952 szone
->log_address
= ~0;
6955 /* Purgeable zone does not participate in the adaptive "largemem" sizing. */
6956 szone
->is_largemem
= 0;
6957 szone
->large_threshold
= LARGE_THRESHOLD
;
6958 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
6961 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
6962 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
6963 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
6964 szone
->large_legacy_reset_mprotect
= TRUE
;
6966 szone
->large_legacy_reset_mprotect
= FALSE
;
6969 szone
->basic_zone
.version
= 6;
6970 szone
->basic_zone
.size
= (void *)purgeable_size
;
6971 szone
->basic_zone
.malloc
= (void *)purgeable_malloc
;
6972 szone
->basic_zone
.calloc
= (void *)purgeable_calloc
;
6973 szone
->basic_zone
.valloc
= (void *)purgeable_valloc
;
6974 szone
->basic_zone
.free
= (void *)purgeable_free
;
6975 szone
->basic_zone
.realloc
= (void *)purgeable_realloc
;
6976 szone
->basic_zone
.destroy
= (void *)purgeable_destroy
;
6977 szone
->basic_zone
.batch_malloc
= (void *)purgeable_batch_malloc
;
6978 szone
->basic_zone
.batch_free
= (void *)purgeable_batch_free
;
6979 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&purgeable_introspect
;
6980 szone
->basic_zone
.memalign
= (void *)purgeable_memalign
;
6981 szone
->basic_zone
.free_definite_size
= (void *)purgeable_free_definite_size
;
6983 szone
->debug_flags
= debug_flags
| SCALABLE_MALLOC_PURGEABLE
;
6985 /* Purgeable zone does not support SCALABLE_MALLOC_ADD_GUARD_PAGES. */
6986 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
6987 _malloc_printf(ASL_LEVEL_INFO
, "purgeable zone does not support guard pages\n");
6988 szone
->debug_flags
&= ~SCALABLE_MALLOC_ADD_GUARD_PAGES
;
6991 LOCK_INIT(szone
->large_szone_lock
);
6993 szone
->helper_zone
= (struct szone_s
*)malloc_default_zone
;
6995 CHECK(szone
, __PRETTY_FUNCTION__
);
6996 return (malloc_zone_t
*)szone
;
7000 * For use by CheckFix: create a new zone whose behavior is, apart from
7001 * the use of death-row and per-CPU magazines, that of Leopard.
7003 static NOINLINE
void *
7004 legacy_valloc(szone_t
*szone
, size_t size
)
7009 num_pages
= round_page(size
) >> vm_page_shift
;
7010 ptr
= large_malloc(szone
, num_pages
, 0, TRUE
);
7012 if (LOG(szone
, ptr
))
7013 malloc_printf("legacy_valloc returned %p\n", ptr
);
7019 create_legacy_scalable_zone(size_t initial_size
, unsigned debug_flags
)
7021 malloc_zone_t
*mzone
= create_scalable_zone(initial_size
, debug_flags
);
7022 szone_t
*szone
= (szone_t
*)mzone
;
7027 szone
->is_largemem
= 0;
7028 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
7029 szone
->large_threshold
= LARGE_THRESHOLD
;
7030 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7032 szone
->basic_zone
.valloc
= (void *)legacy_valloc
;
7033 szone
->basic_zone
.free_definite_size
= NULL
;
7038 /********* Support code for emacs unexec ************/
7040 /* History of freezedry version numbers:
7042 * 1) Old malloc (before the scalable malloc implementation in this file
7044 * 2) Original freezedrying code for scalable malloc. This code was apparently
7045 * based on the old freezedrying code and was fundamentally flawed in its
7046 * assumption that tracking allocated memory regions was adequate to fake
7047 * operations on freezedried memory. This doesn't work, since scalable
7048 * malloc does not store flags in front of large page-aligned allocations.
7049 * 3) Original szone-based freezedrying code.
7050 * 4) Fresher malloc with tiny zone
7051 * 5) 32/64bit compatible malloc
7052 * 6) Metadata within 1MB and 8MB region for tiny and small
7054 * No version backward compatibility is provided, but the version number does
7055 * make it possible for malloc_jumpstart() to return an error if the application
7056 * was freezedried with an older version of malloc.
7058 #define MALLOC_FREEZEDRY_VERSION 6
7067 frozen_malloc(szone_t
*zone
, size_t new_size
)
7069 return malloc(new_size
);
7073 frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
)
7075 return calloc(num_items
, size
);
7079 frozen_valloc(szone_t
*zone
, size_t new_size
)
7081 return valloc(new_size
);
7085 frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
)
7087 size_t old_size
= szone_size(zone
, ptr
);
7090 if (new_size
<= old_size
) {
7093 new_ptr
= malloc(new_size
);
7095 memcpy(new_ptr
, ptr
, old_size
);
7101 frozen_free(szone_t
*zone
, void *ptr
)
7106 frozen_destroy(szone_t
*zone
)
7110 /********* Pseudo-private API for emacs unexec ************/
7113 * malloc_freezedry() records all of the szones in use, so that they can be
7114 * partially reconstituted by malloc_jumpstart(). Due to the differences
7115 * between reconstituted memory regions and those created by the szone code,
7116 * care is taken not to reallocate from the freezedried memory, except in the
7117 * case of a non-growing realloc().
7119 * Due to the flexibility provided by the zone registration mechanism, it is
7120 * impossible to implement generic freezedrying for any zone type. This code
7121 * only handles applications that use the szone allocator, so malloc_freezedry()
7122 * returns 0 (error) if any non-szone zones are encountered.
7126 malloc_freezedry(void)
7128 extern unsigned malloc_num_zones
;
7129 extern malloc_zone_t
**malloc_zones
;
7130 malloc_frozen
*data
;
7133 /* Allocate space in which to store the freezedry state. */
7134 data
= (malloc_frozen
*) malloc(sizeof(malloc_frozen
));
7136 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
7137 data
->version
= MALLOC_FREEZEDRY_VERSION
;
7139 /* Allocate the array of szone pointers. */
7140 data
->nszones
= malloc_num_zones
;
7141 data
->szones
= (szone_t
*) calloc(malloc_num_zones
, sizeof(szone_t
));
7144 * Fill in the array of szone structures. They are copied rather than
7145 * referenced, since the originals are likely to be clobbered during malloc
7148 for (i
= 0; i
< malloc_num_zones
; i
++) {
7149 if (strcmp(malloc_zones
[i
]->zone_name
, "DefaultMallocZone")) {
7150 /* Unknown zone type. */
7155 memcpy(&data
->szones
[i
], malloc_zones
[i
], sizeof(szone_t
));
7158 return((uintptr_t)data
);
7162 malloc_jumpstart(uintptr_t cookie
)
7164 malloc_frozen
*data
= (malloc_frozen
*)cookie
;
7167 if (data
->version
!= MALLOC_FREEZEDRY_VERSION
) {
7168 /* Unsupported freezedry version. */
7172 for (i
= 0; i
< data
->nszones
; i
++) {
7173 /* Set function pointers. Even the functions that stay the same must be
7174 * set, since there are no guarantees that they will be mapped to the
7175 * same addresses. */
7176 data
->szones
[i
].basic_zone
.size
= (void *) szone_size
;
7177 data
->szones
[i
].basic_zone
.malloc
= (void *) frozen_malloc
;
7178 data
->szones
[i
].basic_zone
.calloc
= (void *) frozen_calloc
;
7179 data
->szones
[i
].basic_zone
.valloc
= (void *) frozen_valloc
;
7180 data
->szones
[i
].basic_zone
.free
= (void *) frozen_free
;
7181 data
->szones
[i
].basic_zone
.realloc
= (void *) frozen_realloc
;
7182 data
->szones
[i
].basic_zone
.destroy
= (void *) frozen_destroy
;
7183 data
->szones
[i
].basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
7185 /* Register the freezedried zone. */
7186 malloc_zone_register(&data
->szones
[i
].basic_zone
);