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);
1065 #warning deriving magazine index from pthread_self() [want processor number]
1067 static INLINE mag_index_t
1068 mag_get_thread_index(szone_t
*szone
)
1072 else if ((pthread_key_t
) -1 == szone
->cpu_id_key
) { // In case pthread_key_create() failed.
1075 mag_index_t idx
= (mag_index_t
)(intptr_t)pthread_getspecific(szone
->cpu_id_key
);
1077 // Has this thread been hinted with a non-zero value [i.e. 1 + cpuid()] ?
1078 // If so, bump down the hint to a zero-based magazine index and return it.
1082 // No hint available. Contruct a magazine index for this thread ...
1085 // bump up the hint to exclude zero and try to memorize it ...
1086 pthread_setspecific(szone
->cpu_id_key
, (const void *)((uintptr_t)idx
+ 1));
1088 // and return the (zero-based) magazine index.
1096 mag_lock_zine_for_region_trailer(szone_t
*szone
, magazine_t
*magazines
, region_trailer_t
*trailer
, mag_index_t mag_index
)
1098 mag_index_t refreshed_index
;
1099 magazine_t
*mag_ptr
= &(magazines
[mag_index
]);
1101 // Take the lock on entry.
1102 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1104 // Now in the time it took to acquire the lock, the region may have migrated
1105 // from one magazine to another. In which case the magazine lock we obtained
1106 // (namely magazines[mag_index].mag_lock) is stale. If so, keep on tryin' ...
1107 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
1109 SZONE_MAGAZINE_PTR_UNLOCK(szone
, mag_ptr
);
1111 mag_index
= refreshed_index
;
1112 mag_ptr
= &(magazines
[mag_index
]);
1113 SZONE_MAGAZINE_PTR_LOCK(szone
, mag_ptr
);
1119 /*******************************************************************************
1120 * Region hash implementation
1122 * This is essentially a duplicate of the existing Large allocator hash, minus
1123 * the ability to remove entries. The two should be combined eventually.
1124 ******************************************************************************/
1125 #pragma mark region hash
1128 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
1131 * FIXME: If consecutive queries of the same region are likely, a one-entry
1132 * cache would likely be a significant performance win here.
1134 static INLINE rgnhdl_t
1135 hash_lookup_region_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1136 size_t index
, hash_index
;
1142 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1143 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1144 // this hash works really well. See Knuth TAOCP, Vol. 3.
1146 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1148 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1151 entry
= regions
+ index
;
1156 if (++index
== num_entries
)
1158 } while (index
!= hash_index
);
1163 * hash_region_insert_no_lock - Insert a region into the hash ring.
1166 hash_region_insert_no_lock(region_t
*regions
, size_t num_entries
, size_t shift
, region_t r
) {
1167 size_t index
, hash_index
;
1170 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1171 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1172 // this hash works really well. See Knuth TAOCP, Vol. 3.
1174 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 11400714819323198549ULL) >> (64 - shift
);
1176 index
= hash_index
= (((uintptr_t)r
>> HASH_BLOCKS_ALIGN
) * 2654435761UL) >> (32 - shift
);
1179 entry
= regions
+ index
;
1180 if (*entry
== HASHRING_OPEN_ENTRY
|| *entry
== HASHRING_REGION_DEALLOCATED
) {
1184 if (++index
== num_entries
)
1186 } while (index
!= hash_index
);
1190 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
1191 * must be a VM allocation as to avoid recursing between allocating a new small
1192 * region, and asking the small region to allocate space for the new list of
1196 hash_regions_alloc_no_lock(szone_t
*szone
, size_t num_entries
)
1198 size_t size
= num_entries
* sizeof(region_t
);
1200 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC
);
1204 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
1205 * Return the new region and new size to update the szone. Do not deallocate
1206 * the old entries since someone may still be allocating them.
1209 hash_regions_grow_no_lock(szone_t
*szone
, region_t
*regions
, size_t old_size
, size_t *mutable_shift
,
1212 // double in size and allocate memory for the regions
1213 *new_size
= old_size
+ old_size
;
1214 *mutable_shift
= *mutable_shift
+ 1;
1215 region_t
*new_regions
= hash_regions_alloc_no_lock(szone
, *new_size
);
1217 // rehash the entries into the new list
1219 for (index
= 0; index
< old_size
; ++index
) {
1220 region_t r
= regions
[index
];
1221 if (r
!= HASHRING_OPEN_ENTRY
&& r
!= HASHRING_REGION_DEALLOCATED
)
1222 hash_region_insert_no_lock(new_regions
, *new_size
, *mutable_shift
, r
);
1227 /********************* FREE LIST UTILITIES ************************/
1229 // A free list entry is comprised of a pair of pointers, previous and next.
1230 // These are used to implement a doubly-linked list, which permits efficient
1233 // Because the free list entries are previously freed objects, a misbehaved
1234 // program may write to a pointer after it has called free() on that pointer,
1235 // either by dereference or buffer overflow from an adjacent pointer. This write
1236 // would then corrupt the free list's previous and next pointers, leading to a
1237 // crash. In order to detect this case, we take advantage of the fact that
1238 // malloc'd pointers are known to be at least 16 byte aligned, and thus have
1239 // at least 4 trailing zero bits.
1241 // When an entry is added to the free list, a checksum of the previous and next
1242 // pointers is calculated and written to the low four bits of the respective
1243 // pointers. Upon detection of an invalid checksum, an error is logged and NULL
1244 // is returned. Since all code which un-checksums pointers checks for a NULL
1245 // return, a potentially crashing or malicious dereference is avoided at the
1246 // cost of leaking the corrupted block, and any subsequent blocks on the free
1247 // list of that size.
1249 static NOINLINE
void
1250 free_list_checksum_botch(szone_t
*szone
, free_list_t
*ptr
)
1252 szone_error(szone
, 1, "incorrect checksum for freed object "
1253 "- object was probably modified after being freed.", ptr
, NULL
);
1256 static INLINE
uintptr_t free_list_gen_checksum(uintptr_t ptr
)
1260 chk
= (unsigned char)(ptr
>> 0);
1261 chk
+= (unsigned char)(ptr
>> 8);
1262 chk
+= (unsigned char)(ptr
>> 16);
1263 chk
+= (unsigned char)(ptr
>> 24);
1265 chk
+= (unsigned char)(ptr
>> 32);
1266 chk
+= (unsigned char)(ptr
>> 40);
1267 chk
+= (unsigned char)(ptr
>> 48);
1268 chk
+= (unsigned char)(ptr
>> 56);
1271 return chk
& (uintptr_t)0xF;
1274 static INLINE
uintptr_t
1275 free_list_checksum_ptr(szone_t
*szone
, void *ptr
)
1277 uintptr_t p
= (uintptr_t)ptr
;
1278 return p
| free_list_gen_checksum(p
^ szone
->cookie
);
1281 static INLINE
void *
1282 free_list_unchecksum_ptr(szone_t
*szone
, ptr_union
*ptr
)
1285 p
.u
= (ptr
->u
>> 4) << 4;
1287 if ((ptr
->u
& (uintptr_t)0xF) != free_list_gen_checksum(p
.u
^ szone
->cookie
))
1289 free_list_checksum_botch(szone
, (free_list_t
*)ptr
);
1296 free_list_count(szone_t
*szone
, free_list_t
*ptr
)
1302 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
1308 recirc_list_extract(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1310 // excise node from list
1311 if (NULL
== node
->prev
)
1312 mag_ptr
->firstNode
= node
->next
;
1314 node
->prev
->next
= node
->next
;
1316 if (NULL
== node
->next
)
1317 mag_ptr
->lastNode
= node
->prev
;
1319 node
->next
->prev
= node
->prev
;
1321 mag_ptr
->recirculation_entries
--;
1325 recirc_list_splice_last(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1327 if (NULL
== mag_ptr
->lastNode
) {
1328 mag_ptr
->firstNode
= node
;
1331 node
->prev
= mag_ptr
->lastNode
;
1332 mag_ptr
->lastNode
->next
= node
;
1334 mag_ptr
->lastNode
= node
;
1336 node
->recirc_suitable
= FALSE
;
1337 mag_ptr
->recirculation_entries
++;
1341 recirc_list_splice_first(szone_t
*szone
, magazine_t
*mag_ptr
, region_trailer_t
*node
)
1343 if (NULL
== mag_ptr
->firstNode
) {
1344 mag_ptr
->lastNode
= node
;
1347 node
->next
= mag_ptr
->firstNode
;
1348 mag_ptr
->firstNode
->prev
= node
;
1350 mag_ptr
->firstNode
= node
;
1352 node
->recirc_suitable
= FALSE
;
1353 mag_ptr
->recirculation_entries
++;
1356 /* Macros used to manipulate the uint32_t quantity mag_bitmap. */
1358 /* BITMAPV variants are used by tiny. */
1359 #if defined(__LP64__)
1360 // assert(NUM_SLOTS == 64) in which case (slot >> 5) is either 0 or 1
1361 #define BITMAPV_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1362 #define BITMAPV_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1363 #define BITMAPV_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1364 #define BITMAPV_CTZ(bitmap) (__builtin_ctzl(bitmap))
1366 // assert(NUM_SLOTS == 32) in which case (slot >> 5) is always 0, so code it that way
1367 #define BITMAPV_SET(bitmap,slot) (bitmap[0] |= 1 << (slot))
1368 #define BITMAPV_CLR(bitmap,slot) (bitmap[0] &= ~ (1 << (slot)))
1369 #define BITMAPV_BIT(bitmap,slot) ((bitmap[0] >> (slot)) & 1)
1370 #define BITMAPV_CTZ(bitmap) (__builtin_ctz(bitmap))
1373 /* BITMAPN is used by small. (slot >> 5) takes on values from 0 to 7. */
1374 #define BITMAPN_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1375 #define BITMAPN_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1376 #define BITMAPN_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1378 /* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
1379 #define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap[0]))
1381 /********************* TINY FREE LIST UTILITIES ************************/
1383 // We encode the meta-headers as follows:
1384 // Each quantum has an associated set of 2 bits:
1385 // block_header when 1 says this block is the beginning of a block
1386 // in_use when 1 says this block is in use
1387 // so a block in use of size 3 is 1-1 0-X 0-X
1388 // for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
1389 // for a block middle the bits are 0-0
1391 // We store the meta-header bit arrays by interleaving them 32 bits at a time.
1392 // Initial 32 bits of block_header, followed by initial 32 bits of in_use, followed
1393 // by next 32 bits of block_header, followed by next 32 bits of in_use, etc.
1394 // This localizes memory references thereby reducing cache and TLB pressures.
1397 BITARRAY_SET(uint32_t *bits
, msize_t index
)
1399 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1400 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1401 bits
[(index
>> 5) << 1] |= (1 << (index
& 31));
1405 BITARRAY_CLR(uint32_t *bits
, msize_t index
)
1407 bits
[(index
>> 5) << 1] &= ~(1 << (index
& 31));
1410 static INLINE boolean_t
1411 BITARRAY_BIT(uint32_t *bits
, msize_t index
)
1413 return ((bits
[(index
>> 5) << 1]) >> (index
& 31)) & 1;
1417 static INLINE
void bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
) ALWAYSINLINE
;
1420 bitarray_mclr(uint32_t *bits
, unsigned start
, unsigned end
)
1422 // start >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1423 // (start >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1424 uint32_t *addr
= bits
+ ((start
>> 5) << 1);
1426 uint32_t span
= end
- start
;
1431 addr
[0] &= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1432 addr
[2] &= (0xFFFFFFFFU
<< (end
- 32));
1434 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1435 mask
|= (0xFFFFFFFFU
<< end
);
1442 * Obtain the size of a free tiny block (in msize_t units).
1445 get_tiny_free_size(const void *ptr
)
1447 void *next_block
= (void *)((uintptr_t)ptr
+ TINY_QUANTUM
);
1448 void *region_end
= TINY_REGION_END(TINY_REGION_FOR_PTR(ptr
));
1450 // check whether the next block is outside the tiny region or a block header
1451 // if so, then the size of this block is one, and there is no stored size.
1452 if (next_block
< region_end
)
1454 uint32_t *next_header
= TINY_BLOCK_HEADER_FOR_PTR(next_block
);
1455 msize_t next_index
= TINY_INDEX_FOR_PTR(next_block
);
1457 if (!BITARRAY_BIT(next_header
, next_index
))
1458 return TINY_FREE_SIZE(ptr
);
1464 * Get the size of the previous free block, which is stored in the last two
1465 * bytes of the block. If the previous block is not free, then the result is
1469 get_tiny_previous_free_msize(const void *ptr
)
1471 // check whether the previous block is in the tiny region and a block header
1472 // if so, then the size of the previous block is one, and there is no stored
1474 if (ptr
!= TINY_REGION_FOR_PTR(ptr
))
1476 void *prev_block
= (void *)((uintptr_t)ptr
- TINY_QUANTUM
);
1477 uint32_t *prev_header
= TINY_BLOCK_HEADER_FOR_PTR(prev_block
);
1478 msize_t prev_index
= TINY_INDEX_FOR_PTR(prev_block
);
1479 if (BITARRAY_BIT(prev_header
, prev_index
))
1481 return TINY_PREVIOUS_MSIZE(ptr
);
1483 // don't read possibly unmapped memory before the beginning of the region
1487 static INLINE msize_t
1488 get_tiny_meta_header(const void *ptr
, boolean_t
*is_free
)
1490 // returns msize and is_free
1491 // may return 0 for the msize component (meaning 65536)
1492 uint32_t *block_header
;
1495 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1496 index
= TINY_INDEX_FOR_PTR(ptr
);
1498 msize_t midx
= (index
>> 5) << 1;
1499 uint32_t mask
= 1 << (index
& 31);
1501 if (0 == (block_header
[midx
] & mask
)) // if (!BITARRAY_BIT(block_header, index))
1503 if (0 == (block_header
[midx
+ 1] & mask
)) { // if (!BITARRAY_BIT(in_use, index))
1505 return get_tiny_free_size(ptr
);
1508 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1509 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1510 #if defined(__LP64__)
1511 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1512 // That's guaranteed to be somewhwere in the next 64 bits. And those bits could span three
1513 // uint32_t block_header elements. Collect the bits into a single uint64_t and measure up with ffsl.
1514 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1515 uint32_t bitidx
= index
& 31;
1516 uint64_t word_lo
= addr
[0];
1517 uint64_t word_mid
= addr
[2];
1518 uint64_t word_hi
= addr
[4];
1519 uint64_t word_lomid
= (word_lo
>> bitidx
) | (word_mid
<< (32 - bitidx
));
1520 uint64_t word
= bitidx
? word_lomid
| (word_hi
<< (64 - bitidx
)) : word_lomid
;
1521 uint32_t result
= __builtin_ffsl(word
>> 1);
1523 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1524 // That's guaranteed to be somwhwere in the next 32 bits. And those bits could span two
1525 // uint32_t block_header elements. Collect the bits into a single uint32_t and measure up with ffs.
1526 uint32_t *addr
= ((uint32_t *)block_header
) + ((index
>> 5) << 1);
1527 uint32_t bitidx
= index
& 31;
1528 uint32_t word
= bitidx
? (addr
[0] >> bitidx
) | (addr
[2] << (32 - bitidx
)) : addr
[0];
1529 uint32_t result
= __builtin_ffs(word
>> 1);
1535 set_tiny_meta_header_in_use(const void *ptr
, msize_t msize
)
1537 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1538 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1539 msize_t clr_msize
= msize
- 1;
1540 msize_t midx
= (index
>> 5) << 1;
1541 uint32_t val
= (1 << (index
& 31));
1544 if (msize
>= NUM_TINY_SLOTS
)
1545 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr
, msize
);
1546 if ((unsigned)index
+ (unsigned)msize
> 0x10000)
1547 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr
, msize
);
1550 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1551 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1553 // bitarray_mclr(block_header, index, end_bit);
1554 // bitarray_mclr(in_use, index, end_bit);
1557 midx
= (index
>> 5) << 1;
1559 unsigned start
= index
& 31;
1560 unsigned end
= start
+ clr_msize
;
1562 #if defined(__LP64__)
1564 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1565 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 64));
1566 block_header
[midx
+ 0] &= mask0
; // clear header
1567 block_header
[midx
+ 1] &= mask0
; // clear in_use
1568 block_header
[midx
+ 2] = 0; // clear header
1569 block_header
[midx
+ 3] = 0; // clear in_use
1570 block_header
[midx
+ 4] &= mask1
; // clear header
1571 block_header
[midx
+ 5] &= mask1
; // clear in_use
1575 unsigned mask0
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1576 unsigned mask1
= (0xFFFFFFFFU
<< (end
- 32));
1577 block_header
[midx
+ 0] &= mask0
;
1578 block_header
[midx
+ 1] &= mask0
;
1579 block_header
[midx
+ 2] &= mask1
;
1580 block_header
[midx
+ 3] &= mask1
;
1582 unsigned mask
= (0xFFFFFFFFU
>> (31 - start
)) >> 1;
1583 mask
|= (0xFFFFFFFFU
<< end
);
1584 block_header
[midx
+ 0] &= mask
;
1585 block_header
[midx
+ 1] &= mask
;
1588 // we set the block_header bit for the following block to reaffirm next block is a block
1590 midx
= (index
>> 5) << 1;
1591 val
= (1 << (index
& 31));
1592 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize));
1598 mf
= get_tiny_meta_header(ptr
, &ff
);
1600 malloc_printf("setting header for tiny in_use %p : %d\n", ptr
, msize
);
1601 malloc_printf("reading header for tiny %p : %d %d\n", ptr
, mf
, ff
);
1608 set_tiny_meta_header_in_use_1(const void *ptr
) // As above with msize == 1
1610 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1611 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1612 msize_t midx
= (index
>> 5) << 1;
1613 uint32_t val
= (1 << (index
& 31));
1615 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1616 block_header
[midx
+ 1] |= val
; // BITARRAY_SET(in_use, index);
1619 midx
= (index
>> 5) << 1;
1620 val
= (1 << (index
& 31));
1622 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, (index+clr_msize))
1626 set_tiny_meta_header_middle(const void *ptr
)
1628 // indicates this block is in the middle of an in use block
1629 uint32_t *block_header
;
1633 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1634 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1635 index
= TINY_INDEX_FOR_PTR(ptr
);
1637 BITARRAY_CLR(block_header
, index
);
1638 BITARRAY_CLR(in_use
, index
);
1642 set_tiny_meta_header_free(const void *ptr
, msize_t msize
)
1644 // !msize is acceptable and means 65536
1645 uint32_t *block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1646 msize_t index
= TINY_INDEX_FOR_PTR(ptr
);
1647 msize_t midx
= (index
>> 5) << 1;
1648 uint32_t val
= (1 << (index
& 31));
1651 if ((unsigned)index
+ (unsigned)msize
> 0x10000) {
1652 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr
, msize
);
1656 block_header
[midx
] |= val
; // BITARRAY_SET(block_header, index);
1657 block_header
[midx
+ 1] &= ~val
; // BITARRAY_CLR(in_use, index);
1659 // mark the end of this block if msize is > 1. For msize == 0, the whole
1660 // region is free, so there is no following block. For msize == 1, there is
1661 // no space to write the size on 64 bit systems. The size for 1 quantum
1662 // blocks is computed from the metadata bitmaps.
1664 void *follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
1665 TINY_PREVIOUS_MSIZE(follower
) = msize
;
1666 TINY_FREE_SIZE(ptr
) = msize
;
1669 TINY_FREE_SIZE(ptr
) = msize
;
1673 msize_t mf
= get_tiny_meta_header(ptr
, &ff
);
1674 if ((msize
!= mf
) || !ff
) {
1675 malloc_printf("setting header for tiny free %p : %u\n", ptr
, msize
);
1676 malloc_printf("reading header for tiny %p : %u %u\n", ptr
, mf
, ff
);
1681 static INLINE boolean_t
1682 tiny_meta_header_is_free(const void *ptr
)
1684 uint32_t *block_header
;
1688 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1689 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1690 index
= TINY_INDEX_FOR_PTR(ptr
);
1691 if (!BITARRAY_BIT(block_header
, index
))
1693 return !BITARRAY_BIT(in_use
, index
);
1696 static INLINE
void *
1697 tiny_previous_preceding_free(void *ptr
, msize_t
*prev_msize
)
1699 // returns the previous block, assuming and verifying it's free
1700 uint32_t *block_header
;
1703 msize_t previous_msize
;
1704 msize_t previous_index
;
1707 block_header
= TINY_BLOCK_HEADER_FOR_PTR(ptr
);
1708 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
1709 index
= TINY_INDEX_FOR_PTR(ptr
);
1713 if ((previous_msize
= get_tiny_previous_free_msize(ptr
)) > index
)
1716 previous_index
= index
- previous_msize
;
1717 previous_ptr
= (void *)((uintptr_t)TINY_REGION_FOR_PTR(ptr
) + TINY_BYTES_FOR_MSIZE(previous_index
));
1718 if (!BITARRAY_BIT(block_header
, previous_index
))
1720 if (BITARRAY_BIT(in_use
, previous_index
))
1722 if (get_tiny_free_size(previous_ptr
) != previous_msize
)
1725 // conservative check did match true check
1726 *prev_msize
= previous_msize
;
1727 return previous_ptr
;
1731 * Adds an item to the proper free list, and also marks the meta-header of the
1733 * Assumes szone has been locked
1736 tiny_free_list_add_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1738 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1739 free_list_t
*free_ptr
= ptr
;
1740 free_list_t
*free_head
= tiny_mag_ptr
->mag_free_list
[slot
];
1743 if (LOG(szone
,ptr
)) {
1744 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1746 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
1747 szone_error(szone
, 1, "tiny_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
1750 set_tiny_meta_header_free(ptr
, msize
);
1753 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
1754 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
1755 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
1757 if (! tiny_meta_header_is_free(free_head
)) {
1758 szone_error(szone
, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
1759 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
1762 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
1764 BITMAPV_SET(tiny_mag_ptr
->mag_bitmap
, slot
);
1766 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
1767 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
1769 tiny_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
1773 * Removes the item pointed to by ptr in the proper free list.
1774 * Assumes szone has been locked
1777 tiny_free_list_remove_ptr(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, void *ptr
, msize_t msize
)
1779 grain_t slot
= (!msize
|| (msize
>= NUM_TINY_SLOTS
)) ? NUM_TINY_SLOTS
- 1 : msize
- 1;
1780 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
1782 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
1783 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
1786 if (LOG(szone
,ptr
)) {
1787 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
1791 // The block to remove is the head of the free list
1793 if (tiny_mag_ptr
->mag_free_list
[slot
] != ptr
) {
1794 szone_error(szone
, 1, "tiny_free_list_remove_ptr: Internal invariant broken (tiny_mag_ptr->mag_free_list[slot])", ptr
,
1795 "ptr=%p slot=%d msize=%d tiny_mag_ptr->mag_free_list[slot]=%p\n",
1796 ptr
, slot
, msize
, (void *)tiny_mag_ptr
->mag_free_list
[slot
]);
1800 tiny_mag_ptr
->mag_free_list
[slot
] = next
;
1801 if (!next
) BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
1803 // We know free_ptr is already checksummed, so we don't need to do it
1805 previous
->next
= free_ptr
->next
;
1808 // We know free_ptr is already checksummed, so we don't need to do it
1810 next
->previous
= free_ptr
->previous
;
1815 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
1816 * or NULL if not found.
1818 static INLINE region_t
1819 tiny_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
1821 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
1822 szone
->tiny_region_generation
->num_regions_allocated
,
1823 szone
->tiny_region_generation
->num_regions_allocated_shift
,
1824 TINY_REGION_FOR_PTR(ptr
));
1829 tiny_finalize_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
) {
1830 void *last_block
, *previous_block
;
1831 uint32_t *last_header
;
1832 msize_t last_msize
, previous_msize
, last_index
;
1834 last_block
= (void *)
1835 ((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) - tiny_mag_ptr
->mag_bytes_free_at_end
);
1836 last_msize
= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
1837 last_header
= TINY_BLOCK_HEADER_FOR_PTR(last_block
);
1838 last_index
= TINY_INDEX_FOR_PTR(last_block
);
1840 // Before anything we transform any remaining mag_bytes_free_at_end into a
1841 // regular free block. We take special care here to update the bitfield
1842 // information, since we are bypassing the normal free codepath. If there
1843 // is more than one quanta worth of memory in mag_bytes_free_at_end, then
1844 // there will be two block headers:
1845 // 1) header for the free space at end, msize = 1
1846 // 2) header inserted by set_tiny_meta_header_in_use after block
1847 // We must clear the second one so that when the free block's size is
1848 // queried, we do not think the block is only 1 quantum in size because
1849 // of the second set header bit.
1850 if (last_index
!= (NUM_TINY_BLOCKS
- 1))
1851 BITARRAY_CLR(last_header
, (last_index
+ 1));
1853 // It is possible that the block prior to the last block in the region has
1854 // been free'd, but was not coalesced with the free bytes at the end of the
1855 // block, since we treat the bytes at the end of the region as "in use" in
1856 // the meta headers. Attempt to coalesce the last block with the previous
1857 // block, so we don't violate the "no consecutive free blocks" invariant.
1859 // FIXME: Need to investigate how much work would be required to increase
1860 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
1861 // than performing this workaround.
1863 previous_block
= tiny_previous_preceding_free(last_block
, &previous_msize
);
1864 if (previous_block
) {
1865 set_tiny_meta_header_middle(last_block
);
1866 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous_block
, previous_msize
);
1867 last_block
= previous_block
;
1868 last_msize
+= previous_msize
;
1871 // splice last_block into the free list
1872 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, last_block
, last_msize
);
1873 tiny_mag_ptr
->mag_bytes_free_at_end
= 0;
1874 tiny_mag_ptr
->mag_last_region
= NULL
;
1878 tiny_free_detach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
1879 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1880 uintptr_t current
= start
;
1881 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1884 int total_alloc
= 0;
1886 while (current
< limit
) {
1887 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1888 if (is_free
&& !msize
&& (current
== start
)) {
1889 // first block is all free
1894 malloc_printf("*** tiny_free_detach_region error with %p: msize=%d is_free =%d\n",
1895 (void *)current
, msize
, is_free
);
1900 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
1904 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1910 tiny_free_reattach_region(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, region_t r
) {
1911 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1912 uintptr_t current
= start
;
1913 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1916 size_t total_alloc
= 0;
1918 while (current
< limit
) {
1919 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1920 if (is_free
&& !msize
&& (current
== start
)) {
1921 // first block is all free
1926 malloc_printf("*** tiny_free_reattach_region error with %p: msize=%d is_free =%d\n",
1927 (void *)current
, msize
, is_free
);
1932 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, (void *)current
, msize
);
1934 total_alloc
+= TINY_BYTES_FOR_MSIZE(msize
);
1936 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1942 tiny_free_scan_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
1943 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(r
);
1944 uintptr_t current
= start
;
1945 uintptr_t limit
= (uintptr_t)TINY_REGION_END(r
);
1948 boolean_t did_advise
= FALSE
;
1950 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
1952 while (current
< limit
) {
1953 msize
= get_tiny_meta_header((void *)current
, &is_free
);
1954 if (is_free
&& !msize
&& (current
== start
)) {
1955 // first block is all free
1957 malloc_printf("*** tiny_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
1958 (void *)current
, msize
, is_free
);
1960 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
1961 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
1964 madvise_free_range(szone
, r
, pgLo
, pgHi
);
1971 malloc_printf("*** tiny_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
1972 (void *)current
, msize
, is_free
);
1977 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
1978 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
1981 madvise_free_range(szone
, r
, pgLo
, pgHi
);
1985 current
+= TINY_BYTES_FOR_MSIZE(msize
);
1989 /* Move the node to the tail of the Deopt's recirculation list to delay its re-use. */
1990 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(r
);
1991 recirc_list_extract(szone
, depot_ptr
, node
); // excise node from list
1992 recirc_list_splice_last(szone
, depot_ptr
, node
); // connect to magazine as last node
1997 tiny_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
1999 #warning Tune Depot headroom
2000 if (0 < node
->bytes_used
||
2001 depot_ptr
->recirculation_entries
< (szone
->num_tiny_magazines
* 2)) {
2005 // disconnect node from Depot
2006 recirc_list_extract(szone
, depot_ptr
, node
);
2008 // Iterate the region pulling its free entries off the (locked) Depot's free list
2009 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2010 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2012 if (0 == objects_in_use
) {
2013 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
2014 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
2015 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2016 szone
->tiny_region_generation
->num_regions_allocated
,
2017 szone
->tiny_region_generation
->num_regions_allocated_shift
, sparse_region
);
2018 *pSlot
= HASHRING_REGION_DEALLOCATED
;
2019 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2020 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
2021 __sync_fetch_and_add( &(szone
->num_tiny_regions_dealloc
), 1); // Atomically increment num_tiny_regions_dealloc
2024 OSAtomicIncrement64( (volatile int64_t *)&(szone
->num_tiny_regions_dealloc
) );
2026 OSAtomicIncrement32( (volatile int32_t *)&(szone
->num_tiny_regions_dealloc
) );
2030 // Transfer ownership of the region back to the OS
2031 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
); // Avoid denial of service to Depot while in kernel
2032 deallocate_pages(szone
, sparse_region
, TINY_REGION_SIZE
, 0);
2033 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
2035 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
); // DTrace USDT Probe
2038 szone_error(szone
, 1, "tiny_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
2043 tiny_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2045 // The entire magazine crossed the "emptiness threshold". Transfer a region
2046 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2047 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2048 region_trailer_t
*node
= tiny_mag_ptr
->firstNode
;
2050 while (node
&& !node
->recirc_suitable
) {
2056 malloc_printf("*** tiny_free_do_recirc_to_depot end of list\n");
2061 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2063 // Deal with unclaimed memory -- mag_bytes_free_at_end
2064 if (sparse_region
== tiny_mag_ptr
->mag_last_region
&& tiny_mag_ptr
->mag_bytes_free_at_end
) {
2065 tiny_finalize_region(szone
, tiny_mag_ptr
);
2068 // disconnect "suitable" node from magazine
2069 recirc_list_extract(szone
, tiny_mag_ptr
, node
);
2071 // Iterate the region pulling its free entries off its (locked) magazine's free list
2072 int objects_in_use
= tiny_free_detach_region(szone
, tiny_mag_ptr
, sparse_region
);
2073 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2075 // hand over the region to the (locked) Depot
2076 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2077 // this will cause tiny_free_list_add_ptr called by tiny_free_reattach_region to use
2078 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
2079 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
2081 // Iterate the region putting its free entries on Depot's free list
2082 size_t bytes_inplay
= tiny_free_reattach_region(szone
, depot_ptr
, sparse_region
);
2084 tiny_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2085 tiny_mag_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2086 tiny_mag_ptr
->mag_num_objects
-= objects_in_use
;
2088 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2089 depot_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2090 depot_ptr
->mag_num_objects
+= objects_in_use
;
2092 // connect to Depot as first (MRU) node
2093 recirc_list_splice_first(szone
, depot_ptr
, node
);
2095 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2097 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
2098 tiny_free_scan_madvise_free(szone
, depot_ptr
, sparse_region
);
2100 // If the region is entirely empty vm_deallocate() it
2101 tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
2103 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2107 tiny_get_region_from_depot(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
)
2109 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2111 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2112 if (szone
->num_tiny_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
2116 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
2117 szone_error(szone
, 1, "tiny_get_region_from_depot called for magazine index -1", NULL
, NULL
);
2122 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
2124 // Appropriate one of the Depot's regions. Prefer LIFO selection for best cache utilization.
2125 region_trailer_t
*node
= depot_ptr
->firstNode
;
2127 if (NULL
== node
) { // Depot empty?
2128 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2132 // disconnect first node from Depot
2133 recirc_list_extract(szone
, depot_ptr
, node
);
2135 // Iterate the region pulling its free entries off the (locked) Depot's free list
2136 region_t sparse_region
= TINY_REGION_FOR_PTR(node
);
2137 int objects_in_use
= tiny_free_detach_region(szone
, depot_ptr
, sparse_region
);
2139 // Transfer ownership of the region
2140 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region
) = mag_index
;
2142 // Iterate the region putting its free entries on its new (locked) magazine's free list
2143 size_t bytes_inplay
= tiny_free_reattach_region(szone
, tiny_mag_ptr
, sparse_region
);
2145 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
2146 depot_ptr
->num_bytes_in_magazine
-= TINY_REGION_PAYLOAD_BYTES
;
2147 depot_ptr
->mag_num_objects
-= objects_in_use
;
2149 tiny_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
2150 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2151 tiny_mag_ptr
->mag_num_objects
+= objects_in_use
;
2153 // connect to magazine as first node (it's maximally sparse at this moment)
2154 recirc_list_splice_first(szone
, tiny_mag_ptr
, node
);
2156 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
2158 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_TINY_REGION(sparse_region
)); // DTrace USDT Probe
2160 if (-1 == madvise((void *)sparse_region
, TINY_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
2161 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
2163 szone_error(szone
, 1, "tiny_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed", sparse_region
, NULL
);
2171 #warning Tune K and f!
2172 #define K 1.5 // headroom measured in number of 1Mb regions
2173 #define DENSITY_THRESHOLD(a) \
2174 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
2177 tiny_free_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, region_t region
, void *ptr
,
2180 void *original_ptr
= ptr
;
2181 size_t original_size
= TINY_BYTES_FOR_MSIZE(msize
);
2182 void *next_block
= ((unsigned char *)ptr
+ original_size
);
2183 msize_t previous_msize
, next_msize
;
2185 free_list_t
*big_free_block
;
2186 free_list_t
*after_next_block
;
2187 free_list_t
*before_next_block
;
2188 boolean_t did_prepend
= FALSE
;
2189 boolean_t did_append
= FALSE
;
2192 if (LOG(szone
,ptr
)) {
2193 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2196 szone_error(szone
, 1, "trying to free tiny block that is too small", ptr
,
2197 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2201 // We try to coalesce this block with the preceeding one
2202 previous
= tiny_previous_preceding_free(ptr
, &previous_msize
);
2205 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
2206 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
2211 // clear the meta_header since this is no longer the start of a block
2212 set_tiny_meta_header_middle(ptr
);
2213 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, previous
, previous_msize
);
2215 msize
+= previous_msize
;
2217 // We try to coalesce with the next block
2218 if ((next_block
< TINY_REGION_END(region
)) && tiny_meta_header_is_free(next_block
)) {
2220 next_msize
= get_tiny_free_size(next_block
);
2222 if (LOG(szone
, ptr
) || LOG(szone
, next_block
)) {
2223 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
2224 ptr
, msize
, next_block
, next_msize
);
2227 // If we are coalescing with the next block, and the next block is in
2228 // the last slot of the free list, then we optimize this case here to
2229 // avoid removing next_block from the slot (NUM_TINY_SLOTS - 1) and then adding ptr back
2230 // to slot (NUM_TINY_SLOTS - 1).
2231 if (next_msize
>= NUM_TINY_SLOTS
) {
2232 msize
+= next_msize
;
2234 big_free_block
= (free_list_t
*)next_block
;
2235 after_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->next
);
2236 before_next_block
= free_list_unchecksum_ptr(szone
, &big_free_block
->previous
);
2238 if (!before_next_block
) {
2239 tiny_mag_ptr
->mag_free_list
[NUM_TINY_SLOTS
-1] = ptr
;
2241 before_next_block
->next
.u
= free_list_checksum_ptr(szone
, ptr
);
2244 if (after_next_block
) {
2245 after_next_block
->previous
.u
= free_list_checksum_ptr(szone
, ptr
);
2248 // we don't need to checksum these since they are already checksummed
2249 ((free_list_t
*)ptr
)->previous
= big_free_block
->previous
;
2250 ((free_list_t
*)ptr
)->next
= big_free_block
->next
;
2252 // clear the meta_header to enable coalescing backwards
2253 set_tiny_meta_header_middle(big_free_block
);
2254 set_tiny_meta_header_free(ptr
, msize
);
2256 goto tiny_free_ending
;
2258 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2259 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2260 msize
+= next_msize
;
2263 // The tiny cache already scribbles free blocks as they go through the
2264 // cache, so we do not need to do it here.
2265 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
2266 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
2269 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, ptr
, msize
);
2271 // When in proper debug mode we write on the memory to help debug memory smashers
2273 tiny_mag_ptr
->mag_num_objects
--;
2274 // we use original_size and not msize to avoid double counting the coalesced blocks
2275 tiny_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
2277 // Update this region's bytes in use count
2278 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2279 size_t bytes_used
= node
->bytes_used
- original_size
;
2280 node
->bytes_used
= bytes_used
;
2282 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2283 if (szone
->num_tiny_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
2285 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
2286 // Emptiness discriminant
2287 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2288 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
2289 recirculation candidates list. */
2290 node
->recirc_suitable
= TRUE
;
2292 /* After this free, we've found the region is still dense, so it must have been even more so before
2293 the free. That implies the region is already correctly marked. Do nothing. */
2296 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
2297 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2298 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2299 size_t a
= tiny_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
2300 size_t u
= tiny_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
2302 if (a
- u
> ((3 * TINY_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
))
2303 tiny_free_do_recirc_to_depot(szone
, tiny_mag_ptr
, mag_index
);
2306 // Freed to Depot. N.B. Lock on tiny_magazines[DEPOT_MAGAZINE_INDEX] is already held
2307 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
2308 uintptr_t round_safe
= round_page(safe_ptr
);
2310 uintptr_t safe_extent
= (uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
2311 uintptr_t trunc_extent
= trunc_page(safe_extent
);
2313 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
2314 if (round_safe
< trunc_extent
) { // Safe area covers a page
2315 if (did_prepend
& did_append
) { // Coalesced preceding with original_ptr *and* with following
2316 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
2317 uintptr_t rnd_safe_follow
=
2318 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
2320 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
));
2321 } else if (did_prepend
) { // Coalesced preceding with original_ptr
2322 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
2324 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
);
2325 } else if (did_append
) { // Coalesced original_ptr with following
2326 uintptr_t rnd_safe_follow
=
2327 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
2329 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
));
2330 } else { // Isolated free cannot exceed 496 bytes, thus round_safe == trunc_extent, and so never get here.
2331 /* madvise_free_range(szone, region, round_safe, trunc_extent); */
2335 if (0 < bytes_used
) {
2336 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
2337 so as to avoid thrashing between the Depot's free list and a magazines's free list
2338 with detach_region/reattach_region */
2340 /* Depot'd region is just now empty. Consider return to OS. */
2341 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(region
);
2342 magazine_t
*depot_ptr
= &(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
]);
2343 tiny_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
); // FIXME: depot_ptr is simply tiny_mag_ptr?
2348 // Allocates from the last region or a freshly allocated region
2350 tiny_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2352 void *ptr
, *aligned_address
;
2354 // Deal with unclaimed memory -- mag_bytes_free_at_end
2355 if (tiny_mag_ptr
->mag_bytes_free_at_end
)
2356 tiny_finalize_region(szone
, tiny_mag_ptr
);
2358 // time to create a new region
2359 aligned_address
= allocate_pages(szone
, TINY_REGION_SIZE
, TINY_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_TINY
);
2360 if (!aligned_address
) // out of memory!
2363 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
); // DTrace USDT Probe
2365 // We set the unused bits of the header in the last pair to be all ones, and those of the inuse to zeroes.
2366 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].header
=
2367 (NUM_TINY_BLOCKS
& 31) ? (0xFFFFFFFFU
<< (NUM_TINY_BLOCKS
& 31)) : 0;
2368 ((tiny_region_t
)aligned_address
)->pairs
[CEIL_NUM_TINY_BLOCKS_WORDS
-1].inuse
= 0;
2370 // Here find the only place in tinyland that (infrequently) takes the tiny_regions_lock.
2371 // Only one thread at a time should be permitted to assess the density of the hash
2372 // ring and adjust if needed.
2373 // Only one thread at a time should be permitted to insert its new region on
2375 // It is safe for all other threads to read the hash ring (hashed_regions) and
2376 // the associated sizes (num_regions_allocated and num_tiny_regions).
2378 LOCK(szone
->tiny_regions_lock
);
2380 // Check to see if the hash ring of tiny regions needs to grow. Try to
2381 // avoid the hash ring becoming too dense.
2382 if (szone
->tiny_region_generation
->num_regions_allocated
< (2 * szone
->num_tiny_regions
)) {
2383 region_t
*new_regions
;
2385 size_t new_shift
= szone
->tiny_region_generation
->num_regions_allocated_shift
; // In/Out parameter
2386 new_regions
= hash_regions_grow_no_lock(szone
, szone
->tiny_region_generation
->hashed_regions
,
2387 szone
->tiny_region_generation
->num_regions_allocated
,
2390 // Do not deallocate the current hashed_regions allocation since someone may
2391 // be iterating it. Instead, just leak it.
2393 // Prepare to advance to the "next generation" of the hash ring.
2394 szone
->tiny_region_generation
->nextgen
->hashed_regions
= new_regions
;
2395 szone
->tiny_region_generation
->nextgen
->num_regions_allocated
= new_size
;
2396 szone
->tiny_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
2398 // Throw the switch to atomically advance to the next generation.
2399 szone
->tiny_region_generation
= szone
->tiny_region_generation
->nextgen
;
2400 // Ensure everyone sees the advance.
2401 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
2402 __sync_synchronize();
2407 // Tag the region at "aligned_address" as belonging to us,
2408 // and so put it under the protection of the magazine lock we are holding.
2409 // Do this before advertising "aligned_address" on the hash ring(!)
2410 MAGAZINE_INDEX_FOR_TINY_REGION(aligned_address
) = mag_index
;
2412 // Insert the new region into the hash ring, and update malloc statistics
2413 hash_region_insert_no_lock(szone
->tiny_region_generation
->hashed_regions
,
2414 szone
->tiny_region_generation
->num_regions_allocated
,
2415 szone
->tiny_region_generation
->num_regions_allocated_shift
,
2418 szone
->num_tiny_regions
++;
2419 UNLOCK(szone
->tiny_regions_lock
);
2421 tiny_mag_ptr
->mag_last_region
= aligned_address
;
2422 BYTES_USED_FOR_TINY_REGION(aligned_address
) = TINY_BYTES_FOR_MSIZE(msize
);
2423 ptr
= aligned_address
;
2424 set_tiny_meta_header_in_use(ptr
, msize
);
2425 tiny_mag_ptr
->mag_num_objects
++;
2426 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(msize
);
2427 tiny_mag_ptr
->num_bytes_in_magazine
+= TINY_REGION_PAYLOAD_BYTES
;
2429 // We put a header on the last block so that it appears in use (for coalescing, etc...)
2430 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr
+ TINY_BYTES_FOR_MSIZE(msize
)));
2431 tiny_mag_ptr
->mag_bytes_free_at_end
= TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS
- msize
);
2433 // connect to magazine as first node (it's maximally sparse at this moment)
2434 recirc_list_splice_first(szone
, tiny_mag_ptr
, REGION_TRAILER_FOR_TINY_REGION(aligned_address
));
2437 if (LOG(szone
,ptr
)) {
2438 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
2444 static INLINE boolean_t
2445 tiny_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
2447 // returns 1 on success
2450 unsigned next_index
;
2453 msize_t next_msize
, coalesced_msize
, leftover_msize
;
2456 index
= TINY_INDEX_FOR_PTR(ptr
);
2457 old_msize
= TINY_MSIZE_FOR_BYTES(old_size
);
2458 next_index
= index
+ old_msize
;
2460 if (next_index
>= NUM_TINY_BLOCKS
) {
2463 next_block
= (char *)ptr
+ old_size
;
2465 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
2466 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)),
2467 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
)));
2470 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2473 is_free
= tiny_meta_header_is_free(next_block
);
2475 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2476 return 0; // next_block is in use;
2478 next_msize
= get_tiny_free_size(next_block
);
2479 if (old_size
+ TINY_BYTES_FOR_MSIZE(next_msize
) < new_size
) {
2480 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2481 return 0; // even with next block, not enough
2484 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2487 tiny_free_list_remove_ptr(szone
, tiny_mag_ptr
, next_block
, next_msize
);
2488 set_tiny_meta_header_middle(next_block
); // clear the meta_header to enable coalescing backwards
2489 coalesced_msize
= TINY_MSIZE_FOR_BYTES(new_size
- old_size
+ TINY_QUANTUM
- 1);
2490 leftover_msize
= next_msize
- coalesced_msize
;
2491 if (leftover_msize
) {
2492 /* there's some left, so put the remainder back */
2493 leftover
= (void *)((uintptr_t)next_block
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
));
2495 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover
, leftover_msize
);
2497 set_tiny_meta_header_in_use(ptr
, old_msize
+ coalesced_msize
);
2499 if (LOG(szone
,ptr
)) {
2500 malloc_printf("in tiny_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr
, old_msize
+ coalesced_msize
);
2503 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2505 // Update this region's bytes in use count
2506 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2507 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(coalesced_msize
);
2508 node
->bytes_used
= bytes_used
;
2510 // Emptiness discriminant
2511 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2512 /* After this reallocation the region is still sparse, so it must have been even more so before
2513 the reallocation. That implies the region is already correctly marked. Do nothing. */
2515 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2516 recirculation candidates list. */
2517 node
->recirc_suitable
= FALSE
;
2520 SZONE_MAGAZINE_PTR_UNLOCK(szone
,tiny_mag_ptr
);
2521 CHECK(szone
, __PRETTY_FUNCTION__
);
2526 tiny_check_region(szone_t
*szone
, region_t region
)
2528 uintptr_t start
, ptr
, region_end
;
2529 boolean_t prev_free
= 0;
2532 free_list_t
*free_head
;
2533 void *follower
, *previous
, *next
;
2534 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
2535 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2538 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2540 /* establish region limits */
2541 start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
2543 region_end
= (uintptr_t)TINY_REGION_END(region
);
2546 * The last region may have a trailing chunk which has not been converted into inuse/freelist
2549 if (region
== tiny_mag_ptr
->mag_last_region
)
2550 region_end
-= tiny_mag_ptr
->mag_bytes_free_at_end
;
2553 * Scan blocks within the region.
2555 while (ptr
< region_end
) {
2557 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
2560 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2561 if (is_free
&& !msize
&& (ptr
== start
)) {
2566 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
2570 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
2577 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
2580 if (msize
> (NUM_TINY_SLOTS
- 1)) {
2581 malloc_printf("*** invariant broken for %p this tiny msize=%d - size is too large\n",
2585 /* move to next block */
2586 ptr
+= TINY_BYTES_FOR_MSIZE(msize
);
2589 * Free blocks must have been coalesced, we cannot have a free block following another
2593 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
2599 * Check the integrity of this block's entry in its freelist.
2601 free_head
= (free_list_t
*)ptr
;
2602 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
2603 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
2604 if (previous
&& !tiny_meta_header_is_free(previous
)) {
2605 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2609 if (next
&& !tiny_meta_header_is_free(next
)) {
2610 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
2615 * Check the free block's trailing size value.
2617 follower
= FOLLOWING_TINY_PTR(ptr
, msize
);
2618 if (((uintptr_t)follower
!= region_end
) && (get_tiny_previous_free_msize(follower
) != msize
)) {
2619 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
2620 "(end marker incorrect) should be %d; in fact %d\n",
2621 ptr
, follower
, TINY_REGION_ADDRESS(region
), region_end
, msize
, get_tiny_previous_free_msize(follower
));
2624 /* move to next block */
2625 ptr
= (uintptr_t)follower
;
2629 * Ensure that we scanned the entire region
2631 if (ptr
!= region_end
) {
2632 malloc_printf("*** invariant broken for region end %p - %p\n", ptr
, region_end
);
2636 * Check the trailing block's integrity.
2638 if (region
== tiny_mag_ptr
->mag_last_region
) {
2639 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2640 msize
= get_tiny_meta_header((void *)ptr
, &is_free
);
2641 if (is_free
|| (msize
!= 1)) {
2642 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr
, msize
, is_free
);
2649 static kern_return_t
2650 tiny_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
2651 memory_reader_t reader
, vm_range_recorder_t recorder
)
2656 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
2661 vm_range_t admin_range
;
2662 vm_range_t ptr_range
;
2663 unsigned char *mapped_region
;
2664 uint32_t *block_header
;
2666 unsigned block_index
;
2667 unsigned block_limit
;
2672 vm_address_t mag_last_free_ptr
= 0;
2673 msize_t mag_last_free_msize
= 0;
2675 region_hash_generation_t
*trg_ptr
;
2676 err
= reader(task
, (vm_address_t
)szone
->tiny_region_generation
, sizeof(region_hash_generation_t
), (void **)&trg_ptr
);
2677 if (err
) return err
;
2679 num_regions
= trg_ptr
->num_regions_allocated
;
2680 err
= reader(task
, (vm_address_t
)trg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
2681 if (err
) return err
;
2683 for (index
= 0; index
< num_regions
; ++index
) {
2684 region
= regions
[index
];
2685 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
2686 range
.address
= (vm_address_t
)TINY_REGION_ADDRESS(region
);
2687 range
.size
= (vm_size_t
)TINY_REGION_SIZE
;
2688 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
2689 admin_range
.address
= range
.address
+ TINY_METADATA_START
;
2690 admin_range
.size
= TINY_METADATA_SIZE
;
2691 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
2693 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
2694 ptr_range
.address
= range
.address
;
2695 ptr_range
.size
= NUM_TINY_BLOCKS
* TINY_QUANTUM
;
2696 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
2698 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
2699 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
2703 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(mapped_region
);
2704 magazine_t
*tiny_mag_ptr
;
2705 err
= reader(task
, (vm_address_t
)&(szone
->tiny_magazines
[mag_index
]), sizeof(magazine_t
),
2706 (void **)&tiny_mag_ptr
);
2707 if (err
) return err
;
2709 void *mag_last_free
= tiny_mag_ptr
->mag_last_free
;
2710 if (mag_last_free
) {
2711 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(TINY_QUANTUM
- 1);
2712 mag_last_free_msize
= (uintptr_t) mag_last_free
& (TINY_QUANTUM
- 1);
2715 block_header
= (uint32_t *)(mapped_region
+ TINY_METADATA_START
+ sizeof(region_trailer_t
));
2716 in_use
= TINY_INUSE_FOR_HEADER(block_header
);
2718 block_limit
= NUM_TINY_BLOCKS
;
2719 if (region
== tiny_mag_ptr
->mag_last_region
)
2720 block_limit
-= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr
->mag_bytes_free_at_end
);
2722 while (block_index
< block_limit
) {
2723 vm_size_t block_offset
= TINY_BYTES_FOR_MSIZE(block_index
);
2724 is_free
= !BITARRAY_BIT(in_use
, block_index
);
2726 mapped_ptr
= mapped_region
+ block_offset
;
2728 // mapped_region, the address at which 'range' in 'task' has been
2729 // mapped into our process, is not necessarily aligned to
2730 // TINY_BLOCKS_ALIGN.
2732 // Since the code in get_tiny_free_size() assumes the pointer came
2733 // from a properly aligned tiny region, and mapped_region is not
2734 // necessarily aligned, then do the size calculation directly.
2735 // If the next bit is set in the header bitmap, then the size is one
2736 // quantum. Otherwise, read the size field.
2737 if (!BITARRAY_BIT(block_header
, (block_index
+1)))
2738 msize
= TINY_FREE_SIZE(mapped_ptr
);
2744 } else if (range
.address
+ block_offset
!= mag_last_free_ptr
) {
2746 bit
= block_index
+ 1;
2747 while (! BITARRAY_BIT(block_header
, bit
)) {
2751 buffer
[count
].address
= range
.address
+ block_offset
;
2752 buffer
[count
].size
= TINY_BYTES_FOR_MSIZE(msize
);
2754 if (count
>= MAX_RECORDER_BUFFER
) {
2755 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2759 // Block is not free but it matches mag_last_free_ptr so even
2760 // though it is not marked free in the bitmap, we treat it as if
2761 // it is and move on
2762 msize
= mag_last_free_msize
;
2764 block_index
+= msize
;
2767 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
2777 tiny_malloc_from_free_list(szone_t
*szone
, magazine_t
*tiny_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
2781 grain_t slot
= msize
- 1;
2782 free_list_t
**free_list
= tiny_mag_ptr
->mag_free_list
;
2783 free_list_t
**the_slot
= free_list
+ slot
;
2785 free_list_t
**limit
;
2786 #if defined(__LP64__)
2791 msize_t leftover_msize
;
2792 free_list_t
*leftover_ptr
;
2794 // Assumes we've locked the region
2795 CHECK_MAGAZINE_PTR_LOCKED(szone
, tiny_mag_ptr
, __PRETTY_FUNCTION__
);
2797 // Look for an exact match by checking the freelist for this msize.
2801 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2803 next
->previous
= ptr
->previous
;
2805 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
2810 if (LOG(szone
, ptr
)) {
2811 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2814 goto return_tiny_alloc
;
2817 // Mask off the bits representing slots holding free blocks smaller than the
2818 // size we need. If there are no larger free blocks, try allocating from
2819 // the free space at the end of the tiny region.
2820 #if defined(__LP64__)
2821 bitmap
= ((uint64_t *)(tiny_mag_ptr
->mag_bitmap
))[0] & ~ ((1ULL << slot
) - 1);
2823 bitmap
= tiny_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
2826 goto try_tiny_malloc_from_end
;
2828 slot
= BITMAPV_CTZ(bitmap
);
2829 limit
= free_list
+ NUM_TINY_SLOTS
- 1;
2832 if (free_list
< limit
) {
2835 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2838 next
->previous
= ptr
->previous
;
2840 BITMAPV_CLR(tiny_mag_ptr
->mag_bitmap
, slot
);
2842 this_msize
= get_tiny_free_size(ptr
);
2843 goto add_leftover_and_proceed
;
2846 malloc_printf("in tiny_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
2850 // We are now looking at the last slot, which contains blocks equal to, or
2851 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
2852 // If the last freelist is not empty, and the head contains a block that is
2853 // larger than our request, then the remainder is put back on the free list.
2856 this_msize
= get_tiny_free_size(ptr
);
2857 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
2858 if (this_msize
- msize
>= NUM_TINY_SLOTS
) {
2859 // the leftover will go back to the free list, so we optimize by
2860 // modifying the free list rather than a pop and push of the head
2861 leftover_msize
= this_msize
- msize
;
2862 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2863 *limit
= leftover_ptr
;
2865 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
2867 leftover_ptr
->previous
= ptr
->previous
;
2868 leftover_ptr
->next
= ptr
->next
;
2869 set_tiny_meta_header_free(leftover_ptr
, leftover_msize
);
2871 if (LOG(szone
,ptr
)) {
2872 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n",
2873 ptr
, msize
, this_msize
);
2877 goto return_tiny_alloc
;
2880 next
->previous
= ptr
->previous
;
2883 goto add_leftover_and_proceed
;
2887 try_tiny_malloc_from_end
:
2888 // Let's see if we can use tiny_mag_ptr->mag_bytes_free_at_end
2889 if (tiny_mag_ptr
->mag_bytes_free_at_end
>= TINY_BYTES_FOR_MSIZE(msize
)) {
2890 ptr
= (free_list_t
*)((uintptr_t)TINY_REGION_END(tiny_mag_ptr
->mag_last_region
) -
2891 tiny_mag_ptr
->mag_bytes_free_at_end
);
2892 tiny_mag_ptr
->mag_bytes_free_at_end
-= TINY_BYTES_FOR_MSIZE(msize
);
2893 if (tiny_mag_ptr
->mag_bytes_free_at_end
) {
2894 // let's add an in use block after ptr to serve as boundary
2895 set_tiny_meta_header_in_use_1((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2899 if (LOG(szone
, ptr
)) {
2900 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr
, msize
);
2903 goto return_tiny_alloc
;
2907 add_leftover_and_proceed
:
2908 if (!this_msize
|| (this_msize
> msize
)) {
2909 leftover_msize
= this_msize
- msize
;
2910 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ TINY_BYTES_FOR_MSIZE(msize
));
2912 if (LOG(szone
,ptr
)) {
2913 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
2916 tiny_free_list_add_ptr(szone
, tiny_mag_ptr
, leftover_ptr
, leftover_msize
);
2921 tiny_mag_ptr
->mag_num_objects
++;
2922 tiny_mag_ptr
->mag_num_bytes_in_objects
+= TINY_BYTES_FOR_MSIZE(this_msize
);
2924 // Update this region's bytes in use count
2925 region_trailer_t
*node
= REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
2926 size_t bytes_used
= node
->bytes_used
+ TINY_BYTES_FOR_MSIZE(this_msize
);
2927 node
->bytes_used
= bytes_used
;
2929 // Emptiness discriminant
2930 if (bytes_used
< DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES
)) {
2931 /* After this allocation the region is still sparse, so it must have been even more so before
2932 the allocation. That implies the region is already correctly marked. Do nothing. */
2934 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2935 recirculation candidates list. */
2936 node
->recirc_suitable
= FALSE
;
2939 if (LOG(szone
,ptr
)) {
2940 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
2944 set_tiny_meta_header_in_use(ptr
, this_msize
);
2946 set_tiny_meta_header_in_use_1(ptr
);
2949 #undef DENSITY_THRESHOLD
2952 static INLINE
void *
2953 tiny_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
2956 mag_index_t mag_index
= mag_get_thread_index(szone
);
2957 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
2960 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
2961 szone_error(szone
, 1, "malloc called for magazine index -1", NULL
, NULL
);
2966 szone_error(szone
, 1, "invariant broken (!msize) in allocation (region)", NULL
, NULL
);
2971 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
2974 ptr
= tiny_mag_ptr
->mag_last_free
;
2976 if ((((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) == msize
) {
2978 tiny_mag_ptr
->mag_last_free
= NULL
;
2979 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2980 CHECK(szone
, __PRETTY_FUNCTION__
);
2981 ptr
= (void *)((uintptr_t)ptr
& ~ (TINY_QUANTUM
- 1));
2982 if (cleared_requested
) {
2983 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
2986 if (LOG(szone
,ptr
)) {
2987 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr
, msize
);
2992 #endif /* TINY_CACHE */
2994 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
2996 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
2997 CHECK(szone
, __PRETTY_FUNCTION__
);
2998 if (cleared_requested
) {
2999 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3004 if (tiny_get_region_from_depot(szone
, tiny_mag_ptr
, mag_index
)) {
3005 ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
3007 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3008 CHECK(szone
, __PRETTY_FUNCTION__
);
3009 if (cleared_requested
) {
3010 memset(ptr
, 0, TINY_BYTES_FOR_MSIZE(msize
));
3016 ptr
= tiny_malloc_from_region_no_lock(szone
, tiny_mag_ptr
, mag_index
, msize
);
3017 // we don't clear because this freshly allocated space is pristine
3018 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3019 CHECK(szone
, __PRETTY_FUNCTION__
);
3023 static NOINLINE
void
3024 free_tiny_botch(szone_t
*szone
, free_list_t
*ptr
)
3026 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
3027 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3028 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3029 szone_error(szone
, 1, "double free", ptr
, NULL
);
3033 free_tiny(szone_t
*szone
, void *ptr
, region_t tiny_region
, size_t known_size
)
3037 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
3038 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3040 // ptr is known to be in tiny_region
3042 msize
= TINY_MSIZE_FOR_BYTES(known_size
+ TINY_QUANTUM
- 1);
3044 msize
= get_tiny_meta_header(ptr
, &is_free
);
3046 free_tiny_botch(szone
, ptr
);
3052 malloc_printf("*** free_tiny() block in use is too large: %p\n", ptr
);
3057 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3060 // Depot does not participate in TINY_CACHE since it can't be directly malloc()'d
3061 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3062 if (msize
< TINY_QUANTUM
) { // to see if the bits fit in the last 4 bits
3063 void *ptr2
= tiny_mag_ptr
->mag_last_free
; // Might be NULL
3064 region_t rgn2
= tiny_mag_ptr
->mag_last_free_rgn
;
3066 /* check that we don't already have this pointer in the cache */
3067 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (TINY_QUANTUM
- 1))) {
3068 free_tiny_botch(szone
, ptr
);
3072 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
3073 memset(ptr
, 0x55, TINY_BYTES_FOR_MSIZE(msize
));
3075 tiny_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
3076 tiny_mag_ptr
->mag_last_free_rgn
= tiny_region
;
3079 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3080 CHECK(szone
, __PRETTY_FUNCTION__
);
3084 msize
= (uintptr_t)ptr2
& (TINY_QUANTUM
- 1);
3085 ptr
= (void *)(((uintptr_t)ptr2
) & ~(TINY_QUANTUM
- 1));
3089 #endif /* TINY_CACHE */
3091 // Now in the time it took to acquire the lock, the region may have migrated
3092 // from one magazine to another. I.e. trailer->mag_index is volatile.
3093 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
3094 // is stale. If so, keep on tryin' ...
3095 region_trailer_t
*trailer
= REGION_TRAILER_FOR_TINY_REGION(tiny_region
);
3096 mag_index_t refreshed_index
;
3098 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
3100 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3102 mag_index
= refreshed_index
;
3103 tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3104 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3107 tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
);
3108 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3109 CHECK(szone
, __PRETTY_FUNCTION__
);
3113 print_tiny_free_list(szone_t
*szone
)
3116 _SIMPLE_STRING b
= _simple_salloc();
3117 mag_index_t mag_index
;
3120 _simple_sappend(b
, "tiny free sizes:\n");
3121 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3123 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
3124 while (slot
< NUM_TINY_SLOTS
) {
3125 ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3127 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== NUM_TINY_SLOTS
-1) ? ">=" : "",
3128 (slot
+1)*TINY_QUANTUM
, free_list_count(szone
, ptr
));
3132 _simple_sappend(b
,"\n");
3134 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3140 print_tiny_region(boolean_t verbose
, region_t region
, size_t bytes_at_end
)
3142 unsigned counts
[1024];
3143 unsigned in_use
= 0;
3144 uintptr_t start
= (uintptr_t)TINY_REGION_ADDRESS(region
);
3145 uintptr_t current
= start
;
3146 uintptr_t limit
= (uintptr_t)TINY_REGION_END(region
) - bytes_at_end
;
3151 uintptr_t pgTot
= 0;
3153 if (region
== HASHRING_REGION_DEALLOCATED
) {
3154 if ((b
= _simple_salloc()) != NULL
) {
3155 _simple_sprintf(b
, "Tiny region [unknown address] was returned to the OS\n");
3156 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3162 memset(counts
, 0, sizeof(counts
));
3163 while (current
< limit
) {
3164 msize
= get_tiny_meta_header((void *)current
, &is_free
);
3165 if (is_free
& !msize
&& (current
== start
)) {
3166 // first block is all free
3167 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3168 uintptr_t pgHi
= trunc_page(start
+ TINY_REGION_SIZE
- sizeof(msize_t
));
3171 pgTot
+= (pgHi
- pgLo
);
3176 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
3181 if (msize
> NUM_TINY_SLOTS
)
3182 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current
, msize
);
3187 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3188 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3191 pgTot
+= (pgHi
- pgLo
);
3194 current
+= TINY_BYTES_FOR_MSIZE(msize
);
3196 if ((b
= _simple_salloc()) != NULL
) {
3197 _simple_sprintf(b
, "Tiny region [%p-%p, %y] \t", (void *)start
, TINY_REGION_END(region
), (int)TINY_REGION_SIZE
);
3198 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_TINY_REGION(region
));
3199 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_TINY_REGION(region
));
3201 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
);
3202 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_TINY_REGION(region
)) {
3203 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
3205 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
3207 if (verbose
&& in_use
) {
3208 _simple_sappend(b
, "\n\tSizes in use: ");
3209 for (ci
= 0; ci
< 1024; ci
++)
3211 _simple_sprintf(b
, "%d[%d] ", TINY_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
3213 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
3219 tiny_free_list_check(szone_t
*szone
, grain_t slot
)
3221 mag_index_t mag_index
;
3223 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
3224 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
3225 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
3228 free_list_t
*ptr
= szone
->tiny_magazines
[mag_index
].mag_free_list
[slot
];
3230 free_list_t
*previous
= NULL
;
3233 is_free
= tiny_meta_header_is_free(ptr
);
3235 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3236 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3239 if (((uintptr_t)ptr
) & (TINY_QUANTUM
- 1)) {
3240 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3241 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3244 if (!tiny_region_for_ptr_no_lock(szone
, ptr
)) {
3245 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3246 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3249 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
3250 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
3251 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3255 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
3259 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
3264 /********************* SMALL FREE LIST UTILITIES ************************/
3267 * Mark a block as free. Only the first quantum of a block is marked thusly,
3268 * the remainder are marked "middle".
3271 small_meta_header_set_is_free(msize_t
*meta_headers
, unsigned index
, msize_t msize
)
3273 meta_headers
[index
] = msize
| SMALL_IS_FREE
;
3277 * Mark a block as in use. Only the first quantum of a block is marked thusly,
3278 * the remainder are marked "middle".
3281 small_meta_header_set_in_use(msize_t
*meta_headers
, msize_t index
, msize_t msize
)
3283 meta_headers
[index
] = msize
;
3287 * Mark a quantum as being the second or later in a block.
3290 small_meta_header_set_middle(msize_t
*meta_headers
, msize_t index
)
3292 meta_headers
[index
] = 0;
3296 * Adds an item to the proper free list, and also marks the meta-header of the
3298 * Assumes szone has been locked
3301 small_free_list_add_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3303 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3304 free_list_t
*free_ptr
= ptr
;
3305 free_list_t
*free_head
= small_mag_ptr
->mag_free_list
[slot
];
3309 if (LOG(szone
,ptr
)) {
3310 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3312 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
3313 szone_error(szone
, 1, "small_free_list_add_ptr: Unaligned ptr", ptr
, NULL
);
3316 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), msize
);
3320 if (free_list_unchecksum_ptr(szone
, &free_head
->previous
)) {
3321 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr
,
3322 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr
, slot
, (void *)free_head
, free_head
->previous
.p
);
3324 if (!SMALL_PTR_IS_FREE(free_head
)) {
3325 szone_error(szone
, 1, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr
,
3326 "ptr=%p slot=%d free_head=%p\n", ptr
, slot
, (void *)free_head
);
3329 free_head
->previous
.u
= free_list_checksum_ptr(szone
, free_ptr
);
3331 BITMAPN_SET(small_mag_ptr
->mag_bitmap
, slot
);
3333 free_ptr
->previous
.u
= free_list_checksum_ptr(szone
, NULL
);
3334 free_ptr
->next
.u
= free_list_checksum_ptr(szone
, free_head
);
3336 small_mag_ptr
->mag_free_list
[slot
] = free_ptr
;
3338 // Store msize at the end of the block denoted by "ptr" (i.e. at a negative offset from "follower")
3339 follower
= (void *)((uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
3340 SMALL_PREVIOUS_MSIZE(follower
) = msize
;
3344 * Removes the item pointed to by ptr in the proper free list.
3345 * Assumes szone has been locked
3348 small_free_list_remove_ptr(szone_t
*szone
, magazine_t
*small_mag_ptr
, void *ptr
, msize_t msize
)
3350 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
3351 free_list_t
*free_ptr
= ptr
, *next
, *previous
;
3353 next
= free_list_unchecksum_ptr(szone
, &free_ptr
->next
);
3354 previous
= free_list_unchecksum_ptr(szone
, &free_ptr
->previous
);
3357 if (LOG(szone
,ptr
)) {
3358 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__
, ptr
, msize
);
3363 // The block to remove is the head of the free list
3365 if (small_mag_ptr
->mag_free_list
[slot
] != ptr
) {
3366 szone_error(szone
, 1, "small_free_list_remove_ptr: Internal invariant broken (small_mag_ptr->mag_free_list[slot])", ptr
,
3367 "ptr=%p slot=%d msize=%d small_mag_ptr->mag_free_list[slot]=%p\n",
3368 ptr
, slot
, msize
, (void *)small_mag_ptr
->mag_free_list
[slot
]);
3372 small_mag_ptr
->mag_free_list
[slot
] = next
;
3373 if (!next
) BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
3375 // We know free_ptr is already checksummed, so we don't need to do it
3377 previous
->next
= free_ptr
->next
;
3380 // We know free_ptr is already checksummed, so we don't need to do it
3382 next
->previous
= free_ptr
->previous
;
3387 * small_region_for_ptr_no_lock - Returns the small region containing the pointer,
3388 * or NULL if not found.
3390 static INLINE region_t
3391 small_region_for_ptr_no_lock(szone_t
*szone
, const void *ptr
)
3393 rgnhdl_t r
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3394 szone
->small_region_generation
->num_regions_allocated
,
3395 szone
->small_region_generation
->num_regions_allocated_shift
,
3396 SMALL_REGION_FOR_PTR(ptr
));
3401 small_finalize_region(szone_t
*szone
, magazine_t
*small_mag_ptr
) {
3402 void *last_block
, *previous_block
;
3403 msize_t last_msize
, previous_msize
, last_index
;
3405 last_block
= SMALL_REGION_END(small_mag_ptr
->mag_last_region
) - small_mag_ptr
->mag_bytes_free_at_end
;
3406 last_msize
= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
3408 // It is possible that the block prior to the last block in the region has
3409 // been free'd, but was not coalesced with the free bytes at the end of the
3410 // block, since we treat the bytes at the end of the region as "in use" in
3411 // the meta headers. Attempt to coalesce the last block with the previous
3412 // block, so we don't violate the "no consecutive free blocks" invariant.
3414 // FIXME: If we could calculate the previous small free size in the same
3415 // manner as tiny_previous_preceding_free, it would eliminate the
3416 // index & previous msize checks, which are a guard against reading
3417 // bogus data out of in-use or written-on-freed memory.
3419 // FIXME: Need to investigate how much work would be required to increase
3420 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
3421 // than performing this workaround.
3423 last_index
= SMALL_META_INDEX_FOR_PTR(last_block
);
3424 previous_msize
= SMALL_PREVIOUS_MSIZE(last_block
);
3426 if (last_index
&& (previous_msize
<= last_index
)) {
3427 previous_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3428 if (*SMALL_METADATA_FOR_PTR(previous_block
) == (previous_msize
| SMALL_IS_FREE
)) {
3429 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(last_block
);
3431 small_meta_header_set_middle(meta_headers
, last_index
);
3432 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous_block
, previous_msize
);
3433 last_block
= (void *)((uintptr_t)last_block
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3434 last_msize
+= previous_msize
;
3438 // splice last_block into the free list
3439 small_free_list_add_ptr(szone
, small_mag_ptr
, last_block
, last_msize
);
3440 small_mag_ptr
->mag_bytes_free_at_end
= 0;
3441 small_mag_ptr
->mag_last_region
= NULL
;
3445 small_free_detach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3446 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3447 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3448 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3449 uintptr_t current
= start
;
3450 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3451 int total_alloc
= 0;
3453 while (current
< limit
) {
3454 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3455 msize_t msize_and_free
= meta_headers
[index
];
3456 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3457 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3461 malloc_printf("*** small_free_detach_region error with %p: msize=%d is_free =%d\n",
3462 (void *)current
, msize
, is_free
);
3467 small_free_list_remove_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3471 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3477 small_free_reattach_region(szone_t
*szone
, magazine_t
*small_mag_ptr
, region_t r
) {
3478 unsigned char *ptr
= SMALL_REGION_ADDRESS(r
);
3479 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3480 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3481 uintptr_t current
= start
;
3482 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3483 size_t total_alloc
= 0;
3485 while (current
< limit
) {
3486 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3487 msize_t msize_and_free
= meta_headers
[index
];
3488 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3489 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3493 malloc_printf("*** small_free_reattach_region error with %p: msize=%d is_free =%d\n",
3494 (void *)current
, msize
, is_free
);
3499 small_free_list_add_ptr(szone
, small_mag_ptr
, (void *)current
, msize
);
3501 total_alloc
+= SMALL_BYTES_FOR_MSIZE(msize
);
3503 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3509 small_free_scan_depot_madvise_free(szone_t
*szone
, magazine_t
*depot_ptr
, region_t r
) {
3510 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(r
);
3511 uintptr_t current
= start
;
3512 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(r
);
3513 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(start
);
3514 boolean_t did_advise
= FALSE
;
3516 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
3518 while (current
< limit
) {
3519 unsigned index
= SMALL_META_INDEX_FOR_PTR(current
);
3520 msize_t msize_and_free
= meta_headers
[index
];
3521 boolean_t is_free
= msize_and_free
& SMALL_IS_FREE
;
3522 msize_t msize
= msize_and_free
& ~ SMALL_IS_FREE
;
3524 if (is_free
&& !msize
&& (current
== start
)) {
3526 // first block is all free
3527 malloc_printf("*** small_free_scan_depot_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
3528 (void *)current
, msize
, is_free
);
3530 uintptr_t pgLo
= round_page(start
+ sizeof(free_list_t
) + sizeof(msize_t
));
3531 uintptr_t pgHi
= trunc_page(start
+ SMALL_REGION_SIZE
- sizeof(msize_t
));
3534 madvise_free_range(szone
, r
, pgLo
, pgHi
);
3541 malloc_printf("*** small_free_scan_depot_madvise_free error with %p: msize=%d is_free =%d\n",
3542 (void *)current
, msize
, is_free
);
3547 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
3548 uintptr_t pgHi
= trunc_page(current
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
3551 madvise_free_range(szone
, r
, pgLo
, pgHi
);
3555 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
3559 /* Move the node to the tail of the Deopt's recirculation list to delay its re-use. */
3560 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(r
);
3561 recirc_list_extract(szone
, depot_ptr
, node
); // excise node from list
3562 recirc_list_splice_last(szone
, depot_ptr
, node
); // connect to magazine as last node
3567 small_free_try_depot_unmap_no_lock(szone_t
*szone
, magazine_t
*depot_ptr
, region_trailer_t
*node
)
3569 #warning Tune Depot headroom
3570 if (0 < node
->bytes_used
||
3571 depot_ptr
->recirculation_entries
< (szone
->num_small_magazines
* 2)) {
3575 // disconnect first node from Depot
3576 recirc_list_extract(szone
, depot_ptr
, node
);
3578 // Iterate the region pulling its free entries off the (locked) Depot's free list
3579 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3580 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
3582 if (0 == objects_in_use
) {
3583 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
3584 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
3585 rgnhdl_t pSlot
= hash_lookup_region_no_lock(szone
->small_region_generation
->hashed_regions
,
3586 szone
->small_region_generation
->num_regions_allocated
,
3587 szone
->small_region_generation
->num_regions_allocated_shift
, sparse_region
);
3588 *pSlot
= HASHRING_REGION_DEALLOCATED
;
3589 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3590 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
3591 __sync_fetch_and_add( &(szone
->num_small_regions_dealloc
), 1); // Atomically increment num_small_regions_dealloc
3594 OSAtomicIncrement64( (volatile int64_t *)&(szone
->num_small_regions_dealloc
) );
3596 OSAtomicIncrement32( (volatile int32_t *)&(szone
->num_small_regions_dealloc
) );
3600 // Transfer ownership of the region back to the OS
3601 SZONE_MAGAZINE_PTR_UNLOCK(szone
, depot_ptr
); // Avoid denial of service to Depot while in kernel
3602 deallocate_pages(szone
, sparse_region
, SMALL_REGION_SIZE
, 0);
3603 SZONE_MAGAZINE_PTR_LOCK(szone
, depot_ptr
);
3605 MAGMALLOC_DEALLOCREGION((void *)szone
, (void *)sparse_region
); // DTrace USDT Probe
3608 szone_error(szone
, 1, "small_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL
, "%d\n", objects_in_use
);
3613 small_free_do_recirc_to_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
3615 // The entire magazine crossed the "emptiness threshold". Transfer a region
3616 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
3617 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
3618 region_trailer_t
*node
= small_mag_ptr
->firstNode
;
3620 while (node
&& !node
->recirc_suitable
) {
3626 malloc_printf("*** small_free_do_recirc_to_depot end of list\n");
3631 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3633 // Deal with unclaimed memory -- mag_bytes_free_at_end
3634 if (sparse_region
== small_mag_ptr
->mag_last_region
&& small_mag_ptr
->mag_bytes_free_at_end
) {
3635 small_finalize_region(szone
, small_mag_ptr
);
3638 // disconnect first node from magazine
3639 recirc_list_extract(szone
, small_mag_ptr
, node
);
3641 // Iterate the region pulling its free entries off its (locked) magazine's free list
3642 int objects_in_use
= small_free_detach_region(szone
, small_mag_ptr
, sparse_region
);
3643 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3645 // hand over the region to the (locked) Depot
3646 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
3647 // this will cause small_free_list_add_ptr called by small_free_reattach_region to use
3648 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
3649 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = DEPOT_MAGAZINE_INDEX
;
3651 // Iterate the region putting its free entries on Depot's free list
3652 size_t bytes_inplay
= small_free_reattach_region(szone
, depot_ptr
, sparse_region
);
3654 small_mag_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
3655 small_mag_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3656 small_mag_ptr
->mag_num_objects
-= objects_in_use
;
3658 depot_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
3659 depot_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3660 depot_ptr
->mag_num_objects
+= objects_in_use
;
3662 // connect to Depot as first node
3663 recirc_list_splice_first(szone
, depot_ptr
, node
);
3665 MAGMALLOC_RECIRCREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
3667 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
3668 small_free_scan_depot_madvise_free(szone
, depot_ptr
, sparse_region
);
3670 // If the region is entirely empty vm_deallocate() it
3671 small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
3673 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3677 small_get_region_from_depot(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
)
3679 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3681 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
3682 if (szone
->num_small_magazines
== 1) // Uniprocessor, single magazine, so no recirculation necessary
3686 if (DEPOT_MAGAZINE_INDEX
== mag_index
) {
3687 szone_error(szone
, 1, "small_get_region_from_depot called for magazine index -1", NULL
, NULL
);
3692 SZONE_MAGAZINE_PTR_LOCK(szone
,depot_ptr
);
3694 // Appropriate one of the Depot's regions. Prefer LIFO selection for best cache utilization.
3695 region_trailer_t
*node
= depot_ptr
->firstNode
;
3697 if (NULL
== node
) { // Depot empty?
3698 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3702 // disconnect first node from Depot
3703 recirc_list_extract(szone
, depot_ptr
, node
);
3705 // Iterate the region pulling its free entries off the (locked) Depot's free list
3706 region_t sparse_region
= SMALL_REGION_FOR_PTR(node
);
3707 int objects_in_use
= small_free_detach_region(szone
, depot_ptr
, sparse_region
);
3709 // Transfer ownership of the region
3710 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region
) = mag_index
;
3712 // Iterate the region putting its free entries on its new (locked) magazine's free list
3713 size_t bytes_inplay
= small_free_reattach_region(szone
, small_mag_ptr
, sparse_region
);
3715 depot_ptr
->mag_num_bytes_in_objects
-= bytes_inplay
;
3716 depot_ptr
->num_bytes_in_magazine
-= SMALL_REGION_PAYLOAD_BYTES
;
3717 depot_ptr
->mag_num_objects
-= objects_in_use
;
3719 small_mag_ptr
->mag_num_bytes_in_objects
+= bytes_inplay
;
3720 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3721 small_mag_ptr
->mag_num_objects
+= objects_in_use
;
3723 // connect to magazine as first node (it's maximally sparse at this moment)
3724 recirc_list_splice_first(szone
, small_mag_ptr
, node
);
3726 SZONE_MAGAZINE_PTR_UNLOCK(szone
,depot_ptr
);
3728 MAGMALLOC_DEPOTREGION((void *)szone
, (int)mag_index
, (int)BYTES_USED_FOR_SMALL_REGION(sparse_region
)); // DTrace USDT Probe
3730 if (-1 == madvise((void *)sparse_region
, SMALL_REGION_PAYLOAD_BYTES
, MADV_FREE_REUSE
)) {
3731 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
3733 szone_error(szone
, 1, "small_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed", sparse_region
, NULL
);
3741 #warning Tune K and f!
3742 #define K 1.5 // headroom measured in number of 8Mb regions
3743 #define DENSITY_THRESHOLD(a) \
3744 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
3747 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
)
3749 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3750 unsigned index
= SMALL_META_INDEX_FOR_PTR(ptr
);
3751 void *original_ptr
= ptr
;
3752 size_t original_size
= SMALL_BYTES_FOR_MSIZE(msize
);
3753 unsigned char *next_block
= ((unsigned char *)ptr
+ original_size
);
3754 msize_t next_index
= index
+ msize
;
3755 msize_t previous_msize
, next_msize
;
3757 boolean_t did_prepend
= FALSE
;
3758 boolean_t did_append
= FALSE
;
3761 if (LOG(szone
,ptr
)) {
3762 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
3765 szone_error(szone
, 1, "trying to free small block that is too small", ptr
,
3766 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr
, msize
);
3770 // We try to coalesce this block with the preceeding one
3771 if (index
&& (SMALL_PREVIOUS_MSIZE(ptr
) <= index
)) {
3772 previous_msize
= SMALL_PREVIOUS_MSIZE(ptr
);
3773 if (meta_headers
[index
- previous_msize
] == (previous_msize
| SMALL_IS_FREE
)) {
3774 previous
= (void *)((uintptr_t)ptr
- SMALL_BYTES_FOR_MSIZE(previous_msize
));
3775 // previous is really to be coalesced
3778 if (LOG(szone
, ptr
) || LOG(szone
,previous
)) {
3779 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr
, previous
);
3782 small_free_list_remove_ptr(szone
, small_mag_ptr
, previous
, previous_msize
);
3783 small_meta_header_set_middle(meta_headers
, index
);
3785 msize
+= previous_msize
;
3786 index
-= previous_msize
;
3789 // We try to coalesce with the next block
3790 if ((next_block
< SMALL_REGION_END(region
)) && (meta_headers
[next_index
] & SMALL_IS_FREE
)) {
3791 // next block is free, we coalesce
3793 next_msize
= meta_headers
[next_index
] & ~ SMALL_IS_FREE
;
3796 malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n",
3797 ptr
, msize
, next_block
, next_msize
);
3799 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
3800 small_meta_header_set_middle(meta_headers
, next_index
);
3801 msize
+= next_msize
;
3803 if (szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) {
3805 szone_error(szone
, 1, "incorrect size information - block header was damaged", ptr
, NULL
);
3807 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
3810 small_free_list_add_ptr(szone
, small_mag_ptr
, ptr
, msize
);
3811 small_mag_ptr
->mag_num_objects
--;
3812 // we use original_size and not msize to avoid double counting the coalesced blocks
3813 small_mag_ptr
->mag_num_bytes_in_objects
-= original_size
;
3815 // Update this region's bytes in use count
3816 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
3817 size_t bytes_used
= node
->bytes_used
- original_size
;
3818 node
->bytes_used
= bytes_used
;
3820 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
3821 if (szone
->num_small_magazines
== 1) { // Uniprocessor, single magazine, so no recirculation necessary
3823 } else if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
3824 // Emptiness discriminant
3825 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
3826 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
3827 recirculation candidates list. */
3828 node
->recirc_suitable
= TRUE
;
3830 /* After this free, we've found the region is still dense, so it must have been even more so before
3831 the free. That implies the region is already correctly marked. Do nothing. */
3834 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
3835 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
3836 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
3838 size_t a
= small_mag_ptr
->num_bytes_in_magazine
; // Total bytes allocated to this magazine
3839 size_t u
= small_mag_ptr
->mag_num_bytes_in_objects
; // In use (malloc'd) from this magaqzine
3841 if (a
- u
> ((3 * SMALL_REGION_PAYLOAD_BYTES
) / 2) && u
< DENSITY_THRESHOLD(a
))
3842 small_free_do_recirc_to_depot(szone
, small_mag_ptr
, mag_index
);
3845 // Freed to Depot. N.B. Lock on small_magazines[DEPOT_MAGAZINE_INDEX] is already held
3846 uintptr_t safe_ptr
= (uintptr_t)ptr
+ sizeof(free_list_t
) + sizeof(msize_t
);
3847 uintptr_t round_safe
= round_page(safe_ptr
);
3849 uintptr_t safe_extent
= (uintptr_t)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
);
3850 uintptr_t trunc_extent
= trunc_page(safe_extent
);
3852 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
3853 if (round_safe
< trunc_extent
) { // Safe area covers a page (perhaps many)
3854 if (did_prepend
& did_append
) { // Coalesced preceding with original_ptr *and* with following
3855 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
3856 uintptr_t rnd_safe_follow
=
3857 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
3859 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), MIN(rnd_safe_follow
, trunc_extent
));
3860 } else if (did_prepend
) { // Coalesced preceding with original_ptr
3861 uintptr_t trunc_safe_prev
= trunc_page((uintptr_t)original_ptr
- sizeof(msize_t
));
3863 madvise_free_range(szone
, region
, MAX(round_safe
, trunc_safe_prev
), trunc_extent
);
3864 } else if (did_append
) { // Coalesced original_ptr with following
3865 uintptr_t rnd_safe_follow
=
3866 round_page((uintptr_t)original_ptr
+ original_size
+ sizeof(free_list_t
) + sizeof(msize_t
));
3868 madvise_free_range(szone
, region
, round_safe
, MIN(rnd_safe_follow
, trunc_extent
));
3869 } else // Isolated free
3870 madvise_free_range(szone
, region
, round_safe
, trunc_extent
);
3873 if (0 < bytes_used
) {
3874 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
3875 so as to avoid thrashing between the Depot's free list and a magazines's free list
3876 with detach_region/reattach_region */
3878 /* Depot'd region is just now empty. Consider return to OS. */
3879 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(region
);
3880 magazine_t
*depot_ptr
= &(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
]);
3881 small_free_try_depot_unmap_no_lock(szone
, depot_ptr
, node
);
3886 // Allocates from the last region or a freshly allocated region
3888 small_malloc_from_region_no_lock(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
3890 void *ptr
, *aligned_address
;
3892 // Before anything we transform the mag_bytes_free_at_end - if any - to a regular free block
3893 /* FIXME: last_block needs to be coalesced with previous entry if free, <rdar://5462322> */
3894 if (small_mag_ptr
->mag_bytes_free_at_end
)
3895 small_finalize_region(szone
, small_mag_ptr
);
3897 // time to create a new region
3898 aligned_address
= allocate_pages(szone
, SMALL_REGION_SIZE
, SMALL_BLOCKS_ALIGN
, 0, VM_MEMORY_MALLOC_SMALL
);
3899 if (!aligned_address
)
3902 MAGMALLOC_ALLOCREGION((void *)szone
, (int)mag_index
); // DTrace USDT Probe
3904 // Here find the only place in smallville that (infrequently) takes the small_regions_lock.
3905 // Only one thread at a time should be permitted to assess the density of the hash
3906 // ring and adjust if needed.
3907 // Only one thread at a time should be permitted to insert its new region on
3909 // It is safe for all other threads to read the hash ring (hashed_regions) and
3910 // the associated sizes (num_regions_allocated and num_small_regions).
3912 LOCK(szone
->small_regions_lock
);
3913 // Check to see if the hash ring of small regions needs to grow. Try to
3914 // avoid the hash ring becoming too dense.
3915 if (szone
->small_region_generation
->num_regions_allocated
< (2 * szone
->num_small_regions
)) {
3916 region_t
*new_regions
;
3918 size_t new_shift
= szone
->small_region_generation
->num_regions_allocated_shift
; // In/Out parameter
3919 new_regions
= hash_regions_grow_no_lock(szone
, szone
->small_region_generation
->hashed_regions
,
3920 szone
->small_region_generation
->num_regions_allocated
,
3923 // Do not deallocate the current hashed_regions allocation since someone
3924 // may be iterating it. Instead, just leak it.
3926 // Prepare to advance to the "next generation" of the hash ring.
3927 szone
->small_region_generation
->nextgen
->hashed_regions
= new_regions
;
3928 szone
->small_region_generation
->nextgen
->num_regions_allocated
= new_size
;
3929 szone
->small_region_generation
->nextgen
->num_regions_allocated_shift
= new_shift
;
3931 // Throw the switch to atomically advance to the next generation.
3932 szone
->small_region_generation
= szone
->small_region_generation
->nextgen
;
3933 // Ensure everyone sees the advance.
3934 #if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1))) /* GCC 4.1 and forward supports atomic builtins */
3935 __sync_synchronize();
3940 // Tag the region at "aligned_address" as belonging to us,
3941 // and so put it under the protection of the magazine lock we are holding.
3942 // Do this before advertising "aligned_address" on the hash ring(!)
3943 MAGAZINE_INDEX_FOR_SMALL_REGION(aligned_address
) = mag_index
;
3945 // Insert the new region into the hash ring, and update malloc statistics
3946 hash_region_insert_no_lock(szone
->small_region_generation
->hashed_regions
,
3947 szone
->small_region_generation
->num_regions_allocated
,
3948 szone
->small_region_generation
->num_regions_allocated_shift
,
3951 szone
->num_small_regions
++;
3953 UNLOCK(szone
->small_regions_lock
);
3955 small_mag_ptr
->mag_last_region
= aligned_address
;
3956 BYTES_USED_FOR_SMALL_REGION(aligned_address
) = SMALL_BYTES_FOR_MSIZE(msize
);
3957 ptr
= aligned_address
;
3958 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), 0, msize
);
3959 small_mag_ptr
->mag_num_objects
++;
3960 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(msize
);
3961 small_mag_ptr
->num_bytes_in_magazine
+= SMALL_REGION_PAYLOAD_BYTES
;
3963 // add a big free block
3964 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
) , msize
, NUM_SMALL_BLOCKS
- msize
);
3965 small_mag_ptr
->mag_bytes_free_at_end
= SMALL_BYTES_FOR_MSIZE(NUM_SMALL_BLOCKS
- msize
);
3967 // connect to magazine as first node (it's maximally sparse at this moment)
3968 recirc_list_splice_first(szone
, small_mag_ptr
, REGION_TRAILER_FOR_SMALL_REGION(aligned_address
));
3973 static INLINE boolean_t
3974 small_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
3976 // returns 1 on success
3977 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
3979 msize_t old_msize
, new_msize
;
3980 unsigned next_index
;
3982 msize_t next_msize_and_free
;
3984 msize_t next_msize
, leftover_msize
;
3987 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
3988 old_msize
= SMALL_MSIZE_FOR_BYTES(old_size
);
3989 new_msize
= SMALL_MSIZE_FOR_BYTES(new_size
+ SMALL_QUANTUM
- 1);
3990 next_index
= index
+ old_msize
;
3992 if (next_index
>= NUM_SMALL_BLOCKS
) {
3995 next_block
= (char *)ptr
+ old_size
;
3998 if ((uintptr_t)next_block
& (SMALL_QUANTUM
- 1)) {
3999 szone_error(szone
, 1, "internal invariant broken in realloc(next_block)", next_block
, NULL
);
4001 if (meta_headers
[index
] != old_msize
)
4002 malloc_printf("*** small_try_realloc_in_place incorrect old %d %d\n",
4003 meta_headers
[index
], old_msize
);
4006 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
4007 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)),
4008 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
)));
4011 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
4014 next_msize_and_free
= meta_headers
[next_index
];
4015 is_free
= next_msize_and_free
& SMALL_IS_FREE
;
4017 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4018 return 0; // next_block is in use;
4020 next_msize
= next_msize_and_free
& ~ SMALL_IS_FREE
;
4021 if (old_msize
+ next_msize
< new_msize
) {
4022 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4023 return 0; // even with next block, not enough
4026 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
4029 small_free_list_remove_ptr(szone
, small_mag_ptr
, next_block
, next_msize
);
4030 small_meta_header_set_middle(meta_headers
, next_index
);
4031 leftover_msize
= old_msize
+ next_msize
- new_msize
;
4032 if (leftover_msize
) {
4033 /* there's some left, so put the remainder back */
4034 leftover
= (unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(new_msize
);
4036 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover
, leftover_msize
);
4039 if (SMALL_BYTES_FOR_MSIZE(new_msize
) > szone
->large_threshold
) {
4040 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize
);
4043 small_meta_header_set_in_use(meta_headers
, index
, new_msize
);
4045 if (LOG(szone
,ptr
)) {
4046 malloc_printf("in szone_realloc(), ptr=%p, msize=%d\n", ptr
, *SMALL_METADATA_FOR_PTR(ptr
));
4049 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4051 // Update this region's bytes in use count
4052 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4053 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(new_msize
- old_msize
);
4054 node
->bytes_used
= bytes_used
;
4056 // Emptiness discriminant
4057 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4058 /* After this reallocation the region is still sparse, so it must have been even more so before
4059 the reallocation. That implies the region is already correctly marked. Do nothing. */
4061 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
4062 recirculation candidates list. */
4063 node
->recirc_suitable
= FALSE
;
4066 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4067 CHECK(szone
, __PRETTY_FUNCTION__
);
4072 small_check_region(szone_t
*szone
, region_t region
)
4074 unsigned char *ptr
= SMALL_REGION_ADDRESS(region
);
4075 msize_t
*meta_headers
= SMALL_META_HEADER_FOR_PTR(ptr
);
4076 unsigned char *region_end
= SMALL_REGION_END(region
);
4077 msize_t prev_free
= 0;
4079 msize_t msize_and_free
;
4081 free_list_t
*free_head
;
4082 void *previous
, *next
;
4084 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4085 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4088 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4090 if (region
== small_mag_ptr
->mag_last_region
)
4091 region_end
-= small_mag_ptr
->mag_bytes_free_at_end
;
4093 while (ptr
< region_end
) {
4094 index
= SMALL_META_INDEX_FOR_PTR(ptr
);
4095 msize_and_free
= meta_headers
[index
];
4096 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4098 msize
= msize_and_free
;
4100 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
4101 ptr
, szone
->num_small_regions
, region_end
);
4104 if (SMALL_BYTES_FOR_MSIZE(msize
) > szone
->large_threshold
) {
4105 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
4106 ptr
, msize_and_free
);
4109 ptr
+= SMALL_BYTES_FOR_MSIZE(msize
);
4113 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4114 free_head
= (free_list_t
*)ptr
;
4115 follower
= (msize_t
*)FOLLOWING_SMALL_PTR(ptr
, msize
);
4117 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr
, msize
);
4121 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr
);
4124 previous
= free_list_unchecksum_ptr(szone
, &free_head
->previous
);
4125 next
= free_list_unchecksum_ptr(szone
, &free_head
->next
);
4126 if (previous
&& !SMALL_PTR_IS_FREE(previous
)) {
4127 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
4128 ptr
, free_head
->previous
);
4131 if (next
&& !SMALL_PTR_IS_FREE(next
)) {
4132 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr
);
4135 if (SMALL_PREVIOUS_MSIZE(follower
) != msize
) {
4136 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
4137 "(end marker incorrect) should be %d; in fact %d\n",
4138 ptr
, follower
, SMALL_REGION_ADDRESS(region
), region_end
, msize
, SMALL_PREVIOUS_MSIZE(follower
));
4141 ptr
= (unsigned char *)follower
;
4142 prev_free
= SMALL_IS_FREE
;
4148 static kern_return_t
4149 small_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, szone_t
*szone
,
4150 memory_reader_t reader
, vm_range_recorder_t recorder
)
4155 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4160 vm_range_t admin_range
;
4161 vm_range_t ptr_range
;
4162 unsigned char *mapped_region
;
4163 msize_t
*block_header
;
4164 unsigned block_index
;
4165 unsigned block_limit
;
4166 msize_t msize_and_free
;
4168 vm_address_t mag_last_free_ptr
= 0;
4169 msize_t mag_last_free_msize
= 0;
4171 region_hash_generation_t
*srg_ptr
;
4172 err
= reader(task
, (vm_address_t
)szone
->small_region_generation
, sizeof(region_hash_generation_t
), (void **)&srg_ptr
);
4173 if (err
) return err
;
4175 num_regions
= srg_ptr
->num_regions_allocated
;
4176 err
= reader(task
, (vm_address_t
)srg_ptr
->hashed_regions
, sizeof(region_t
) * num_regions
, (void **)®ions
);
4177 if (err
) return err
;
4179 for (index
= 0; index
< num_regions
; ++index
) {
4180 region
= regions
[index
];
4181 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
4182 range
.address
= (vm_address_t
)SMALL_REGION_ADDRESS(region
);
4183 range
.size
= SMALL_REGION_SIZE
;
4184 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4185 admin_range
.address
= range
.address
+ SMALL_METADATA_START
;
4186 admin_range
.size
= SMALL_METADATA_SIZE
;
4187 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &admin_range
, 1);
4189 if (type_mask
& (MALLOC_PTR_REGION_RANGE_TYPE
| MALLOC_ADMIN_REGION_RANGE_TYPE
)) {
4190 ptr_range
.address
= range
.address
;
4191 ptr_range
.size
= NUM_SMALL_BLOCKS
* SMALL_QUANTUM
;
4192 recorder(task
, context
, MALLOC_PTR_REGION_RANGE_TYPE
, &ptr_range
, 1);
4194 if (type_mask
& MALLOC_PTR_IN_USE_RANGE_TYPE
) {
4195 err
= reader(task
, range
.address
, range
.size
, (void **)&mapped_region
);
4199 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(mapped_region
);
4200 magazine_t
*small_mag_ptr
;
4201 err
= reader(task
, (vm_address_t
)&(szone
->small_magazines
[mag_index
]), sizeof(magazine_t
),
4202 (void **)&small_mag_ptr
);
4203 if (err
) return err
;
4205 void *mag_last_free
= small_mag_ptr
->mag_last_free
;
4206 if (mag_last_free
) {
4207 mag_last_free_ptr
= (uintptr_t) mag_last_free
& ~(SMALL_QUANTUM
- 1);
4208 mag_last_free_msize
= (uintptr_t) mag_last_free
& (SMALL_QUANTUM
- 1);
4211 block_header
= (msize_t
*)(mapped_region
+ SMALL_METADATA_START
+ sizeof(region_trailer_t
));
4213 block_limit
= NUM_SMALL_BLOCKS
;
4214 if (region
== small_mag_ptr
->mag_last_region
)
4215 block_limit
-= SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
);
4216 while (block_index
< block_limit
) {
4217 msize_and_free
= block_header
[block_index
];
4218 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4219 if (! (msize_and_free
& SMALL_IS_FREE
) &&
4220 range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
) != mag_last_free_ptr
) {
4222 buffer
[count
].address
= range
.address
+ SMALL_BYTES_FOR_MSIZE(block_index
);
4223 buffer
[count
].size
= SMALL_BYTES_FOR_MSIZE(msize
);
4225 if (count
>= MAX_RECORDER_BUFFER
) {
4226 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4230 block_index
+= msize
;
4233 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
, buffer
, count
);
4243 small_malloc_from_free_list(szone_t
*szone
, magazine_t
*small_mag_ptr
, mag_index_t mag_index
, msize_t msize
)
4247 grain_t slot
= (msize
<= szone
->num_small_slots
) ? msize
- 1 : szone
->num_small_slots
- 1;
4248 free_list_t
**free_list
= small_mag_ptr
->mag_free_list
;
4249 free_list_t
**the_slot
= free_list
+ slot
;
4251 free_list_t
**limit
;
4253 msize_t leftover_msize
;
4254 free_list_t
*leftover_ptr
;
4256 // Assumes we've locked the region
4257 CHECK_MAGAZINE_PTR_LOCKED(szone
, small_mag_ptr
, __PRETTY_FUNCTION__
);
4259 // Look for an exact match by checking the freelist for this msize.
4263 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4265 next
->previous
= ptr
->previous
;
4267 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4271 goto return_small_alloc
;
4274 // Mask off the bits representing slots holding free blocks smaller than
4275 // the size we need. If there are no larger free blocks, try allocating
4276 // from the free space at the end of the small region.
4277 if (szone
->is_largemem
) {
4278 // BITMAPN_CTZ implementation
4279 unsigned idx
= slot
>> 5;
4281 unsigned mask
= ~ ((1 << (slot
& 31)) - 1);
4282 for ( ; idx
< SMALL_BITMAP_WORDS
; ++idx
) {
4283 bitmap
= small_mag_ptr
->mag_bitmap
[idx
] & mask
;
4288 // Check for fallthrough: No bits set in bitmap
4289 if ((bitmap
== 0) && (idx
== SMALL_BITMAP_WORDS
))
4290 goto try_small_from_end
;
4292 // Start looking at the first set bit, plus 32 bits for every word of
4293 // zeroes or entries that were too small.
4294 slot
= BITMAP32_CTZ((&bitmap
)) + (idx
* 32);
4296 bitmap
= small_mag_ptr
->mag_bitmap
[0] & ~ ((1 << slot
) - 1);
4298 goto try_small_from_end
;
4300 slot
= BITMAP32_CTZ((&bitmap
));
4302 // FIXME: Explain use of - 1 here, last slot has special meaning
4303 limit
= free_list
+ szone
->num_small_slots
- 1;
4306 if (free_list
< limit
) {
4310 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4313 next
->previous
= ptr
->previous
;
4315 BITMAPN_CLR(small_mag_ptr
->mag_bitmap
, slot
);
4317 this_msize
= SMALL_PTR_SIZE(ptr
);
4318 goto add_leftover_and_proceed
;
4321 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot
);
4325 // We are now looking at the last slot, which contains blocks equal to, or
4326 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4327 // If the last freelist is not empty, and the head contains a block that is
4328 // larger than our request, then the remainder is put back on the free list.
4332 this_msize
= SMALL_PTR_SIZE(ptr
);
4333 next
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4334 if (this_msize
- msize
>= szone
->num_small_slots
) {
4335 // the leftover will go back to the free list, so we optimize by
4336 // modifying the free list rather than a pop and push of the head
4337 leftover_msize
= this_msize
- msize
;
4338 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4339 *limit
= leftover_ptr
;
4341 next
->previous
.u
= free_list_checksum_ptr(szone
, leftover_ptr
);
4343 leftover_ptr
->previous
= ptr
->previous
;
4344 leftover_ptr
->next
= ptr
->next
;
4345 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(leftover_ptr
),
4346 SMALL_META_INDEX_FOR_PTR(leftover_ptr
), leftover_msize
);
4347 // Store msize at the end of the block denoted by "leftover_ptr" (i.e. at a negative offset from follower)
4348 SMALL_PREVIOUS_MSIZE(FOLLOWING_SMALL_PTR(leftover_ptr
, leftover_msize
)) = leftover_msize
; // Access is safe
4350 if (LOG(szone
,ptr
)) {
4351 malloc_printf("in small_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr
, msize
, this_msize
);
4355 goto return_small_alloc
;
4358 next
->previous
= ptr
->previous
;
4361 goto add_leftover_and_proceed
;
4365 // Let's see if we can use small_mag_ptr->mag_bytes_free_at_end
4366 if (small_mag_ptr
->mag_bytes_free_at_end
>= SMALL_BYTES_FOR_MSIZE(msize
)) {
4367 ptr
= (free_list_t
*)(SMALL_REGION_END(small_mag_ptr
->mag_last_region
) -
4368 small_mag_ptr
->mag_bytes_free_at_end
);
4369 small_mag_ptr
->mag_bytes_free_at_end
-= SMALL_BYTES_FOR_MSIZE(msize
);
4370 if (small_mag_ptr
->mag_bytes_free_at_end
) {
4371 // let's mark this block as in use to serve as boundary
4372 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
),
4373 SMALL_META_INDEX_FOR_PTR((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
)),
4374 SMALL_MSIZE_FOR_BYTES(small_mag_ptr
->mag_bytes_free_at_end
));
4377 goto return_small_alloc
;
4381 add_leftover_and_proceed
:
4382 if (this_msize
> msize
) {
4383 leftover_msize
= this_msize
- msize
;
4384 leftover_ptr
= (free_list_t
*)((unsigned char *)ptr
+ SMALL_BYTES_FOR_MSIZE(msize
));
4386 if (LOG(szone
,ptr
)) {
4387 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr
, this_msize
);
4390 small_free_list_add_ptr(szone
, small_mag_ptr
, leftover_ptr
, leftover_msize
);
4395 small_mag_ptr
->mag_num_objects
++;
4396 small_mag_ptr
->mag_num_bytes_in_objects
+= SMALL_BYTES_FOR_MSIZE(this_msize
);
4398 // Update this region's bytes in use count
4399 region_trailer_t
*node
= REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4400 size_t bytes_used
= node
->bytes_used
+ SMALL_BYTES_FOR_MSIZE(this_msize
);
4401 node
->bytes_used
= bytes_used
;
4403 // Emptiness discriminant
4404 if (bytes_used
< DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES
)) {
4405 /* After this allocation the region is still sparse, so it must have been even more so before
4406 the allocation. That implies the region is already correctly marked. Do nothing. */
4408 /* Region has crossed threshold from sparsity to density. Mark in not "suitable" on the
4409 recirculation candidates list. */
4410 node
->recirc_suitable
= FALSE
;
4413 if (LOG(szone
,ptr
)) {
4414 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr
, this_msize
, msize
);
4417 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr
), SMALL_META_INDEX_FOR_PTR(ptr
), this_msize
);
4420 #undef DENSITY_THRESHOLD
4423 static INLINE
void *
4424 small_malloc_should_clear(szone_t
*szone
, msize_t msize
, boolean_t cleared_requested
)
4427 mag_index_t mag_index
= mag_get_thread_index(szone
);
4428 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4430 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4433 ptr
= (void *)small_mag_ptr
->mag_last_free
;
4435 if ((((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) == msize
) {
4437 small_mag_ptr
->mag_last_free
= NULL
;
4438 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4439 CHECK(szone
, __PRETTY_FUNCTION__
);
4440 ptr
= (void *)((uintptr_t)ptr
& ~ (SMALL_QUANTUM
- 1));
4441 if (cleared_requested
) {
4442 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4446 #endif /* SMALL_CACHE */
4448 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
4450 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4451 CHECK(szone
, __PRETTY_FUNCTION__
);
4452 if (cleared_requested
) {
4453 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4458 if (small_get_region_from_depot(szone
, small_mag_ptr
, mag_index
)) {
4459 ptr
= small_malloc_from_free_list(szone
, small_mag_ptr
, mag_index
, msize
);
4461 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4462 CHECK(szone
, __PRETTY_FUNCTION__
);
4463 if (cleared_requested
) {
4464 memset(ptr
, 0, SMALL_BYTES_FOR_MSIZE(msize
));
4470 ptr
= small_malloc_from_region_no_lock(szone
, small_mag_ptr
, mag_index
, msize
);
4471 // we don't clear because this freshly allocated space is pristine
4472 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4473 CHECK(szone
, __PRETTY_FUNCTION__
);
4477 static NOINLINE
void
4478 free_small_botch(szone_t
*szone
, free_list_t
*ptr
)
4480 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4481 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4482 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4483 szone_error(szone
, 1, "double free", ptr
, NULL
);
4487 free_small(szone_t
*szone
, void *ptr
, region_t small_region
, size_t known_size
)
4490 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
4491 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4493 // ptr is known to be in small_region
4495 msize
= SMALL_MSIZE_FOR_BYTES(known_size
+ SMALL_QUANTUM
- 1);
4497 msize
= SMALL_PTR_SIZE(ptr
);
4498 if (SMALL_PTR_IS_FREE(ptr
)) {
4499 free_small_botch(szone
, ptr
);
4504 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4507 // Depot does not participate in SMALL_CACHE since it can't be directly malloc()'d
4508 if (DEPOT_MAGAZINE_INDEX
!= mag_index
) {
4510 void *ptr2
= small_mag_ptr
->mag_last_free
; // Might be NULL
4511 region_t rgn2
= small_mag_ptr
->mag_last_free_rgn
;
4513 /* check that we don't already have this pointer in the cache */
4514 if (ptr
== (void *)((uintptr_t)ptr2
& ~ (SMALL_QUANTUM
- 1))) {
4515 free_small_botch(szone
, ptr
);
4519 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && msize
)
4520 memset(ptr
, 0x55, SMALL_BYTES_FOR_MSIZE(msize
));
4522 small_mag_ptr
->mag_last_free
= (void *)(((uintptr_t)ptr
) | msize
);
4523 small_mag_ptr
->mag_last_free_rgn
= small_region
;
4526 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4527 CHECK(szone
, __PRETTY_FUNCTION__
);
4531 msize
= (uintptr_t)ptr2
& (SMALL_QUANTUM
- 1);
4532 ptr
= (void *)(((uintptr_t)ptr2
) & ~(SMALL_QUANTUM
- 1));
4533 small_region
= rgn2
;
4535 #endif /* SMALL_CACHE */
4537 // Now in the time it took to acquire the lock, the region may have migrated
4538 // from one magazine to another. I.e. trailer->mag_index is volatile.
4539 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
4540 // is stale. If so, keep on tryin' ...
4541 region_trailer_t
*trailer
= REGION_TRAILER_FOR_SMALL_REGION(small_region
);
4542 mag_index_t refreshed_index
;
4544 while (mag_index
!= (refreshed_index
= trailer
->mag_index
)) { // Note assignment
4546 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4548 mag_index
= refreshed_index
;
4549 small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4550 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4553 small_free_no_lock(szone
, small_mag_ptr
, mag_index
, small_region
, ptr
, msize
);
4554 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4555 CHECK(szone
, __PRETTY_FUNCTION__
);
4559 print_small_free_list(szone_t
*szone
)
4562 _SIMPLE_STRING b
= _simple_salloc();
4563 mag_index_t mag_index
;
4566 _simple_sappend(b
, "small free sizes:\n");
4567 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4569 _simple_sprintf(b
,"\tMagazine %d: ", mag_index
);
4570 while (slot
< szone
->num_small_slots
) {
4571 ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
4573 _simple_sprintf(b
, "%s%y[%d]; ", (slot
== szone
->num_small_slots
-1) ? ">=" : "",
4574 (slot
+ 1) * SMALL_QUANTUM
, free_list_count(szone
, ptr
));
4578 _simple_sappend(b
,"\n");
4580 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4586 print_small_region(szone_t
*szone
, boolean_t verbose
, region_t region
, size_t bytes_at_end
)
4588 unsigned counts
[1024];
4589 unsigned in_use
= 0;
4590 uintptr_t start
= (uintptr_t)SMALL_REGION_ADDRESS(region
);
4591 uintptr_t current
= start
;
4592 uintptr_t limit
= (uintptr_t)SMALL_REGION_END(region
) - bytes_at_end
;
4593 msize_t msize_and_free
;
4597 uintptr_t pgTot
= 0;
4599 if (region
== HASHRING_REGION_DEALLOCATED
) {
4600 if ((b
= _simple_salloc()) != NULL
) {
4601 _simple_sprintf(b
, "Small region [unknown address] was returned to the OS\n");
4602 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4608 memset(counts
, 0, sizeof(counts
));
4609 while (current
< limit
) {
4610 msize_and_free
= *SMALL_METADATA_FOR_PTR(current
);
4611 msize
= msize_and_free
& ~ SMALL_IS_FREE
;
4613 malloc_printf("*** error with %p: msize=%d\n", (void *)current
, (unsigned)msize
);
4616 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4622 uintptr_t pgLo
= round_page(current
+ sizeof(free_list_t
) + sizeof(msize_t
));
4623 uintptr_t pgHi
= trunc_page(current
+ TINY_BYTES_FOR_MSIZE(msize
) - sizeof(msize_t
));
4626 pgTot
+= (pgHi
- pgLo
);
4629 current
+= SMALL_BYTES_FOR_MSIZE(msize
);
4631 if ((b
= _simple_salloc()) != NULL
) {
4632 _simple_sprintf(b
, "Small region [%p-%p, %y] \t", (void *)start
, SMALL_REGION_END(region
), (int)SMALL_REGION_SIZE
);
4633 _simple_sprintf(b
, "Magazine=%d \t", MAGAZINE_INDEX_FOR_SMALL_REGION(region
));
4634 _simple_sprintf(b
, "Allocations in use=%d \t Bytes in use=%ly \t", in_use
, BYTES_USED_FOR_SMALL_REGION(region
));
4636 _simple_sprintf(b
, "Untouched=%ly ", bytes_at_end
);
4637 if (DEPOT_MAGAZINE_INDEX
== MAGAZINE_INDEX_FOR_SMALL_REGION(region
)) {
4638 _simple_sprintf(b
, "Advised MADV_FREE=%ly", pgTot
);
4640 _simple_sprintf(b
, "Fragments subject to reclamation=%ly", pgTot
);
4642 if (verbose
&& in_use
) {
4643 _simple_sappend(b
, "\n\tSizes in use: ");
4644 for (ci
= 0; ci
< 1024; ci
++)
4646 _simple_sprintf(b
, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci
), counts
[ci
]);
4648 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4654 small_free_list_check(szone_t
*szone
, grain_t slot
)
4656 mag_index_t mag_index
;
4658 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
4659 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
4660 SZONE_MAGAZINE_PTR_LOCK(szone
, small_mag_ptr
);
4663 free_list_t
*ptr
= szone
->small_magazines
[mag_index
].mag_free_list
[slot
];
4664 msize_t msize_and_free
;
4665 free_list_t
*previous
= NULL
;
4668 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
4669 if (!(msize_and_free
& SMALL_IS_FREE
)) {
4670 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4671 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4674 if (((uintptr_t)ptr
) & (SMALL_QUANTUM
- 1)) {
4675 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4676 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4679 if (!small_region_for_ptr_no_lock(szone
, ptr
)) {
4680 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4681 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4684 if (free_list_unchecksum_ptr(szone
, &ptr
->previous
) != previous
) {
4685 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot
, count
, ptr
);
4686 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4690 ptr
= free_list_unchecksum_ptr(szone
, &ptr
->next
);
4694 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
4699 /*******************************************************************************
4700 * Large allocator implementation
4701 ******************************************************************************/
4702 #pragma mark large allocator
4707 large_debug_print(szone_t
*szone
)
4710 large_entry_t
*range
;
4711 _SIMPLE_STRING b
= _simple_salloc();
4714 for (index
= 0, range
= szone
->large_entries
; index
< szone
->num_large_entries
; index
++, range
++)
4716 _simple_sprintf(b
, "%d: %p(%y); ", index
, range
->address
, range
->size
);
4718 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
, "%s\n", _simple_string(b
));
4725 * Scan the hash ring looking for an entry for the given pointer.
4727 static large_entry_t
*
4728 large_entry_for_pointer_no_lock(szone_t
*szone
, const void *ptr
)
4730 // result only valid with lock held
4731 unsigned num_large_entries
= szone
->num_large_entries
;
4732 unsigned hash_index
;
4734 large_entry_t
*range
;
4736 if (!num_large_entries
)
4739 hash_index
= ((uintptr_t)ptr
>> vm_page_shift
) % num_large_entries
;
4743 range
= szone
->large_entries
+ index
;
4744 if (range
->address
== (vm_address_t
)ptr
)
4746 if (0 == range
->address
)
4747 return NULL
; // end of chain
4749 if (index
== num_large_entries
)
4751 } while (index
!= hash_index
);
4757 large_entry_insert_no_lock(szone_t
*szone
, large_entry_t range
)
4759 unsigned num_large_entries
= szone
->num_large_entries
;
4760 unsigned hash_index
= (((uintptr_t)(range
.address
)) >> vm_page_shift
) % num_large_entries
;
4761 unsigned index
= hash_index
;
4762 large_entry_t
*entry
;
4764 // assert(szone->num_large_objects_in_use < szone->num_large_entries); /* must be called with room to spare */
4767 entry
= szone
->large_entries
+ index
;
4768 if (0 == entry
->address
) {
4770 return; // end of chain
4773 if (index
== num_large_entries
)
4775 } while (index
!= hash_index
);
4777 // assert(0); /* must not fallthrough! */
4780 // FIXME: can't we simply swap the (now empty) entry with the last entry on the collision chain for this hash slot?
4782 large_entries_rehash_after_entry_no_lock(szone_t
*szone
, large_entry_t
*entry
)
4784 unsigned num_large_entries
= szone
->num_large_entries
;
4785 unsigned hash_index
= entry
- szone
->large_entries
;
4786 unsigned index
= hash_index
;
4787 large_entry_t range
;
4789 // assert(entry->address == 0) /* caller must have cleared *entry */
4793 if (index
== num_large_entries
)
4795 range
= szone
->large_entries
[index
];
4796 if (0 == range
.address
)
4798 szone
->large_entries
[index
].address
= (vm_address_t
)0;
4799 szone
->large_entries
[index
].size
= 0;
4800 szone
->large_entries
[index
].did_madvise_reusable
= FALSE
;
4801 large_entry_insert_no_lock(szone
, range
); // this will reinsert in the
4803 } while (index
!= hash_index
);
4805 // assert(0); /* since entry->address == 0, must not fallthrough! */
4808 // FIXME: num should probably be a size_t, since you can theoretically allocate
4809 // more than 2^32-1 large_threshold objects in 64 bit.
4810 static INLINE large_entry_t
*
4811 large_entries_alloc_no_lock(szone_t
*szone
, unsigned num
)
4813 size_t size
= num
* sizeof(large_entry_t
);
4815 // Note that we allocate memory (via a system call) under a spin lock
4816 // That is certainly evil, however it's very rare in the lifetime of a process
4817 // The alternative would slow down the normal case
4818 return allocate_pages(szone
, round_page(size
), 0, 0, VM_MEMORY_MALLOC_LARGE
);
4822 large_entries_free_no_lock(szone_t
*szone
, large_entry_t
*entries
, unsigned num
, vm_range_t
*range_to_deallocate
)
4824 size_t size
= num
* sizeof(large_entry_t
);
4826 range_to_deallocate
->address
= (vm_address_t
)entries
;
4827 range_to_deallocate
->size
= round_page(size
);
4830 static large_entry_t
*
4831 large_entries_grow_no_lock(szone_t
*szone
, vm_range_t
*range_to_deallocate
)
4833 // sets range_to_deallocate
4834 unsigned old_num_entries
= szone
->num_large_entries
;
4835 large_entry_t
*old_entries
= szone
->large_entries
;
4836 // always an odd number for good hashing
4837 unsigned new_num_entries
= (old_num_entries
) ? old_num_entries
* 2 + 1 :
4838 ((vm_page_size
/ sizeof(large_entry_t
)) - 1);
4839 large_entry_t
*new_entries
= large_entries_alloc_no_lock(szone
, new_num_entries
);
4840 unsigned index
= old_num_entries
;
4841 large_entry_t oldRange
;
4843 // if the allocation of new entries failed, bail
4844 if (new_entries
== NULL
)
4847 szone
->num_large_entries
= new_num_entries
;
4848 szone
->large_entries
= new_entries
;
4850 /* rehash entries into the new list */
4852 oldRange
= old_entries
[index
];
4853 if (oldRange
.address
) {
4854 large_entry_insert_no_lock(szone
, oldRange
);
4859 large_entries_free_no_lock(szone
, old_entries
, old_num_entries
, range_to_deallocate
);
4861 range_to_deallocate
->address
= (vm_address_t
)0;
4862 range_to_deallocate
->size
= 0;
4868 // frees the specific entry in the size table
4869 // returns a range to truly deallocate
4871 large_entry_free_no_lock(szone_t
*szone
, large_entry_t
*entry
)
4875 range
.address
= entry
->address
;
4876 range
.size
= entry
->size
;
4878 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
4879 protect((void *)range
.address
, range
.size
, VM_PROT_READ
| VM_PROT_WRITE
, szone
->debug_flags
);
4880 range
.address
-= vm_page_size
;
4881 range
.size
+= 2 * vm_page_size
;
4886 entry
->did_madvise_reusable
= FALSE
;
4887 large_entries_rehash_after_entry_no_lock(szone
, entry
);
4890 if (large_entry_for_pointer_no_lock(szone
, (void *)range
.address
)) {
4891 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
4892 range
.address
, szone
->num_large_entries
);
4893 large_debug_print(szone
);
4900 static NOINLINE kern_return_t
4901 large_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t large_entries_address
,
4902 unsigned num_entries
, memory_reader_t reader
, vm_range_recorder_t recorder
)
4905 vm_range_t buffer
[MAX_RECORDER_BUFFER
];
4907 large_entry_t
*entries
;
4910 large_entry_t entry
;
4912 err
= reader(task
, large_entries_address
, sizeof(large_entry_t
) * num_entries
, (void **)&entries
);
4916 index
= num_entries
;
4917 if (type_mask
& MALLOC_ADMIN_REGION_RANGE_TYPE
) {
4918 range
.address
= large_entries_address
;
4919 range
.size
= round_page(num_entries
* sizeof(large_entry_t
));
4920 recorder(task
, context
, MALLOC_ADMIN_REGION_RANGE_TYPE
, &range
, 1);
4922 if (type_mask
& (MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
)) {
4924 entry
= entries
[index
];
4925 if (entry
.address
) {
4926 range
.address
= entry
.address
;
4927 range
.size
= entry
.size
;
4928 buffer
[count
++] = range
;
4929 if (count
>= MAX_RECORDER_BUFFER
) {
4930 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
4938 recorder(task
, context
, MALLOC_PTR_IN_USE_RANGE_TYPE
| MALLOC_PTR_REGION_RANGE_TYPE
,
4945 large_malloc(szone_t
*szone
, size_t num_pages
, unsigned char alignment
,
4946 boolean_t cleared_requested
)
4949 vm_range_t range_to_deallocate
;
4951 large_entry_t large_entry
;
4954 num_pages
= 1; // minimal allocation size for this szone
4955 size
= (size_t)num_pages
<< vm_page_shift
;
4956 range_to_deallocate
.size
= 0;
4957 range_to_deallocate
.address
= 0;
4960 if (size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
) { // Look for a large_entry_t on the death-row cache?
4963 int i
, best
= -1, idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
4964 size_t best_size
= SIZE_T_MAX
;
4966 while (1) { // Scan large_entry_cache for best fit, starting with most recent entry
4967 size_t this_size
= szone
->large_entry_cache
[idx
].size
;
4969 if (size
== this_size
) { // size match!
4971 best_size
= this_size
;
4975 if (size
<= this_size
&& this_size
< best_size
) { // improved fit?
4977 best_size
= this_size
;
4980 if (idx
== stop_idx
) // exhausted live ring?
4984 idx
--; // bump idx down
4986 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
4989 if (best
> -1 && (best_size
- size
) < size
) { //limit fragmentation to 50%
4990 addr
= (void *)szone
->large_entry_cache
[best
].address
;
4991 boolean_t was_madvised_reusable
= szone
->large_entry_cache
[best
].did_madvise_reusable
;
4993 // Compact live ring to fill entry now vacated at large_entry_cache[best]
4994 // while preserving time-order
4995 if (szone
->large_entry_cache_oldest
< szone
->large_entry_cache_newest
) {
4997 // Ring hasn't wrapped. Fill in from right.
4998 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
4999 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5001 szone
->large_entry_cache_newest
--; // Pull in right endpoint.
5003 } else if (szone
->large_entry_cache_newest
< szone
->large_entry_cache_oldest
) {
5005 // Ring has wrapped. Arrange to fill in from the contiguous side.
5006 if (best
<= szone
->large_entry_cache_newest
) {
5008 for (i
= best
; i
< szone
->large_entry_cache_newest
; ++i
)
5009 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
+ 1];
5011 if (0 < szone
->large_entry_cache_newest
)
5012 szone
->large_entry_cache_newest
--;
5014 szone
->large_entry_cache_newest
= LARGE_ENTRY_CACHE_SIZE
- 1;
5017 for ( i
= best
; i
> szone
->large_entry_cache_oldest
; --i
)
5018 szone
->large_entry_cache
[i
] = szone
->large_entry_cache
[i
- 1];
5020 if (szone
->large_entry_cache_oldest
< LARGE_ENTRY_CACHE_SIZE
- 1)
5021 szone
->large_entry_cache_oldest
++;
5023 szone
->large_entry_cache_oldest
= 0;
5027 // By trichotomy, large_entry_cache_newest == large_entry_cache_oldest.
5028 // That implies best == large_entry_cache_newest == large_entry_cache_oldest
5029 // and the ring is now empty.
5030 szone
->large_entry_cache
[best
].address
= 0;
5031 szone
->large_entry_cache
[best
].size
= 0;
5032 szone
->large_entry_cache
[best
].did_madvise_reusable
= FALSE
;
5035 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5036 // density of hash table too high; grow table
5037 // we do that under lock to avoid a race
5038 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5039 if (entries
== NULL
) {
5040 SZONE_UNLOCK(szone
);
5045 large_entry
.address
= (vm_address_t
)addr
;
5046 large_entry
.size
= best_size
;
5047 large_entry
.did_madvise_reusable
= FALSE
;
5048 large_entry_insert_no_lock(szone
, large_entry
);
5050 szone
->num_large_objects_in_use
++;
5051 szone
->num_bytes_in_large_objects
+= best_size
;
5052 if (!was_madvised_reusable
)
5053 szone
->large_entry_cache_hoard_bytes
-= best_size
;
5054 SZONE_UNLOCK(szone
);
5056 if (range_to_deallocate
.size
) {
5057 // we deallocate outside the lock
5058 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5061 // Perform the madvise() outside the lock.
5062 // Typically the madvise() is successful and we'll quickly return from this routine.
5063 // In the unusual case of failure, reacquire the lock to unwind.
5064 if (was_madvised_reusable
&& -1 == madvise(addr
, size
, MADV_FREE_REUSE
)) {
5065 /* -1 return: VM map entry change makes this unfit for reuse. */
5067 szone_error(szone
, 1, "large_malloc madvise(..., MADV_FREE_REUSE) failed", addr
, NULL
);
5071 szone
->num_large_objects_in_use
--;
5072 szone
->num_bytes_in_large_objects
-= large_entry
.size
;
5074 // Re-acquire "entry" after interval just above where we let go the lock.
5075 large_entry_t
*entry
= large_entry_for_pointer_no_lock(szone
, addr
);
5076 if (NULL
== entry
) {
5077 szone_error(szone
, 1, "entry for pointer being discarded from death-row vanished", addr
, NULL
);
5078 SZONE_UNLOCK(szone
);
5081 range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5082 SZONE_UNLOCK(szone
);
5084 if (range_to_deallocate
.size
) {
5085 // we deallocate outside the lock
5086 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5089 /* Fall through to allocate_pages() afresh. */
5091 if (cleared_requested
) {
5092 memset(addr
, 0, size
);
5098 SZONE_UNLOCK(szone
);
5102 range_to_deallocate
.size
= 0;
5103 range_to_deallocate
.address
= 0;
5104 #endif /* LARGE_CACHE */
5106 addr
= allocate_pages(szone
, size
, alignment
, szone
->debug_flags
, VM_MEMORY_MALLOC_LARGE
);
5112 if ((szone
->num_large_objects_in_use
+ 1) * 4 > szone
->num_large_entries
) {
5113 // density of hash table too high; grow table
5114 // we do that under lock to avoid a race
5115 large_entry_t
*entries
= large_entries_grow_no_lock(szone
, &range_to_deallocate
);
5116 if (entries
== NULL
) {
5117 SZONE_UNLOCK(szone
);
5122 large_entry
.address
= (vm_address_t
)addr
;
5123 large_entry
.size
= size
;
5124 large_entry
.did_madvise_reusable
= FALSE
;
5125 large_entry_insert_no_lock(szone
, large_entry
);
5127 szone
->num_large_objects_in_use
++;
5128 szone
->num_bytes_in_large_objects
+= size
;
5129 SZONE_UNLOCK(szone
);
5131 if (range_to_deallocate
.size
) {
5132 // we deallocate outside the lock
5133 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, range_to_deallocate
.size
, 0);
5138 static NOINLINE
void
5139 free_large(szone_t
*szone
, void *ptr
)
5141 // We have established ptr is page-aligned and neither tiny nor small
5142 large_entry_t
*entry
;
5143 vm_range_t vm_range_to_deallocate
;
5146 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5149 #ifndef MADV_CAN_REUSE
5150 #define MADV_CAN_REUSE 9 /* per Francois, for testing until xnu is resubmitted to B&I */
5152 if (entry
->size
< LARGE_CACHE_SIZE_ENTRY_LIMIT
&&
5153 -1 != madvise((void *)(entry
->address
), entry
->size
, MADV_CAN_REUSE
)) { // Put the large_entry_t on the death-row cache?
5154 int idx
= szone
->large_entry_cache_newest
, stop_idx
= szone
->large_entry_cache_oldest
;
5155 large_entry_t this_entry
= *entry
; // Make a local copy, "entry" is volatile when lock is let go.
5156 boolean_t reusable
= TRUE
;
5157 boolean_t should_madvise
= szone
->large_entry_cache_hoard_bytes
+ this_entry
.size
> szone
->large_entry_cache_hoard_lmit
;
5160 // [Note that repeated entries in death-row risk vending the same entry subsequently
5161 // to two different malloc() calls. By checking here the (illegal) double free
5162 // is accommodated, matching the behavior of the previous implementation.]
5163 while (1) { // Scan large_entry_cache starting with most recent entry
5164 if (szone
->large_entry_cache
[idx
].address
== entry
->address
) {
5165 szone_error(szone
, 1, "pointer being freed already on death-row", ptr
, NULL
);
5166 SZONE_UNLOCK(szone
);
5170 if (idx
== stop_idx
) // exhausted live ring?
5174 idx
--; // bump idx down
5176 idx
= LARGE_ENTRY_CACHE_SIZE
- 1; // wrap idx
5179 SZONE_UNLOCK(szone
);
5181 if (szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) { // Are we a purgable zone?
5182 int state
= VM_PURGABLE_NONVOLATILE
; // restore to default condition
5184 if (KERN_SUCCESS
!= vm_purgable_control(mach_task_self(), this_entry
.address
, VM_PURGABLE_SET_STATE
, &state
)) {
5185 malloc_printf("*** can't vm_purgable_control(..., VM_PURGABLE_SET_STATE) for large freed block at %p\n", this_entry
.address
);
5190 if (szone
->large_legacy_reset_mprotect
) { // Linked for Leopard?
5191 // Accomodate Leopard apps that (illegally) mprotect() their own guard pages on large malloc'd allocations
5192 kern_return_t err
= vm_protect(mach_task_self(), (vm_address_t
)(this_entry
.address
), this_entry
.size
,
5193 0, PROT_READ
| PROT_WRITE
);
5195 malloc_printf("*** can't reset protection for large freed block at %p\n", this_entry
.address
);
5200 // madvise(..., MADV_REUSABLE) death-row arrivals if hoarding would exceed large_entry_cache_hoard_lmit
5201 if (should_madvise
) {
5202 // Issue madvise to avoid paging out the dirtied free()'d pages in "entry"
5203 MAGMALLOC_MADVFREEREGION((void *)szone
, (void *)0, (void *)(this_entry
.address
), this_entry
.size
); // DTrace USDT Probe
5205 if (-1 == madvise((void *)(this_entry
.address
), this_entry
.size
, MADV_FREE_REUSABLE
)) {
5206 /* -1 return: VM map entry change makes this unfit for reuse. */
5208 szone_error(szone
, 1, "free_large madvise(..., MADV_FREE_REUSABLE) failed", (void *)this_entry
.address
, NULL
);
5216 // Re-acquire "entry" after interval just above where we let go the lock.
5217 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5218 if (NULL
== entry
) {
5219 szone_error(szone
, 1, "entry for pointer being freed from death-row vanished", ptr
, NULL
);
5220 SZONE_UNLOCK(szone
);
5224 // Add "entry" to death-row ring
5226 int idx
= szone
->large_entry_cache_newest
; // Most recently occupied
5230 if (szone
->large_entry_cache_newest
== szone
->large_entry_cache_oldest
&&
5231 0 == szone
->large_entry_cache
[idx
].address
) {
5232 // Ring is empty, idx is good as it stands
5236 // Extend the queue to the "right" by bumping up large_entry_cache_newest
5237 if (idx
== LARGE_ENTRY_CACHE_SIZE
- 1)
5238 idx
= 0; // Wrap index
5240 idx
++; // Bump index
5242 if (idx
== szone
->large_entry_cache_oldest
) { // Fully occupied
5243 // Drop this entry from the cache and deallocate the VM
5244 addr
= szone
->large_entry_cache
[idx
].address
;
5245 adjsize
= szone
->large_entry_cache
[idx
].size
;
5246 if (!szone
->large_entry_cache
[idx
].did_madvise_reusable
)
5247 szone
->large_entry_cache_hoard_bytes
-= adjsize
;
5249 // Using an unoccupied cache slot
5255 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
))
5256 memset((void *)(entry
->address
), 0x55, entry
->size
);
5258 entry
->did_madvise_reusable
= should_madvise
; // Was madvise()'d above?
5259 if (!should_madvise
) // Entered on death-row without madvise() => up the hoard total
5260 szone
->large_entry_cache_hoard_bytes
+= entry
->size
;
5262 szone
->large_entry_cache
[idx
] = *entry
;
5263 szone
->large_entry_cache_newest
= idx
;
5265 szone
->num_large_objects_in_use
--;
5266 szone
->num_bytes_in_large_objects
-= entry
->size
;
5268 (void)large_entry_free_no_lock(szone
, entry
);
5271 SZONE_UNLOCK(szone
);
5275 // Fall through to drop large_entry_cache_oldest from the cache,
5276 // and then deallocate its pages.
5278 // Trim the queue on the "left" by bumping up large_entry_cache_oldest
5279 if (szone
->large_entry_cache_oldest
== LARGE_ENTRY_CACHE_SIZE
- 1)
5280 szone
->large_entry_cache_oldest
= 0;
5282 szone
->large_entry_cache_oldest
++;
5284 // we deallocate_pages, including guard pages, outside the lock
5285 SZONE_UNLOCK(szone
);
5286 deallocate_pages(szone
, (void *)addr
, (size_t)adjsize
, 0);
5289 /* fall through to discard an allocation that is not reusable */
5292 #endif /* LARGE_CACHE */
5294 szone
->num_large_objects_in_use
--;
5295 szone
->num_bytes_in_large_objects
-= entry
->size
;
5297 vm_range_to_deallocate
= large_entry_free_no_lock(szone
, entry
);
5300 large_debug_print(szone
);
5302 szone_error(szone
, 1, "pointer being freed was not allocated", ptr
, NULL
);
5303 SZONE_UNLOCK(szone
);
5306 SZONE_UNLOCK(szone
); // we release the lock asap
5307 CHECK(szone
, __PRETTY_FUNCTION__
);
5309 // we deallocate_pages, including guard pages, outside the lock
5310 if (vm_range_to_deallocate
.address
) {
5312 // FIXME: large_entry_for_pointer_no_lock() needs the lock held ...
5313 if (large_entry_for_pointer_no_lock(szone
, (void *)vm_range_to_deallocate
.address
)) {
5314 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n",
5315 vm_range_to_deallocate
.address
, szone
->num_large_entries
);
5316 large_debug_print(szone
);
5320 deallocate_pages(szone
, (void *)vm_range_to_deallocate
.address
, (size_t)vm_range_to_deallocate
.size
, 0);
5325 large_try_realloc_in_place(szone_t
*szone
, void *ptr
, size_t old_size
, size_t new_size
)
5327 vm_address_t addr
= (vm_address_t
)ptr
+ old_size
;
5328 large_entry_t
*large_entry
;
5332 large_entry
= large_entry_for_pointer_no_lock(szone
, (void *)addr
);
5333 SZONE_UNLOCK(szone
);
5335 if (large_entry
) { // check if "addr = ptr + old_size" is already spoken for
5336 return 0; // large pointer already exists in table - extension is not going to work
5339 new_size
= round_page(new_size
);
5341 * Ask for allocation at a specific address, and mark as realloc
5342 * to request coalescing with previous realloc'ed extensions.
5344 err
= vm_allocate(mach_task_self(), &addr
, new_size
- old_size
, VM_MAKE_TAG(VM_MEMORY_REALLOC
));
5345 if (err
!= KERN_SUCCESS
) {
5350 /* extend existing large entry */
5351 large_entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5353 szone_error(szone
, 1, "large entry reallocated is not properly in table", ptr
, NULL
);
5354 SZONE_UNLOCK(szone
);
5355 return 0; // Bail, leaking "addr"
5358 large_entry
->address
= (vm_address_t
)ptr
;
5359 large_entry
->size
= new_size
;
5360 szone
->num_bytes_in_large_objects
+= new_size
- old_size
;
5361 SZONE_UNLOCK(szone
); // we release the lock asap
5366 /********************* Zone call backs ************************/
5368 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
5370 static NOINLINE
void
5371 szone_free(szone_t
*szone
, void *ptr
)
5373 region_t tiny_region
;
5374 region_t small_region
;
5377 if (LOG(szone
, ptr
))
5378 malloc_printf("in szone_free with %p\n", ptr
);
5383 * Try to free to a tiny region.
5385 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
5386 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
5389 if ((tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
5390 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
5391 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
5394 free_tiny(szone
, ptr
, tiny_region
, 0);
5399 * Try to free to a small region.
5401 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
5402 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
5405 if ((small_region
= small_region_for_ptr_no_lock(szone
, ptr
)) != NULL
) {
5406 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
5407 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
5410 free_small(szone
, ptr
, small_region
, 0);
5414 /* check that it's a legal large allocation */
5415 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
5416 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
5419 free_large(szone
, ptr
);
5422 static NOINLINE
void
5423 szone_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
5426 if (LOG(szone
, ptr
))
5427 malloc_printf("in szone_free_definite_size with %p\n", ptr
);
5430 szone_error(szone
, 1, "pointer of size zero being freed", ptr
, NULL
);
5439 * Try to free to a tiny region.
5441 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1)) {
5442 szone_error(szone
, 1, "Non-aligned pointer being freed", ptr
, NULL
);
5445 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5446 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
) {
5447 szone_error(szone
, 1, "Pointer to metadata being freed", ptr
, NULL
);
5450 free_tiny(szone
, ptr
, TINY_REGION_FOR_PTR(ptr
), size
);
5455 * Try to free to a small region.
5457 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1)) {
5458 szone_error(szone
, 1, "Non-aligned pointer being freed (2)", ptr
, NULL
);
5461 if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5462 (size
<= szone
->large_threshold
)) {
5463 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
) {
5464 szone_error(szone
, 1, "Pointer to metadata being freed (2)", ptr
, NULL
);
5467 free_small(szone
, ptr
, SMALL_REGION_FOR_PTR(ptr
), size
);
5471 /* check that it's a legal large allocation */
5472 if ((uintptr_t)ptr
& (vm_page_size
- 1)) {
5473 szone_error(szone
, 1, "non-page-aligned, non-allocated pointer being freed", ptr
, NULL
);
5476 free_large(szone
, ptr
);
5479 static NOINLINE
void *
5480 szone_malloc_should_clear(szone_t
*szone
, size_t size
, boolean_t cleared_requested
)
5485 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5487 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5490 ptr
= tiny_malloc_should_clear(szone
, msize
, cleared_requested
);
5491 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5492 (size
<= szone
->large_threshold
)) {
5494 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
5497 ptr
= small_malloc_should_clear(szone
, msize
, cleared_requested
);
5500 size_t num_pages
= round_page(size
) >> vm_page_shift
;
5501 if (num_pages
== 0) /* Overflowed */
5504 ptr
= large_malloc(szone
, num_pages
, 0, cleared_requested
);
5507 if (LOG(szone
, ptr
))
5508 malloc_printf("szone_malloc returned %p\n", ptr
);
5511 * If requested, scribble on allocated memory.
5513 if ((szone
->debug_flags
& SCALABLE_MALLOC_DO_SCRIBBLE
) && ptr
&& !cleared_requested
&& size
)
5514 memset(ptr
, 0xaa, size
);
5519 static NOINLINE
void *
5520 szone_malloc(szone_t
*szone
, size_t size
) {
5521 return szone_malloc_should_clear(szone
, size
, 0);
5524 static NOINLINE
void *
5525 szone_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
5527 size_t total_bytes
= num_items
* size
;
5529 // Check for overflow of integer multiplication
5530 if (num_items
> 1) {
5531 #if __LP64__ /* size_t is uint64_t */
5532 if ((num_items
| size
) & 0xffffffff00000000ul
) {
5533 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
5534 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
5535 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
5538 #else /* size_t is uint32_t */
5539 if ((num_items
| size
) & 0xffff0000ul
) {
5540 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
5541 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
5542 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
5548 return szone_malloc_should_clear(szone
, total_bytes
, 1);
5551 static NOINLINE
void *
5552 szone_valloc(szone_t
*szone
, size_t size
)
5556 if (size
<= szone
->large_threshold
) {
5557 ptr
= szone_memalign(szone
, vm_page_size
, size
);
5561 num_pages
= round_page(size
) >> vm_page_shift
;
5562 ptr
= large_malloc(szone
, num_pages
, 0, 0);
5566 if (LOG(szone
, ptr
))
5567 malloc_printf("szone_valloc returned %p\n", ptr
);
5572 /* Isolate PIC-base load (for __is_threaded) here. */
5573 static NOINLINE
size_t
5574 szone_size_try_large(szone_t
*szone
, const void *ptr
)
5577 large_entry_t
*entry
;
5580 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
5584 SZONE_UNLOCK(szone
);
5586 if (LOG(szone
, ptr
)) {
5587 malloc_printf("szone_size for %p returned %d\n", ptr
, (unsigned)size
);
5593 static NOINLINE
size_t
5594 szone_size(szone_t
*szone
, const void *ptr
)
5597 msize_t msize
, msize_and_free
;
5602 if (LOG(szone
, ptr
)) {
5603 malloc_printf("in szone_size for %p (szone=%p)\n", ptr
, szone
);
5608 * Look for it in a tiny region.
5610 if ((uintptr_t)ptr
& (TINY_QUANTUM
- 1))
5612 if (tiny_region_for_ptr_no_lock(szone
, ptr
)) {
5613 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
5615 msize
= get_tiny_meta_header(ptr
, &is_free
);
5620 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr
));
5621 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
5623 if (msize
< TINY_QUANTUM
&& ptr
== (void *)((uintptr_t)(tiny_mag_ptr
->mag_last_free
) & ~ (TINY_QUANTUM
- 1)))
5627 return TINY_BYTES_FOR_MSIZE(msize
);
5631 * Look for it in a small region.
5633 if ((uintptr_t)ptr
& (SMALL_QUANTUM
- 1))
5635 if (small_region_for_ptr_no_lock(szone
, ptr
)) {
5636 if (SMALL_META_INDEX_FOR_PTR(ptr
) >= NUM_SMALL_BLOCKS
)
5638 msize_and_free
= *SMALL_METADATA_FOR_PTR(ptr
);
5639 if (msize_and_free
& SMALL_IS_FREE
)
5643 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr
));
5644 magazine_t
*small_mag_ptr
= &(szone
->small_magazines
[mag_index
]);
5646 if (ptr
== (void *)((uintptr_t)(small_mag_ptr
->mag_last_free
) & ~ (SMALL_QUANTUM
- 1)))
5650 return SMALL_BYTES_FOR_MSIZE(msize_and_free
);
5654 * If not page-aligned, it cannot have come from a large allocation.
5656 if ((uintptr_t)ptr
& (vm_page_size
- 1))
5660 * Look for it in a large entry.
5662 return szone_size_try_large(szone
, ptr
);
5665 static NOINLINE
void *
5666 szone_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
5672 if (LOG(szone
, ptr
)) {
5673 malloc_printf("in szone_realloc for %p, %d\n", ptr
, (unsigned)new_size
);
5677 ptr
= szone_malloc(szone
, new_size
);
5680 old_size
= szone_size(szone
, ptr
);
5682 szone_error(szone
, 1, "pointer being reallocated was not allocated", ptr
, NULL
);
5685 /* we never shrink an allocation */
5686 if (old_size
>= new_size
)
5690 * If the new size suits the tiny allocator and the pointer being resized
5691 * belongs to a tiny region, try to reallocate in-place.
5693 if ((new_size
+ TINY_QUANTUM
- 1) <= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
5694 if (tiny_region_for_ptr_no_lock(szone
, ptr
) != NULL
) {
5695 if (tiny_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5701 * If the new size suits the small allocator and the pointer being resized
5702 * belongs to a small region, and we're not protecting the small allocations
5703 * try to reallocate in-place.
5705 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5706 ((new_size
+ SMALL_QUANTUM
- 1) <= szone
->large_threshold
) &&
5707 (small_region_for_ptr_no_lock(szone
, ptr
) != NULL
)) {
5708 if (small_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5713 * If the allocation's a large allocation, try to reallocate in-place there.
5715 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) &&
5716 !(szone
->debug_flags
& SCALABLE_MALLOC_PURGEABLE
) &&
5717 (old_size
> szone
->large_threshold
)) {
5718 if (large_try_realloc_in_place(szone
, ptr
, old_size
, new_size
)) {
5724 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
5726 new_ptr
= szone_malloc(szone
, new_size
);
5727 if (new_ptr
== NULL
)
5731 * If the allocation's large enough, try to copy using VM. If that fails, or
5732 * if it's too small, just copy by hand.
5734 if ((old_size
< szone
->vm_copy_threshold
) ||
5735 vm_copy(mach_task_self(), (vm_address_t
)ptr
, old_size
, (vm_address_t
)new_ptr
))
5736 memcpy(new_ptr
, ptr
, old_size
);
5737 szone_free(szone
, ptr
);
5740 if (LOG(szone
, ptr
)) {
5741 malloc_printf("szone_realloc returned %p for %d\n", new_ptr
, (unsigned)new_size
);
5747 static NOINLINE
void *
5748 szone_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
5750 if ((size
+ alignment
) < size
) // size_t arithmetic wrapped!
5753 // alignment is gauranteed a power of 2 at least as large as sizeof(void *), hence non-zero.
5754 // Since size + alignment didn't wrap, 0 <= size + alignment - 1 < size + alignment
5755 size_t span
= size
+ alignment
- 1;
5757 if (alignment
<= TINY_QUANTUM
) {
5758 return szone_malloc(szone
, size
); // Trivially satisfied by tiny, small, or large
5760 } else if (span
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5761 msize_t mspan
= TINY_MSIZE_FOR_BYTES(span
+ TINY_QUANTUM
- 1);
5762 void *p
= szone_malloc(szone
, span
); // avoids inlining tiny_malloc_should_clear(szone, mspan, 0);
5767 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
5768 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
5770 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5771 msize_t mpad
= TINY_MSIZE_FOR_BYTES(pad
+ TINY_QUANTUM
- 1);
5772 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
5775 void *q
= (void *)(((uintptr_t) p
) + pad
);
5777 // Mark q as a block header and in-use, thus creating two blocks.
5778 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5779 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
5780 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
5781 set_tiny_meta_header_in_use(q
, msize
);
5783 // set_tiny_meta_header_in_use() "reaffirms" the block_header on the *following* block, so
5784 // now set its in_use bit as well. But only if its within the original allocation made above.
5786 BITARRAY_SET(TINY_INUSE_FOR_HEADER(TINY_BLOCK_HEADER_FOR_PTR(q
)), TINY_INDEX_FOR_PTR(q
) + msize
);
5787 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5789 // Give up mpad blocks beginning at p to the tiny free list
5790 // region_t r = TINY_REGION_FOR_PTR(p);
5791 szone_free(szone
, p
); // avoids inlining free_tiny(szone, p, &r);
5793 p
= q
; // advance p to the desired alignment
5797 void *q
= (void *)(((uintptr_t) p
) + TINY_BYTES_FOR_MSIZE(msize
));
5798 // Mark q as block header and in-use, thus creating two blocks.
5799 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5800 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)),
5801 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p
)));
5802 set_tiny_meta_header_in_use(q
, mwaste
);
5803 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5805 // Give up mwaste blocks beginning at q to the tiny free list
5806 // region_t r = TINY_REGION_FOR_PTR(q);
5807 szone_free(szone
, q
); // avoids inlining free_tiny(szone, q, &r);
5810 return p
; // p has the desired size and alignment, and can later be free()'d
5812 } else if ((NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
< size
&& alignment
<= SMALL_QUANTUM
) {
5813 return szone_malloc(szone
, size
); // Trivially satisfied by small or large
5815 } else if (!((szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
) && (span
<= szone
->large_threshold
)) {
5817 if (size
<= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
) {
5818 size
= (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
+ TINY_QUANTUM
; // ensure block allocated by small does not have a tiny-possible size
5819 span
= size
+ alignment
- 1;
5822 msize_t mspan
= SMALL_MSIZE_FOR_BYTES(span
+ SMALL_QUANTUM
- 1);
5823 void *p
= szone_malloc(szone
, span
); // avoid inlining small_malloc_should_clear(szone, mspan, 0);
5828 size_t offset
= ((uintptr_t) p
) & (alignment
- 1); // p % alignment
5829 size_t pad
= (0 == offset
) ? 0 : alignment
- offset
; // p + pad achieves desired alignment
5831 msize_t msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
5832 msize_t mpad
= SMALL_MSIZE_FOR_BYTES(pad
+ SMALL_QUANTUM
- 1);
5833 msize_t mwaste
= mspan
- msize
- mpad
; // excess blocks
5836 void *q
= (void *)(((uintptr_t) p
) + pad
);
5838 // Mark q as block header and in-use, thus creating two blocks.
5839 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
5840 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
5841 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
5842 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), mpad
);
5843 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), msize
+ mwaste
);
5844 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5846 // Give up mpad blocks beginning at p to the small free list
5847 // region_t r = SMALL_REGION_FOR_PTR(p);
5848 szone_free(szone
, p
); // avoid inlining free_small(szone, p, &r);
5850 p
= q
; // advance p to the desired alignment
5853 void *q
= (void *)(((uintptr_t) p
) + SMALL_BYTES_FOR_MSIZE(msize
));
5854 // Mark q as block header and in-use, thus creating two blocks.
5855 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
5856 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)),
5857 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p
)));
5858 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p
), SMALL_META_INDEX_FOR_PTR(p
), msize
);
5859 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q
), SMALL_META_INDEX_FOR_PTR(q
), mwaste
);
5860 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
5862 // Give up mwaste blocks beginning at q to the small free list
5863 // region_t r = SMALL_REGION_FOR_PTR(q);
5864 szone_free(szone
, q
); // avoid inlining free_small(szone, q, &r);
5867 return p
; // p has the desired size and alignment, and can later be free()'d
5869 } else if (szone
->large_threshold
< size
&& alignment
<= vm_page_size
) {
5870 return szone_malloc(szone
, size
); // Trivially satisfied by large
5873 // ensure block allocated by large does not have a small-possible size
5874 size_t num_pages
= round_page(MAX(szone
->large_threshold
+ 1, size
)) >> vm_page_shift
;
5877 if (num_pages
== 0) /* Overflowed */
5880 p
= large_malloc(szone
, num_pages
, MAX(vm_page_shift
, __builtin_ctz(alignment
)), 0);
5887 // given a size, returns the number of pointers allocated capable of holding
5888 // that size, up to the limit specified by the 'count' argument. These pointers
5889 // are stored in the 'results' array, which must be allocated by the caller.
5890 // may return zero, since this function is only a best attempt at allocating
5891 // the pointers. clients should be prepared to call malloc for any additional
5892 // blocks they need.
5893 static NOINLINE
unsigned
5894 szone_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
5896 msize_t msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
5898 mag_index_t mag_index
= mag_get_thread_index(szone
);
5899 magazine_t
*tiny_mag_ptr
= &(szone
->tiny_magazines
[mag_index
]);
5901 // only bother implementing this for tiny
5902 if (size
> (NUM_TINY_SLOTS
- 1)*TINY_QUANTUM
)
5904 // make sure to return objects at least one quantum in size
5908 CHECK(szone
, __PRETTY_FUNCTION__
);
5910 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
5911 // the caller has done so.
5912 SZONE_MAGAZINE_PTR_LOCK(szone
, tiny_mag_ptr
);
5914 // with the zone locked, allocate objects from the free list until all
5915 // sufficiently large objects have been exhausted, or we have met our quota
5916 // of objects to allocate.
5917 while (found
< count
) {
5918 void *ptr
= tiny_malloc_from_free_list(szone
, tiny_mag_ptr
, mag_index
, msize
);
5925 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5929 /* Try caching the tiny_region and checking if the next ptr hits there. */
5930 static NOINLINE
void
5931 szone_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
5935 region_t tiny_region
= NULL
;
5938 magazine_t
*tiny_mag_ptr
= NULL
;
5939 mag_index_t mag_index
= -1;
5941 // frees all the pointers in to_be_freed
5942 // note that to_be_freed may be overwritten during the process
5946 CHECK(szone
, __PRETTY_FUNCTION__
);
5947 while (cc
< count
) {
5948 ptr
= to_be_freed
[cc
];
5950 if (NULL
== tiny_region
|| tiny_region
!= TINY_REGION_FOR_PTR(ptr
)) { // region same as last iteration?
5951 if (tiny_mag_ptr
) { // non-NULL iff magazine lock taken
5952 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5953 tiny_mag_ptr
= NULL
;
5956 tiny_region
= tiny_region_for_ptr_no_lock(szone
, ptr
);
5959 tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
5960 REGION_TRAILER_FOR_TINY_REGION(tiny_region
),
5961 MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
));
5962 mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region
);
5966 // this is a tiny pointer
5967 if (TINY_INDEX_FOR_PTR(ptr
) >= NUM_TINY_BLOCKS
)
5968 break; // pointer to metadata; let the standard free deal with it
5969 msize
= get_tiny_meta_header(ptr
, &is_free
);
5971 break; // a double free; let the standard free deal with it
5973 tiny_free_no_lock(szone
, tiny_mag_ptr
, mag_index
, tiny_region
, ptr
, msize
);
5974 to_be_freed
[cc
] = NULL
;
5976 // No region in this zone claims ptr; let the standard free deal with it
5984 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
5985 tiny_mag_ptr
= NULL
;
5988 CHECK(szone
, __PRETTY_FUNCTION__
);
5990 ptr
= to_be_freed
[count
];
5992 szone_free(szone
, ptr
);
5996 // FIXME: Suppose one of the locks is held?
5998 szone_destroy(szone_t
*szone
)
6001 large_entry_t
*large
;
6002 vm_range_t range_to_deallocate
;
6004 /* destroy large entries */
6005 index
= szone
->num_large_entries
;
6007 large
= szone
->large_entries
+ index
;
6008 if (large
->address
) {
6009 // we deallocate_pages, including guard pages
6010 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6013 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6014 if (range_to_deallocate
.size
)
6015 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6017 /* destroy tiny regions */
6018 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
)
6019 if ((HASHRING_OPEN_ENTRY
!= szone
->tiny_region_generation
->hashed_regions
[index
]) &&
6020 (HASHRING_REGION_DEALLOCATED
!= szone
->tiny_region_generation
->hashed_regions
[index
]))
6021 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
[index
], TINY_REGION_SIZE
, 0);
6023 /* destroy small regions */
6024 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
)
6025 if ((HASHRING_OPEN_ENTRY
!= szone
->small_region_generation
->hashed_regions
[index
]) &&
6026 (HASHRING_REGION_DEALLOCATED
!= szone
->small_region_generation
->hashed_regions
[index
]))
6027 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
[index
], SMALL_REGION_SIZE
, 0);
6029 /* destroy region hash rings, if any */
6030 if (szone
->tiny_region_generation
->hashed_regions
!= szone
->initial_tiny_regions
) {
6031 size_t size
= round_page(szone
->tiny_region_generation
->num_regions_allocated
* sizeof(region_t
));
6032 deallocate_pages(szone
, szone
->tiny_region_generation
->hashed_regions
, size
, 0);
6034 if (szone
->small_region_generation
->hashed_regions
!= szone
->initial_small_regions
) {
6035 size_t size
= round_page(szone
->small_region_generation
->num_regions_allocated
* sizeof(region_t
));
6036 deallocate_pages(szone
, szone
->small_region_generation
->hashed_regions
, size
, 0);
6039 /* Now destroy the separate szone region */
6040 if (szone
->cpu_id_key
!= (pthread_key_t
) -1)
6041 (void)pthread_key_delete(szone
->cpu_id_key
);
6042 deallocate_pages(szone
, (void *)&(szone
->tiny_magazines
[-1]), TINY_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6043 deallocate_pages(szone
, (void *)&(szone
->small_magazines
[-1]), SMALL_MAGAZINE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6044 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6047 static NOINLINE
size_t
6048 szone_good_size(szone_t
*szone
, size_t size
)
6051 int guard_small
= (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) && PROTECT_SMALL
;
6053 // Find a good size for this tiny allocation.
6054 if (size
<= (NUM_TINY_SLOTS
- 1) * TINY_QUANTUM
) {
6055 msize
= TINY_MSIZE_FOR_BYTES(size
+ TINY_QUANTUM
- 1);
6058 return TINY_BYTES_FOR_MSIZE(msize
);
6061 // Find a good size for this small allocation.
6062 if (!guard_small
&& (size
<= szone
->large_threshold
)) {
6063 msize
= SMALL_MSIZE_FOR_BYTES(size
+ SMALL_QUANTUM
- 1);
6066 return SMALL_BYTES_FOR_MSIZE(msize
);
6069 // Check for integer overflow on the size, since unlike the two cases above,
6070 // there is no upper bound on allocation size at this point.
6071 if (size
> round_page(size
))
6072 return (size_t)(-1LL);
6075 // It is not acceptable to see a size of zero here, since that means we
6076 // failed to catch a request for zero bytes in the tiny check, or the size
6077 // overflowed to zero during some arithmetic.
6079 malloc_printf("szone_good_size() invariant broken %y\n", size
);
6081 return round_page(size
);
6084 unsigned szone_check_counter
= 0;
6085 unsigned szone_check_start
= 0;
6086 unsigned szone_check_modulo
= 1;
6088 static NOINLINE boolean_t
6089 szone_check_all(szone_t
*szone
, const char *function
)
6093 /* check tiny regions - chould check region count */
6094 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6095 region_t tiny
= szone
->tiny_region_generation
->hashed_regions
[index
];
6097 if (HASHRING_REGION_DEALLOCATED
== tiny
)
6101 magazine_t
*tiny_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->tiny_magazines
,
6102 REGION_TRAILER_FOR_TINY_REGION(tiny
), MAGAZINE_INDEX_FOR_TINY_REGION(tiny
));
6104 if (!tiny_check_region(szone
, tiny
)) {
6105 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6106 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6107 szone_error(szone
, 1, "check: tiny region incorrect", NULL
,
6108 "*** tiny region %ld incorrect szone_check_all(%s) counter=%d\n",
6109 index
, function
, szone_check_counter
);
6112 SZONE_MAGAZINE_PTR_UNLOCK(szone
, tiny_mag_ptr
);
6115 /* check tiny free lists */
6116 for (index
= 0; index
< NUM_TINY_SLOTS
; ++index
) {
6117 if (!tiny_free_list_check(szone
, index
)) {
6118 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6119 szone_error(szone
, 1, "check: tiny free list incorrect", NULL
,
6120 "*** tiny free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6121 index
, function
, szone_check_counter
);
6126 /* check small regions - could check region count */
6127 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6128 region_t small
= szone
->small_region_generation
->hashed_regions
[index
];
6130 if (HASHRING_REGION_DEALLOCATED
== small
)
6134 magazine_t
*small_mag_ptr
= mag_lock_zine_for_region_trailer(szone
, szone
->small_magazines
,
6135 REGION_TRAILER_FOR_SMALL_REGION(small
), MAGAZINE_INDEX_FOR_SMALL_REGION(small
));
6137 if (!small_check_region(szone
, small
)) {
6138 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6139 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6140 szone_error(szone
, 1, "check: small region incorrect", NULL
,
6141 "*** small region %ld incorrect szone_check_all(%s) counter=%d\n",
6142 index
, function
, szone_check_counter
);
6145 SZONE_MAGAZINE_PTR_UNLOCK(szone
, small_mag_ptr
);
6148 /* check small free lists */
6149 for (index
= 0; index
< szone
->num_small_slots
; ++index
) {
6150 if (!small_free_list_check(szone
, index
)) {
6151 szone
->debug_flags
&= ~ CHECK_REGIONS
;
6152 szone_error(szone
, 1, "check: small free list incorrect", NULL
,
6153 "*** small free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6154 index
, function
, szone_check_counter
);
6163 szone_check(szone_t
*szone
)
6165 if ((++szone_check_counter
% 10000) == 0)
6166 _malloc_printf(ASL_LEVEL_NOTICE
, "at szone_check counter=%d\n", szone_check_counter
);
6168 if (szone_check_counter
< szone_check_start
)
6171 if (szone_check_counter
% szone_check_modulo
)
6174 return szone_check_all(szone
, "");
6177 static kern_return_t
6178 szone_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
6179 memory_reader_t reader
, vm_range_recorder_t recorder
)
6184 if (!reader
) reader
= _szone_default_reader
;
6186 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
6187 if (err
) return err
;
6189 err
= tiny_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
6190 if (err
) return err
;
6192 err
= small_in_use_enumerator(task
, context
, type_mask
, szone
, reader
, recorder
);
6193 if (err
) return err
;
6195 err
= large_in_use_enumerator(task
, context
, type_mask
,
6196 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
6200 // Following method is deprecated: use scalable_zone_statistics instead
6202 scalable_zone_info(malloc_zone_t
*zone
, unsigned *info_to_fill
, unsigned count
)
6204 szone_t
*szone
= (void *)zone
;
6207 // We do not lock to facilitate debug
6212 mag_index_t mag_index
;
6214 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6215 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6216 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6217 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6223 for (t
= 0, u
= 0, mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6224 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6225 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6226 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6232 info
[8] = szone
->num_large_objects_in_use
;
6233 info
[9] = szone
->num_bytes_in_large_objects
;
6235 info
[10] = 0; // DEPRECATED szone->num_huge_entries;
6236 info
[11] = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6238 info
[12] = szone
->debug_flags
;
6240 info
[0] = info
[4] + info
[6] + info
[8] + info
[10];
6241 info
[1] = info
[5] + info
[7] + info
[9] + info
[11];
6243 info
[3] = (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
6244 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ info
[9] + info
[11];
6246 info
[2] = info
[3] - s
;
6247 memcpy(info_to_fill
, info
, sizeof(unsigned)*count
);
6250 // FIXME: consistent picture requires locking!
6251 static NOINLINE
void
6252 szone_print(szone_t
*szone
, boolean_t verbose
)
6258 scalable_zone_info((void *)szone
, info
, 13);
6259 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6260 "Scalable zone %p: inUse=%d(%y) touched=%y allocated=%y flags=%d\n",
6261 szone
, info
[0], info
[1], info
[2], info
[3], info
[12]);
6262 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6263 "\ttiny=%d(%y) small=%d(%y) large=%d(%y) huge=%d(%y)\n",
6264 info
[4], info
[5], info
[6], info
[7], info
[8], info
[9], info
[10], info
[11]);
6266 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6267 "%d tiny regions:\n", szone
->num_tiny_regions
);
6268 if (szone
->num_tiny_regions_dealloc
)
6269 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6270 "[%d tiny regions have been vm_deallocate'd]\n", szone
->num_tiny_regions_dealloc
);
6271 for (index
= 0; index
< szone
->tiny_region_generation
->num_regions_allocated
; ++index
) {
6272 region
= szone
->tiny_region_generation
->hashed_regions
[index
];
6273 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
6274 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_TINY_REGION(region
);
6275 print_tiny_region(verbose
, region
, (region
== szone
->tiny_magazines
[mag_index
].mag_last_region
) ?
6276 szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
6280 print_tiny_free_list(szone
);
6282 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6283 "%d small regions:\n", szone
->num_small_regions
);
6284 if (szone
->num_small_regions_dealloc
)
6285 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6286 "[%d small regions have been vm_deallocate'd]\n", szone
->num_small_regions_dealloc
);
6287 for (index
= 0; index
< szone
->small_region_generation
->num_regions_allocated
; ++index
) {
6288 region
= szone
->small_region_generation
->hashed_regions
[index
];
6289 if (HASHRING_OPEN_ENTRY
!= region
&& HASHRING_REGION_DEALLOCATED
!= region
) {
6290 mag_index_t mag_index
= MAGAZINE_INDEX_FOR_SMALL_REGION(region
);
6291 print_small_region(szone
, verbose
, region
,
6292 (region
== szone
->small_magazines
[mag_index
].mag_last_region
) ?
6293 szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
: 0);
6297 print_small_free_list(szone
);
6301 szone_log(malloc_zone_t
*zone
, void *log_address
)
6303 szone_t
*szone
= (szone_t
*)zone
;
6305 szone
->log_address
= log_address
;
6309 szone_force_lock(szone_t
*szone
)
6313 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
6314 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6316 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
])));
6318 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
6319 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[i
])));
6321 SZONE_MAGAZINE_PTR_LOCK(szone
, (&(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
])));
6327 szone_force_unlock(szone_t
*szone
)
6331 SZONE_UNLOCK(szone
);
6333 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
6334 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
6337 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
6338 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6343 szone_locked(szone_t
*szone
)
6348 tookLock
= SZONE_TRY_LOCK(szone
);
6351 SZONE_UNLOCK(szone
);
6353 for (i
= -1; i
< szone
->num_small_magazines
; ++i
) {
6354 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->small_magazines
[i
])));
6357 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->small_magazines
[i
])));
6360 for (i
= -1; i
< szone
->num_tiny_magazines
; ++i
) {
6361 tookLock
= SZONE_MAGAZINE_PTR_TRY_LOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6364 SZONE_MAGAZINE_PTR_UNLOCK(szone
, (&(szone
->tiny_magazines
[i
])));
6370 scalable_zone_statistics(malloc_zone_t
*zone
, malloc_statistics_t
*stats
, unsigned subzone
)
6372 szone_t
*szone
= (szone_t
*)zone
;
6380 mag_index_t mag_index
;
6382 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6383 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6384 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6385 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6388 stats
->blocks_in_use
= t
;
6389 stats
->size_in_use
= u
;
6390 stats
->size_allocated
= (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
;
6391 stats
->max_size_in_use
= stats
->size_allocated
- s
;
6399 mag_index_t mag_index
;
6401 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6402 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6403 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6404 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6407 stats
->blocks_in_use
= t
;
6408 stats
->size_in_use
= u
;
6409 stats
->size_allocated
= (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
;
6410 stats
->max_size_in_use
= stats
->size_allocated
- s
;
6414 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
6415 stats
->size_in_use
= szone
->num_bytes_in_large_objects
;
6416 stats
->max_size_in_use
= stats
->size_allocated
= stats
->size_in_use
;
6419 stats
->blocks_in_use
= 0; // DEPRECATED szone->num_huge_entries;
6420 stats
->size_in_use
= 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6421 stats
->max_size_in_use
= stats
->size_allocated
= 0;
6428 szone_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
6435 mag_index_t mag_index
;
6437 for (mag_index
= -1; mag_index
< szone
->num_tiny_magazines
; mag_index
++) {
6438 s
+= szone
->tiny_magazines
[mag_index
].mag_bytes_free_at_end
;
6439 t
+= szone
->tiny_magazines
[mag_index
].mag_num_objects
;
6440 u
+= szone
->tiny_magazines
[mag_index
].mag_num_bytes_in_objects
;
6443 for (mag_index
= -1; mag_index
< szone
->num_small_magazines
; mag_index
++) {
6444 s
+= szone
->small_magazines
[mag_index
].mag_bytes_free_at_end
;
6445 t
+= szone
->small_magazines
[mag_index
].mag_num_objects
;
6446 u
+= szone
->small_magazines
[mag_index
].mag_num_bytes_in_objects
;
6449 large
= szone
->num_bytes_in_large_objects
+ 0; // DEPRECATED szone->num_bytes_in_huge_objects;
6451 stats
->blocks_in_use
= t
+ szone
->num_large_objects_in_use
+ 0; // DEPRECATED szone->num_huge_entries;
6452 stats
->size_in_use
= u
+ large
;
6453 stats
->max_size_in_use
= stats
->size_allocated
=
6454 (szone
->num_tiny_regions
- szone
->num_tiny_regions_dealloc
) * TINY_REGION_SIZE
+
6455 (szone
->num_small_regions
- szone
->num_small_regions_dealloc
) * SMALL_REGION_SIZE
+ large
;
6456 // Now we account for the untouched areas
6457 stats
->max_size_in_use
-= s
;
6461 legacy_zeroing_large_malloc(szone_t
*szone
, size_t size
) {
6462 if (size
> LARGE_THRESHOLD
) // Leopard and earlier returned a ZFOD range, so ...
6463 return szone_calloc(szone
, 1, size
); // Clear to zero always, ham-handedly touching in each page
6465 return szone_malloc(szone
, size
);
6469 legacy_zeroing_large_valloc(szone_t
*szone
, size_t size
) {
6470 void *p
= szone_valloc(szone
, size
);
6472 // Leopard and earlier returned a ZFOD range, so ...
6473 memset(p
, 0, size
); // Clear to zero always, ham-handedly touching in each page
6477 void zeroify_scalable_zone(malloc_zone_t
*zone
)
6479 szone_t
*szone
= (szone_t
*)zone
;
6482 szone
->basic_zone
.malloc
= (void *)legacy_zeroing_large_malloc
;
6483 szone
->basic_zone
.valloc
= (void *)legacy_zeroing_large_valloc
;
6487 static const struct malloc_introspection_t szone_introspect
= {
6488 (void *)szone_ptr_in_use_enumerator
,
6489 (void *)szone_good_size
,
6490 (void *)szone_check
,
6491 (void *)szone_print
,
6493 (void *)szone_force_lock
,
6494 (void *)szone_force_unlock
,
6495 (void *)szone_statistics
,
6496 (void *)szone_locked
,
6497 }; // marked as const to spare the DATA section
6500 create_scalable_zone(size_t initial_size
, unsigned debug_flags
)
6503 uint64_t hw_memsize
= 0;
6504 size_t uint64_t_size
= sizeof(hw_memsize
);
6508 * Sanity-check our build-time assumptions about the size of a page.
6509 * Since we have sized various things assuming the default page size,
6510 * attempting to determine it dynamically is not useful.
6512 if ((vm_page_size
!= _vm_page_size
) || (vm_page_shift
!= _vm_page_shift
)) {
6513 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
6517 #if defined(__i386__) || defined(__x86_64__)
6518 if (_COMM_PAGE_VERSION_REQD
> (*((short *) _COMM_PAGE_VERSION
))) { // _COMM_PAGE_CPU_NUMBER must be present at runtime
6519 malloc_printf("*** ERROR - comm page version mismatch.\n");
6524 /* get memory for the zone, which is now separate from any region.
6525 add guard pages to prevent walking from any other vm allocations
6526 to here and overwriting the function pointers in basic_zone. */
6527 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6531 /* set up the szone structure */
6533 #warning CHECK_REGIONS enabled
6534 debug_flags
|= CHECK_REGIONS
;
6537 #warning LOG enabled
6538 szone
->log_address
= ~0;
6540 szone
->trg
[0].nextgen
= &(szone
->trg
[1]);
6541 szone
->trg
[1].nextgen
= &(szone
->trg
[0]);
6542 szone
->tiny_region_generation
= &(szone
->trg
[0]);
6544 szone
->tiny_region_generation
->hashed_regions
= szone
->initial_tiny_regions
;
6545 szone
->tiny_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
6546 szone
->tiny_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
6548 szone
->srg
[0].nextgen
= &(szone
->srg
[1]);
6549 szone
->srg
[1].nextgen
= &(szone
->srg
[0]);
6550 szone
->small_region_generation
= &(szone
->srg
[0]);
6552 szone
->small_region_generation
->hashed_regions
= szone
->initial_small_regions
;
6553 szone
->small_region_generation
->num_regions_allocated
= INITIAL_NUM_REGIONS
;
6554 szone
->small_region_generation
->num_regions_allocated_shift
= INITIAL_NUM_REGIONS_SHIFT
;
6558 * Initialize variables that size the free list for SMALL allocations based
6559 * upon the amount of memory in the system. Switch to a larger number of
6560 * free list entries at 1GB.
6562 if (0 == sysctlbyname("hw.memsize", &hw_memsize
, &uint64_t_size
, 0, 0) &&
6563 hw_memsize
>= (1ULL << 30)) {
6564 szone
->is_largemem
= 1;
6565 szone
->num_small_slots
= NUM_SMALL_SLOTS_LARGEMEM
;
6566 szone
->large_threshold
= LARGE_THRESHOLD_LARGEMEM
;
6567 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD_LARGEMEM
;
6569 szone
->is_largemem
= 0;
6570 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
6571 szone
->large_threshold
= LARGE_THRESHOLD
;
6572 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
6575 szone
->large_entry_cache_hoard_lmit
= hw_memsize
>> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
6577 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
6578 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
6579 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
6580 szone
->large_legacy_reset_mprotect
= TRUE
;
6582 szone
->large_legacy_reset_mprotect
= FALSE
;
6585 // Initialize the security token.
6587 szone
->cookie
= ((uintptr_t)arc4random() << 32) | (uintptr_t)arc4random();
6589 szone
->cookie
= arc4random();
6592 szone
->basic_zone
.version
= 6;
6593 szone
->basic_zone
.size
= (void *)szone_size
;
6594 szone
->basic_zone
.malloc
= (void *)szone_malloc
;
6595 szone
->basic_zone
.calloc
= (void *)szone_calloc
;
6596 szone
->basic_zone
.valloc
= (void *)szone_valloc
;
6597 szone
->basic_zone
.free
= (void *)szone_free
;
6598 szone
->basic_zone
.realloc
= (void *)szone_realloc
;
6599 szone
->basic_zone
.destroy
= (void *)szone_destroy
;
6600 szone
->basic_zone
.batch_malloc
= (void *)szone_batch_malloc
;
6601 szone
->basic_zone
.batch_free
= (void *)szone_batch_free
;
6602 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
6603 szone
->basic_zone
.memalign
= (void *)szone_memalign
;
6604 szone
->basic_zone
.free_definite_size
= (void *)szone_free_definite_size
;
6605 szone
->debug_flags
= debug_flags
;
6606 LOCK_INIT(szone
->large_szone_lock
);
6608 #if defined(__ppc__) || defined(__ppc64__)
6610 * In the interest of compatibility for PPC applications executing via Rosetta,
6611 * arrange to zero-fill allocations as occurred by side effect in Leopard and earlier.
6613 zeroify_scalable_zone((malloc_zone_t
*)szone
);
6616 if ((err
= pthread_key_create(&(szone
->cpu_id_key
), NULL
))) {
6617 malloc_printf("*** ERROR -pthread_key_create failure err=%d.\n", err
);
6618 szone
->cpu_id_key
= (pthread_key_t
) -1;
6621 // Query the number of configured processors.
6622 // Uniprocessor case gets just one tiny and one small magazine (whose index is zero). This gives
6623 // the same behavior as the original scalable malloc. MP gets per-CPU magazines
6624 // that scale (way) better.
6625 int nproc
= sysconf(_SC_NPROCESSORS_CONF
);
6626 szone
->num_tiny_magazines
= (nproc
> 1) ? MIN(nproc
, TINY_MAX_MAGAZINES
) : 1;
6628 // FIXME vm_allocate() based on number of configured CPUs
6629 magazine_t
*tiny_magazines
= allocate_pages(NULL
, TINY_MAGAZINE_PAGED_SIZE
, 0,
6630 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6631 if (NULL
== tiny_magazines
)
6634 szone
->tiny_magazines
= &(tiny_magazines
[1]); // szone->tiny_magazines[-1] is the Depot
6636 // The magazines are indexed in [0 .. (num_tiny_magazines - 1)]
6637 // Find the smallest power of 2 that exceeds (num_tiny_magazines - 1)
6638 szone
->num_tiny_magazines_mask_shift
= 0;
6640 while( i
<= (szone
->num_tiny_magazines
- 1) ) {
6641 szone
->num_tiny_magazines_mask_shift
++;
6645 // Now if i <= TINY_MAX_MAGAZINES we'll never access tiny_magazines[] out of bounds.
6646 if (i
> TINY_MAX_MAGAZINES
) {
6647 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
6651 // Reduce i by 1 to obtain a mask covering [0 .. (num_tiny_magazines - 1)]
6652 szone
->num_tiny_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
6654 // Init the tiny_magazine locks
6655 LOCK_INIT(szone
->tiny_regions_lock
);
6656 LOCK_INIT(szone
->tiny_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
6657 for (i
= 0; i
< szone
->num_tiny_magazines
; ++i
) {
6658 LOCK_INIT(szone
->tiny_magazines
[i
].magazine_lock
);
6661 szone
->num_small_magazines
= (nproc
> 1) ? MIN(nproc
, SMALL_MAX_MAGAZINES
) : 1;
6663 // FIXME vm_allocate() based on number of configured CPUs
6664 magazine_t
*small_magazines
= allocate_pages(NULL
, SMALL_MAGAZINE_PAGED_SIZE
, 0,
6665 SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6666 if (NULL
== small_magazines
)
6669 szone
->small_magazines
= &(small_magazines
[1]); // szone->small_magazines[-1] is the Depot
6671 // The magazines are indexed in [0 .. (num_small_magazines - 1)]
6672 // Find the smallest power of 2 that exceeds (num_small_magazines - 1)
6673 szone
->num_small_magazines_mask_shift
= 0;
6674 while( i
<= (szone
->num_small_magazines
- 1) ) {
6675 szone
->num_small_magazines_mask_shift
++;
6679 // Now if i <= SMALL_MAX_MAGAZINES we'll never access small_magazines[] out of bounds.
6680 if (i
> SMALL_MAX_MAGAZINES
) {
6681 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
6685 // Reduce i by 1 to obtain a mask covering [0 .. (num_small_magazines - 1)]
6686 szone
->num_small_magazines_mask
= i
- 1; // A mask used for hashing to a magazine index (and a safety aid)
6688 // Init the small_magazine locks
6689 LOCK_INIT(szone
->small_regions_lock
);
6690 LOCK_INIT(szone
->small_magazines
[DEPOT_MAGAZINE_INDEX
].magazine_lock
);
6691 for (i
= 0; i
< szone
->num_small_magazines
; ++i
) {
6692 LOCK_INIT(szone
->small_magazines
[i
].magazine_lock
);
6695 CHECK(szone
, __PRETTY_FUNCTION__
);
6696 return (malloc_zone_t
*)szone
;
6700 // purgeable zones have their own "large" allocation pool, but share "tiny" and "large"
6701 // heaps with a helper_zone identified in the call to create_purgeable_zone()
6704 purgeable_size(szone_t
*szone
, const void *ptr
)
6706 size_t t
= szone_size_try_large(szone
, ptr
);
6711 return szone_size(szone
->helper_zone
, ptr
);
6715 purgeable_malloc(szone_t
*szone
, size_t size
) {
6716 if (size
<= szone
->large_threshold
)
6717 return szone_malloc(szone
->helper_zone
, size
);
6719 return szone_malloc(szone
, size
);
6723 purgeable_calloc(szone_t
*szone
, size_t num_items
, size_t size
)
6725 size_t total_bytes
= num_items
* size
;
6727 // Check for overflow of integer multiplication
6728 if (num_items
> 1) {
6729 #if __LP64__ /* size_t is uint64_t */
6730 if ((num_items
| size
) & 0xffffffff00000000ul
) {
6731 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
6732 __uint128_t product
= ((__uint128_t
)num_items
) * ((__uint128_t
)size
);
6733 if ((uint64_t)(product
>> 64)) // compiles to test on upper register of register pair
6736 #else /* size_t is uint32_t */
6737 if ((num_items
| size
) & 0xffff0000ul
) {
6738 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
6739 uint64_t product
= ((uint64_t)num_items
) * ((uint64_t)size
);
6740 if ((uint32_t)(product
>> 32)) // compiles to test on upper register of register pair
6746 if (total_bytes
<= szone
->large_threshold
)
6747 return szone_calloc(szone
->helper_zone
, 1, total_bytes
);
6749 return szone_calloc(szone
, 1, total_bytes
);
6753 purgeable_valloc(szone_t
*szone
, size_t size
)
6755 if (size
<= szone
->large_threshold
)
6756 return szone_valloc(szone
->helper_zone
, size
);
6758 return szone_valloc(szone
, size
);
6762 purgeable_free(szone_t
*szone
, void *ptr
)
6764 large_entry_t
*entry
;
6767 entry
= large_entry_for_pointer_no_lock(szone
, ptr
);
6768 SZONE_UNLOCK(szone
);
6770 return free_large(szone
, ptr
);
6772 return szone_free(szone
->helper_zone
, ptr
);
6777 purgeable_free_definite_size(szone_t
*szone
, void *ptr
, size_t size
)
6779 if (size
<= szone
->large_threshold
)
6780 return szone_free_definite_size(szone
->helper_zone
, ptr
, size
);
6782 return szone_free_definite_size(szone
, ptr
, size
);
6786 purgeable_realloc(szone_t
*szone
, void *ptr
, size_t new_size
)
6788 if (new_size
<= szone
->large_threshold
)
6789 return szone_realloc(szone
->helper_zone
, ptr
, new_size
);
6791 return szone_realloc(szone
, ptr
, new_size
);
6795 purgeable_destroy(szone_t
*szone
)
6797 /* destroy large entries */
6798 size_t index
= szone
->num_large_entries
;
6799 large_entry_t
*large
;
6800 vm_range_t range_to_deallocate
;
6803 large
= szone
->large_entries
+ index
;
6804 if (large
->address
) {
6805 // we deallocate_pages, including guard pages
6806 deallocate_pages(szone
, (void *)(large
->address
), large
->size
, szone
->debug_flags
);
6809 large_entries_free_no_lock(szone
, szone
->large_entries
, szone
->num_large_entries
, &range_to_deallocate
);
6810 if (range_to_deallocate
.size
)
6811 deallocate_pages(szone
, (void *)range_to_deallocate
.address
, (size_t)range_to_deallocate
.size
, 0);
6813 /* Now destroy the separate szone region */
6814 deallocate_pages(szone
, (void *)szone
, SZONE_PAGED_SIZE
, SCALABLE_MALLOC_ADD_GUARD_PAGES
);
6818 purgeable_batch_malloc(szone_t
*szone
, size_t size
, void **results
, unsigned count
)
6820 return szone_batch_malloc(szone
->helper_zone
, size
, results
, count
);
6824 purgeable_batch_free(szone_t
*szone
, void **to_be_freed
, unsigned count
)
6826 return szone_batch_free(szone
->helper_zone
, to_be_freed
, count
);
6830 purgeable_memalign(szone_t
*szone
, size_t alignment
, size_t size
)
6832 if (size
<= szone
->large_threshold
)
6833 return szone_memalign(szone
->helper_zone
, alignment
, size
);
6835 return szone_memalign(szone
, alignment
, size
);
6838 static kern_return_t
6839 purgeable_ptr_in_use_enumerator(task_t task
, void *context
, unsigned type_mask
, vm_address_t zone_address
,
6840 memory_reader_t reader
, vm_range_recorder_t recorder
)
6845 if (!reader
) reader
= _szone_default_reader
;
6847 err
= reader(task
, zone_address
, sizeof(szone_t
), (void **)&szone
);
6848 if (err
) return err
;
6850 err
= large_in_use_enumerator(task
, context
, type_mask
,
6851 (vm_address_t
)szone
->large_entries
, szone
->num_large_entries
, reader
, recorder
);
6856 purgeable_good_size(szone_t
*szone
, size_t size
)
6858 if (size
<= szone
->large_threshold
)
6859 return szone_good_size(szone
->helper_zone
, size
);
6861 return szone_good_size(szone
, size
);
6865 purgeable_check(szone_t
*szone
)
6871 purgeable_print(szone_t
*szone
, boolean_t verbose
)
6873 _malloc_printf(MALLOC_PRINTF_NOLOG
| MALLOC_PRINTF_NOPREFIX
,
6874 "Scalable zone %p: inUse=%d(%y) flags=%d\n",
6875 szone
, szone
->num_large_objects_in_use
, szone
->num_bytes_in_large_objects
, szone
->debug_flags
);
6879 purgeable_log(malloc_zone_t
*zone
, void *log_address
)
6881 szone_t
*szone
= (szone_t
*)zone
;
6883 szone
->log_address
= log_address
;
6887 purgeable_force_lock(szone_t
*szone
)
6893 purgeable_force_unlock(szone_t
*szone
)
6895 SZONE_UNLOCK(szone
);
6899 purgeable_statistics(szone_t
*szone
, malloc_statistics_t
*stats
)
6901 stats
->blocks_in_use
= szone
->num_large_objects_in_use
;
6902 stats
->size_in_use
= stats
->max_size_in_use
= stats
->size_allocated
= szone
->num_bytes_in_large_objects
;
6906 purgeable_locked(szone_t
*szone
)
6910 tookLock
= SZONE_TRY_LOCK(szone
);
6913 SZONE_UNLOCK(szone
);
6917 static const struct malloc_introspection_t purgeable_introspect
= {
6918 (void *)purgeable_ptr_in_use_enumerator
,
6919 (void *)purgeable_good_size
,
6920 (void *)purgeable_check
,
6921 (void *)purgeable_print
,
6923 (void *)purgeable_force_lock
,
6924 (void *)purgeable_force_unlock
,
6925 (void *)purgeable_statistics
,
6926 (void *)purgeable_locked
,
6927 }; // marked as const to spare the DATA section
6930 create_purgeable_zone(size_t initial_size
, malloc_zone_t
*malloc_default_zone
, unsigned debug_flags
)
6934 /* get memory for the zone, which is now separate from any region.
6935 add guard pages to prevent walking from any other vm allocations
6936 to here and overwriting the function pointers in basic_zone. */
6937 szone
= allocate_pages(NULL
, SZONE_PAGED_SIZE
, 0, SCALABLE_MALLOC_ADD_GUARD_PAGES
, VM_MEMORY_MALLOC
);
6941 /* set up the szone structure */
6943 #warning LOG enabled
6944 szone
->log_address
= ~0;
6947 /* Purgeable zone does not participate in the adaptive "largemem" sizing. */
6948 szone
->is_largemem
= 0;
6949 szone
->large_threshold
= LARGE_THRESHOLD
;
6950 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
6953 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
6954 int32_t libSystemVersion
= NSVersionOfLinkTimeLibrary("System");
6955 if ((-1 != libSystemVersion
) && ((libSystemVersion
>> 16) < 112) /* CFSystemVersionSnowLeopard */)
6956 szone
->large_legacy_reset_mprotect
= TRUE
;
6958 szone
->large_legacy_reset_mprotect
= FALSE
;
6961 szone
->basic_zone
.version
= 6;
6962 szone
->basic_zone
.size
= (void *)purgeable_size
;
6963 szone
->basic_zone
.malloc
= (void *)purgeable_malloc
;
6964 szone
->basic_zone
.calloc
= (void *)purgeable_calloc
;
6965 szone
->basic_zone
.valloc
= (void *)purgeable_valloc
;
6966 szone
->basic_zone
.free
= (void *)purgeable_free
;
6967 szone
->basic_zone
.realloc
= (void *)purgeable_realloc
;
6968 szone
->basic_zone
.destroy
= (void *)purgeable_destroy
;
6969 szone
->basic_zone
.batch_malloc
= (void *)purgeable_batch_malloc
;
6970 szone
->basic_zone
.batch_free
= (void *)purgeable_batch_free
;
6971 szone
->basic_zone
.introspect
= (struct malloc_introspection_t
*)&purgeable_introspect
;
6972 szone
->basic_zone
.memalign
= (void *)purgeable_memalign
;
6973 szone
->basic_zone
.free_definite_size
= (void *)purgeable_free_definite_size
;
6975 szone
->debug_flags
= debug_flags
| SCALABLE_MALLOC_PURGEABLE
;
6977 /* Purgeable zone does not support SCALABLE_MALLOC_ADD_GUARD_PAGES. */
6978 if (szone
->debug_flags
& SCALABLE_MALLOC_ADD_GUARD_PAGES
) {
6979 _malloc_printf(ASL_LEVEL_INFO
, "purgeable zone does not support guard pages\n");
6980 szone
->debug_flags
&= ~SCALABLE_MALLOC_ADD_GUARD_PAGES
;
6983 LOCK_INIT(szone
->large_szone_lock
);
6985 szone
->helper_zone
= (struct szone_s
*)malloc_default_zone
;
6987 CHECK(szone
, __PRETTY_FUNCTION__
);
6988 return (malloc_zone_t
*)szone
;
6992 * For use by CheckFix: create a new zone whose behavior is, apart from
6993 * the use of death-row and per-CPU magazines, that of Leopard.
6995 static NOINLINE
void *
6996 legacy_valloc(szone_t
*szone
, size_t size
)
7001 num_pages
= round_page(size
) >> vm_page_shift
;
7002 ptr
= large_malloc(szone
, num_pages
, 0, TRUE
);
7004 if (LOG(szone
, ptr
))
7005 malloc_printf("legacy_valloc returned %p\n", ptr
);
7011 create_legacy_scalable_zone(size_t initial_size
, unsigned debug_flags
)
7013 malloc_zone_t
*mzone
= create_scalable_zone(initial_size
, debug_flags
);
7014 szone_t
*szone
= (szone_t
*)mzone
;
7019 szone
->is_largemem
= 0;
7020 szone
->num_small_slots
= NUM_SMALL_SLOTS
;
7021 szone
->large_threshold
= LARGE_THRESHOLD
;
7022 szone
->vm_copy_threshold
= VM_COPY_THRESHOLD
;
7024 szone
->basic_zone
.valloc
= (void *)legacy_valloc
;
7025 szone
->basic_zone
.free_definite_size
= NULL
;
7030 /********* Support code for emacs unexec ************/
7032 /* History of freezedry version numbers:
7034 * 1) Old malloc (before the scalable malloc implementation in this file
7036 * 2) Original freezedrying code for scalable malloc. This code was apparently
7037 * based on the old freezedrying code and was fundamentally flawed in its
7038 * assumption that tracking allocated memory regions was adequate to fake
7039 * operations on freezedried memory. This doesn't work, since scalable
7040 * malloc does not store flags in front of large page-aligned allocations.
7041 * 3) Original szone-based freezedrying code.
7042 * 4) Fresher malloc with tiny zone
7043 * 5) 32/64bit compatible malloc
7044 * 6) Metadata within 1MB and 8MB region for tiny and small
7046 * No version backward compatibility is provided, but the version number does
7047 * make it possible for malloc_jumpstart() to return an error if the application
7048 * was freezedried with an older version of malloc.
7050 #define MALLOC_FREEZEDRY_VERSION 6
7059 frozen_malloc(szone_t
*zone
, size_t new_size
)
7061 return malloc(new_size
);
7065 frozen_calloc(szone_t
*zone
, size_t num_items
, size_t size
)
7067 return calloc(num_items
, size
);
7071 frozen_valloc(szone_t
*zone
, size_t new_size
)
7073 return valloc(new_size
);
7077 frozen_realloc(szone_t
*zone
, void *ptr
, size_t new_size
)
7079 size_t old_size
= szone_size(zone
, ptr
);
7082 if (new_size
<= old_size
) {
7085 new_ptr
= malloc(new_size
);
7087 memcpy(new_ptr
, ptr
, old_size
);
7093 frozen_free(szone_t
*zone
, void *ptr
)
7098 frozen_destroy(szone_t
*zone
)
7102 /********* Pseudo-private API for emacs unexec ************/
7105 * malloc_freezedry() records all of the szones in use, so that they can be
7106 * partially reconstituted by malloc_jumpstart(). Due to the differences
7107 * between reconstituted memory regions and those created by the szone code,
7108 * care is taken not to reallocate from the freezedried memory, except in the
7109 * case of a non-growing realloc().
7111 * Due to the flexibility provided by the zone registration mechanism, it is
7112 * impossible to implement generic freezedrying for any zone type. This code
7113 * only handles applications that use the szone allocator, so malloc_freezedry()
7114 * returns 0 (error) if any non-szone zones are encountered.
7118 malloc_freezedry(void)
7120 extern unsigned malloc_num_zones
;
7121 extern malloc_zone_t
**malloc_zones
;
7122 malloc_frozen
*data
;
7125 /* Allocate space in which to store the freezedry state. */
7126 data
= (malloc_frozen
*) malloc(sizeof(malloc_frozen
));
7128 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
7129 data
->version
= MALLOC_FREEZEDRY_VERSION
;
7131 /* Allocate the array of szone pointers. */
7132 data
->nszones
= malloc_num_zones
;
7133 data
->szones
= (szone_t
*) calloc(malloc_num_zones
, sizeof(szone_t
));
7136 * Fill in the array of szone structures. They are copied rather than
7137 * referenced, since the originals are likely to be clobbered during malloc
7140 for (i
= 0; i
< malloc_num_zones
; i
++) {
7141 if (strcmp(malloc_zones
[i
]->zone_name
, "DefaultMallocZone")) {
7142 /* Unknown zone type. */
7147 memcpy(&data
->szones
[i
], malloc_zones
[i
], sizeof(szone_t
));
7150 return((uintptr_t)data
);
7154 malloc_jumpstart(uintptr_t cookie
)
7156 malloc_frozen
*data
= (malloc_frozen
*)cookie
;
7159 if (data
->version
!= MALLOC_FREEZEDRY_VERSION
) {
7160 /* Unsupported freezedry version. */
7164 for (i
= 0; i
< data
->nszones
; i
++) {
7165 /* Set function pointers. Even the functions that stay the same must be
7166 * set, since there are no guarantees that they will be mapped to the
7167 * same addresses. */
7168 data
->szones
[i
].basic_zone
.size
= (void *) szone_size
;
7169 data
->szones
[i
].basic_zone
.malloc
= (void *) frozen_malloc
;
7170 data
->szones
[i
].basic_zone
.calloc
= (void *) frozen_calloc
;
7171 data
->szones
[i
].basic_zone
.valloc
= (void *) frozen_valloc
;
7172 data
->szones
[i
].basic_zone
.free
= (void *) frozen_free
;
7173 data
->szones
[i
].basic_zone
.realloc
= (void *) frozen_realloc
;
7174 data
->szones
[i
].basic_zone
.destroy
= (void *) frozen_destroy
;
7175 data
->szones
[i
].basic_zone
.introspect
= (struct malloc_introspection_t
*)&szone_introspect
;
7177 /* Register the freezedried zone. */
7178 malloc_zone_register(&data
->szones
[i
].basic_zone
);