]> git.saurik.com Git - apple/libc.git/blame_incremental - gen/scalable_malloc.c
Libc-763.11.tar.gz
[apple/libc.git] / gen / scalable_malloc.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1999, 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/* Author: Bertrand Serlet, August 1999 */
25
26#include "scalable_malloc.h"
27#include "malloc_printf.h"
28#include "_simple.h"
29
30#include <pthread_internals.h>
31
32#include <unistd.h>
33#include <libc.h>
34#include <mach/vm_statistics.h>
35#include <mach/mach_init.h>
36#include <sys/types.h>
37#include <sys/mman.h>
38
39/********************* DEFINITIONS ************************/
40
41#define DEBUG_MALLOC 0 // set to one to debug malloc itself
42
43#define DEBUG_CLIENT 0 // set to one to help debug a nasty memory smasher
44
45#if DEBUG_MALLOC
46#warning DEBUG_MALLOC ENABLED
47# define INLINE
48# define ALWAYSINLINE
49# define CHECK_LOCKED(szone, fun) \
50do { \
51 if (__is_threaded && TRY_LOCK(szone->lock)) { \
52 malloc_printf("*** lock was not set %p in %s\n", szone->lock, fun); \
53 } \
54} while (0)
55#else
56# define INLINE __inline__
57# define ALWAYSINLINE __attribute__((always_inline))
58# define CHECK_LOCKED(szone, fun) {}
59#endif
60
61/*
62 * Access to global variables is slow, so optimise our handling of vm_page_size
63 * and vm_page_shift.
64 */
65#define _vm_page_size vm_page_size /* to get to the originals */
66#define _vm_page_shift vm_page_shift
67#define vm_page_size 4096 /* our normal working sizes */
68#define vm_page_shift 12
69
70typedef unsigned short msize_t; // a size in multiples of SHIFT_SMALL_QUANTUM or SHIFT_TINY_QUANTUM
71
72typedef union {
73 void *p;
74 uintptr_t u;
75} ptr_union;
76
77typedef struct {
78 ptr_union previous;
79 ptr_union next;
80} free_list_t;
81
82typedef struct {
83 uintptr_t address_and_num_pages;
84 // this type represents both an address and a number of pages
85 // the low bits are the number of pages; the high bits are the address
86 // note that the exact number of bits used for depends on the page size
87 // also, this cannot represent pointers larger than 1 << (vm_page_shift * 2)
88} compact_range_t;
89
90typedef unsigned char grain_t;
91
92#define CHECK_REGIONS (1 << 31)
93
94#define MAX_RECORDER_BUFFER 256
95
96/********************* DEFINITIONS for tiny ************************/
97
98/*
99 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the szone's tiny_regions
100 * pointer.
101 *
102 * Each region is laid out as a heap, followed by a header block, all within
103 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header is
104 * 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
105 * The header block is arranged:
106 *
107 * 0xfc080
108 * header bits
109 * 0xfe001
110 * 0xffffffff pad word
111 * 0xfe005
112 * in-use bits
113 * 0xfff86
114 * pad word (not written)
115 * 0xfff8a-0xfffff
116 * unused
117 *
118 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding TINY_QUANTUM block
119 * within the heap.
120 *
121 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
122 * that the corresponding quantum is the first quantum in a block (either in use or free). The
123 * in-use bit is set for the header if the block has been handed out (allocated). If the header
124 * bit is not set, the in-use bit is invalid.
125 *
126 * The szone maintains an array of 32 freelists, each of which is used to hold
127 * free objects of the corresponding quantum size.
128 *
129 * A free block is laid out depending on its size, in order to fit all free
130 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
131 * not store their size in the block, instead relying on the header information
132 * to determine their size. Blocks of two or more quanta have room to store
133 * their size in the block, and store it both after the 'next' pointer, and in
134 * the last 2 bytes of the block.
135 *
136 * 1-quantum block
137 * Offset (32-bit mode) (64-bit mode)
138 * 0x0 0x0 : previous
139 * 0x4 0x08 : next
140 * end end
141 *
142 * >1-quantum block
143 * Offset (32-bit mode) (64-bit mode)
144 * 0x0 0x0 : previous
145 * 0x4 0x08 : next
146 * 0x8 0x10 : size (in quantum counts)
147 * end - 2 end - 2 : size (in quantum counts)
148 * end end
149 *
150 * All fields are pointer-sized, except for the size which is an unsigned short.
151 *
152 */
153
154#define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
155#define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
156
157#define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
158
159#define NUM_TINY_SLOTS 32 // number of slots for free-lists
160
161#define NUM_TINY_BLOCKS 64520
162#define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
163#define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
164#define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM)
165
166/*
167 * Enough room for the data, followed by the bit arrays (2-bits per block) plus 2 words of padding
168 * as our bitmap operators overflow, plus rounding to the nearest page.
169 */
170#define TINY_HEADER_SIZE ((NUM_TINY_BLOCKS >> 2) + 8)
171#define TINY_REGION_SIZE ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_HEADER_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
172
173/*
174 * Beginning and end pointers for a region's heap.
175 */
176#define TINY_REGION_ADDRESS(region) ((void *)(region))
177#define TINY_REGION_END(region) (TINY_REGION_ADDRESS(region) + (NUM_TINY_BLOCKS * TINY_QUANTUM))
178
179/*
180 * Locate the heap base for a pointer known to be within a tiny region.
181 */
182#define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
183
184/*
185 * Convert between byte and msize units.
186 */
187#define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
188#define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
189
190#ifdef __LP64__
191# define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
192#else
193# define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
194#endif
195#define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
196
197/*
198 * Locate the block header for a pointer known to be within a tiny region.
199 */
200#define TINY_HEADER_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
201#define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)((uintptr_t)TINY_REGION_FOR_PTR(_p) + TINY_HEADER_START))
202
203/*
204 * Locate the inuse map for a given block header pointer.
205 */
206#define TINY_INUSE_FOR_HEADER(_h) ((void *)((uintptr_t)(_h) + (NUM_TINY_BLOCKS >> 3) + 4))
207
208/*
209 * Compute the bitmap index for a pointer known to be within a tiny region.
210 */
211#define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
212
213#define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
214
215#if ! TINY_CACHE
216#warning TINY_CACHE turned off
217#endif
218
219/********************* DEFINITIONS for small ************************/
220
221/*
222 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's small_regions
223 * pointer.
224 *
225 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
226 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
227 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
228 * 128 bytes unused.
229 *
230 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
231 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
232 *
233 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
234 * of the corresponding quantum size.
235 *
236 * A free block is laid out as:
237 *
238 * Offset (32-bit mode) (64-bit mode)
239 * 0x0 0x0 : previous
240 * 0x4 0x08 : next
241 * 0x8 0x10 : size (in quantum counts)
242 * end - 2 end - 2 : size (in quantum counts)
243 * end end
244 *
245 * All fields are pointer-sized, except for the size which is an unsigned short.
246 *
247 */
248
249#define SMALL_IS_FREE (1 << 15)
250
251#define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
252#define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
253
254#define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
255
256#define NUM_SMALL_SLOTS 32 // number of slots for free-lists
257
258/*
259 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
260 * convention msize=0 => msize = (1<<15)
261 */
262#define NUM_SMALL_BLOCKS 16320
263#define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
264#define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
265#define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
266#define SMALL_ARRAY_SIZE (NUM_SMALL_BLOCKS * 2)
267#define SMALL_REGION_SIZE ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_ARRAY_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1)) // data + meta data
268
269#define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
270
271/*
272 * Convert between byte and msize units.
273 */
274#define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
275#define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
276
277
278#define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
279#define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
280
281/*
282 * Locate the heap base for a pointer known to be within a small region.
283 */
284#define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
285
286/*
287 * Locate the metadata base for a pointer known to be within a small region.
288 */
289#define SMALL_HEADER_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
290#define SMALL_META_HEADER_FOR_PTR(_p) ((msize_t *)((uintptr_t)SMALL_REGION_FOR_PTR(_p) + SMALL_HEADER_START))
291
292/*
293 * Compute the metadata index for a pointer known to be within a small region.
294 */
295#define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
296
297/*
298 * Find the metadata word for a pointer known to be within a small region.
299 */
300#define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
301
302/*
303 * Determine whether a pointer known to be within a small region points to memory which is free.
304 */
305#define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
306
307/*
308 * Extract the msize value for a pointer known to be within a small region.
309 */
310#define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
311
312#define PROTECT_SMALL 0 // Should be 0: 1 is too slow for normal use
313
314#define SMALL_CACHE 1
315#if !SMALL_CACHE
316#warning SMALL_CACHE turned off
317#endif
318
319/********************* DEFINITIONS for large and huge ***********************/
320
321#define LARGE_THRESHOLD (15 * 1024) // at or above this use "large"
322
323#if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
324#error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
325#endif
326
327#define VM_COPY_THRESHOLD (40 * 1024)
328 // When all memory is touched after a copy, vm_copy() is always a lose
329 // But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages (on a G3/300MHz)
330 // This must be larger than LARGE_THRESHOLD
331
332/*
333 * Given a large_entry, return the address of the allocated block.
334 */
335#define LARGE_ENTRY_ADDRESS(entry) \
336 (void *)(((entry).address_and_num_pages >> vm_page_shift) << vm_page_shift)
337
338/*
339 * Given a large entry, return the number of pages or bytes in the allocated block.
340 */
341#define LARGE_ENTRY_NUM_PAGES(entry) \
342 ((entry).address_and_num_pages & (vm_page_size - 1))
343#define LARGE_ENTRY_SIZE(entry) \
344 (LARGE_ENTRY_NUM_PAGES(entry) << vm_page_shift)
345
346/*
347 * Compare a pointer with a large entry.
348 */
349#define LARGE_ENTRY_MATCHES(entry,ptr) \
350 ((((entry).address_and_num_pages - (uintptr_t)(ptr)) >> vm_page_shift) == 0)
351
352#define LARGE_ENTRY_IS_EMPTY(entry) (((entry).address_and_num_pages) == 0)
353
354typedef compact_range_t large_entry_t;
355typedef vm_range_t huge_entry_t;
356
357/*******************************************************************************
358 * Definitions for region hash
359 ******************************************************************************/
360
361typedef void * region_t;
362
363#define INITIAL_NUM_REGIONS 63 // Must be odd to hash well
364
365/********************* zone itself ************************/
366
367typedef struct {
368 malloc_zone_t basic_zone;
369 pthread_lock_t lock;
370 unsigned debug_flags;
371 void *log_address;
372
373 /* Regions for tiny objects */
374 size_t num_tiny_regions;
375 size_t num_tiny_regions_allocated;
376 region_t *tiny_regions; // hashed by location
377 region_t *last_tiny_region;
378 void *last_tiny_free; // low SHIFT_TINY_QUANTUM indicate the msize
379 unsigned tiny_bitmap; // cache of the 32 free lists
380 free_list_t *tiny_free_list[NUM_TINY_SLOTS]; // 31 free lists for 1*TINY_QUANTUM to 31*TINY_QUANTUM plus 1 for larger than 32*SMALL_QUANTUM
381 size_t tiny_bytes_free_at_end; // the last free region in the last block is treated as a big block in use that is not accounted for
382 unsigned num_tiny_objects;
383 size_t num_bytes_in_tiny_objects;
384
385 /* Regions for small objects */
386 size_t num_small_regions;
387 size_t num_small_regions_allocated;
388 region_t *small_regions; // hashed by location
389 region_t *last_small_region;
390 void *last_small_free; // low SHIFT_SMALL_QUANTUM indicate the msize
391 unsigned small_bitmap; // cache of the free list
392 free_list_t *small_free_list[NUM_SMALL_SLOTS];
393 size_t small_bytes_free_at_end; // the last free region in the last block is treated as a big block in use that is not accounted for
394 unsigned num_small_objects;
395 size_t num_bytes_in_small_objects;
396
397 /* large objects: vm_page_shift <= log2(size) < 2 *vm_page_shift */
398 unsigned num_large_objects_in_use;
399 unsigned num_large_entries;
400 large_entry_t *large_entries; // hashed by location; null entries don't count
401 size_t num_bytes_in_large_objects;
402
403 /* huge objects: log2(size) >= 2 *vm_page_shift */
404 unsigned num_huge_entries;
405 huge_entry_t *huge_entries;
406 size_t num_bytes_in_huge_objects;
407
408 /* Initial region list */
409 region_t initial_tiny_regions[INITIAL_NUM_REGIONS];
410 region_t initial_small_regions[INITIAL_NUM_REGIONS];
411} szone_t;
412
413#define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
414
415#if DEBUG_MALLOC || DEBUG_CLIENT
416static void szone_sleep(void);
417#endif
418__private_extern__ void malloc_error_break(void);
419
420// msg prints after fmt, ...
421static void szone_error(szone_t *szone, const char *msg, const void *ptr, const char *fmt, ...) __printflike(4, 5);
422
423static void protect(void *address, size_t size, unsigned protection, unsigned debug_flags);
424static void *allocate_pages(szone_t *szone, size_t size, unsigned char align, unsigned debug_flags, int vm_page_label);
425static void deallocate_pages(szone_t *szone, void *addr, size_t size, unsigned debug_flags);
426static kern_return_t _szone_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr);
427
428static INLINE void free_list_checksum(szone_t *szone, free_list_t *ptr, const char *msg) ALWAYSINLINE;
429static INLINE void free_list_set_checksum(szone_t *szone, free_list_t *ptr) ALWAYSINLINE;
430static INLINE uintptr_t free_list_checksum_ptr(void *p) ALWAYSINLINE;
431static INLINE void * free_list_unchecksum_ptr(ptr_union ptr) ALWAYSINLINE;
432static unsigned free_list_count(const free_list_t *ptr);
433
434static INLINE msize_t get_tiny_meta_header(const void *ptr, boolean_t *is_free) ALWAYSINLINE;
435static INLINE void set_tiny_meta_header_in_use(const void *ptr, msize_t msize) ALWAYSINLINE;
436static INLINE void set_tiny_meta_header_middle(const void *ptr) ALWAYSINLINE;
437static INLINE void set_tiny_meta_header_free(const void *ptr, msize_t msize) ALWAYSINLINE;
438static INLINE boolean_t tiny_meta_header_is_free(const void *ptr) ALWAYSINLINE;
439static INLINE void *tiny_previous_preceding_free(void *ptr, msize_t *prev_msize) ALWAYSINLINE;
440static void tiny_free_list_add_ptr(szone_t *szone, void *ptr, msize_t msize);
441static void tiny_free_list_remove_ptr(szone_t *szone, void *ptr, msize_t msize);
442static INLINE region_t *tiny_region_for_ptr_no_lock(szone_t *szone, const void *ptr) ALWAYSINLINE;
443static INLINE void tiny_free_no_lock(szone_t *szone, region_t *region, void *ptr, msize_t msize) ALWAYSINLINE;
444static void *tiny_malloc_from_region_no_lock(szone_t *szone, msize_t msize);
445static INLINE boolean_t try_realloc_tiny_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size) ALWAYSINLINE;
446static boolean_t tiny_check_region(szone_t *szone, region_t region);
447static kern_return_t tiny_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone, memory_reader_t reader, vm_range_recorder_t recorder);
448static void *tiny_malloc_from_free_list(szone_t *szone, msize_t msize);
449static INLINE void *tiny_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested) ALWAYSINLINE;
450static INLINE void free_tiny(szone_t *szone, void *ptr, region_t *tiny_region) ALWAYSINLINE;
451static void print_tiny_free_list(szone_t *szone);
452static void print_tiny_region(boolean_t verbose, region_t region, size_t bytes_at_end);
453static boolean_t tiny_free_list_check(szone_t *szone, grain_t slot);
454
455static INLINE void small_meta_header_set_is_free(msize_t *meta_headers, unsigned index, msize_t msize) ALWAYSINLINE;
456static INLINE void small_meta_header_set_in_use(msize_t *meta_headers, msize_t index, msize_t msize) ALWAYSINLINE;
457static INLINE void small_meta_header_set_middle(msize_t *meta_headers, msize_t index) ALWAYSINLINE;
458static void small_free_list_add_ptr(szone_t *szone, void *ptr, msize_t msize);
459static void small_free_list_remove_ptr(szone_t *szone, void *ptr, msize_t msize);
460static INLINE region_t *small_region_for_ptr_no_lock(szone_t *szone, const void *ptr) ALWAYSINLINE;
461static INLINE void small_free_no_lock(szone_t *szone, region_t *region, void *ptr, msize_t msize) ALWAYSINLINE;
462static void *small_malloc_from_region_no_lock(szone_t *szone, msize_t msize);
463static INLINE boolean_t try_realloc_small_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size) ALWAYSINLINE;
464static boolean_t szone_check_small_region(szone_t *szone, region_t region);
465static kern_return_t small_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone, memory_reader_t reader, vm_range_recorder_t recorder);
466static void *small_malloc_from_free_list(szone_t *szone, msize_t msize);
467static INLINE void *small_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested) ALWAYSINLINE;
468static INLINE void *small_malloc_cleared_no_lock(szone_t *szone, msize_t msize) ALWAYSINLINE;
469static INLINE void free_small(szone_t *szone, void *ptr, region_t *small_region) ALWAYSINLINE;
470static void print_small_free_list(szone_t *szone);
471static void print_small_region(szone_t *szone, boolean_t verbose, region_t region, size_t bytes_at_end);
472static boolean_t small_free_list_check(szone_t *szone, grain_t grain);
473
474static region_t * hash_lookup_region_no_lock(region_t *regions, size_t num_entries, region_t r);
475static void hash_region_insert_no_lock(region_t *regions, size_t num_entries, region_t r);
476static region_t * hash_regions_alloc_no_lock(szone_t *szone, size_t num_entries);
477static region_t * hash_regions_grow_no_lock(szone_t *szone, region_t *regions, size_t old_size, size_t *new_size);
478
479#if DEBUG_MALLOC
480static void large_debug_print(szone_t *szone);
481#endif
482static large_entry_t *large_entry_for_pointer_no_lock(szone_t *szone, const void *ptr);
483static void large_entry_insert_no_lock(szone_t *szone, large_entry_t range);
484static INLINE void large_entries_rehash_after_entry_no_lock(szone_t *szone, large_entry_t *entry) ALWAYSINLINE;
485static INLINE large_entry_t *large_entries_alloc_no_lock(szone_t *szone, unsigned num) ALWAYSINLINE;
486static void large_entries_free_no_lock(szone_t *szone, large_entry_t *entries, unsigned num, vm_range_t *range_to_deallocate);
487static large_entry_t * large_entries_grow_no_lock(szone_t *szone, vm_range_t *range_to_deallocate);
488static vm_range_t large_free_no_lock(szone_t *szone, large_entry_t *entry);
489static kern_return_t large_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t large_entries_address, unsigned num_entries, memory_reader_t reader, vm_range_recorder_t recorder);
490static huge_entry_t *huge_entry_for_pointer_no_lock(szone_t *szone, const void *ptr);
491static boolean_t huge_entry_append(szone_t *szone, huge_entry_t huge);
492static kern_return_t huge_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t huge_entries_address, unsigned num_entries, memory_reader_t reader, vm_range_recorder_t recorder);
493static void *large_and_huge_malloc(szone_t *szone, size_t num_pages);
494static INLINE void free_large_or_huge(szone_t *szone, void *ptr) ALWAYSINLINE;
495static INLINE int try_realloc_large_or_huge_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size) ALWAYSINLINE;
496
497static void szone_free(szone_t *szone, void *ptr);
498static INLINE void *szone_malloc_should_clear(szone_t *szone, size_t size, boolean_t cleared_requested) ALWAYSINLINE;
499static void *szone_malloc(szone_t *szone, size_t size);
500static void *szone_calloc(szone_t *szone, size_t num_items, size_t size);
501static void *szone_valloc(szone_t *szone, size_t size);
502static size_t szone_size(szone_t *szone, const void *ptr);
503static void *szone_realloc(szone_t *szone, void *ptr, size_t new_size);
504static unsigned szone_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count);
505static void szone_batch_free(szone_t *szone, void **to_be_freed, unsigned count);
506static void szone_destroy(szone_t *szone);
507static size_t szone_good_size(szone_t *szone, size_t size);
508
509static boolean_t szone_check_all(szone_t *szone, const char *function);
510static boolean_t szone_check(szone_t *szone);
511static kern_return_t szone_ptr_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder);
512static void szone_print(szone_t *szone, boolean_t verbose);
513static void szone_log(malloc_zone_t *zone, void *log_address);
514static void szone_force_lock(szone_t *szone);
515static void szone_force_unlock(szone_t *szone);
516
517static void szone_statistics(szone_t *szone, malloc_statistics_t *stats);
518
519static void *frozen_malloc(szone_t *zone, size_t new_size);
520static void *frozen_calloc(szone_t *zone, size_t num_items, size_t size);
521static void *frozen_valloc(szone_t *zone, size_t new_size);
522static void *frozen_realloc(szone_t *zone, void *ptr, size_t new_size);
523static void frozen_free(szone_t *zone, void *ptr);
524static void frozen_destroy(szone_t *zone);
525
526#if DEBUG_MALLOC
527# define LOG(szone,ptr) \
528 (szone->log_address && (((uintptr_t)szone->log_address == -1) || (szone->log_address == (void *)(ptr))))
529#else
530# define LOG(szone,ptr) 0
531#endif
532
533#define SZONE_LOCK(szone) \
534 do { \
535 LOCK(szone->lock); \
536 } while (0)
537
538#define SZONE_UNLOCK(szone) \
539 do { \
540 UNLOCK(szone->lock); \
541 } while (0)
542
543#define LOCK_AND_NOTE_LOCKED(szone,locked) \
544do { \
545 CHECK(szone, __PRETTY_FUNCTION__); \
546 locked = 1; SZONE_LOCK(szone); \
547} while (0)
548
549#if DEBUG_MALLOC || DEBUG_CLIENT
550# define CHECK(szone,fun) \
551 if ((szone)->debug_flags & CHECK_REGIONS) szone_check_all(szone, fun)
552#else
553# define CHECK(szone,fun) do {} while (0)
554#endif
555
556/********************* VERY LOW LEVEL UTILITIES ************************/
557
558#if DEBUG_MALLOC || DEBUG_CLIENT
559static void
560szone_sleep(void)
561{
562
563 if (getenv("MallocErrorSleep")) {
564 _malloc_printf(ASL_LEVEL_NOTICE, "*** sleeping to help debug\n");
565 sleep(3600); // to help debug
566 }
567}
568#endif
569
570// msg prints after fmt, ...
571static __attribute__((noinline)) void
572szone_error(szone_t *szone, const char *msg, const void *ptr, const char *fmt, ...)
573{
574 va_list ap;
575 _SIMPLE_STRING b = _simple_salloc();
576
577 if (szone) SZONE_UNLOCK(szone);
578 if (b) {
579 if (fmt) {
580 va_start(ap, fmt);
581 _simple_vsprintf(b, fmt, ap);
582 va_end(ap);
583 }
584 if (ptr) {
585 _simple_sprintf(b, "*** error for object %p: %s\n", ptr, msg);
586 } else {
587 _simple_sprintf(b, "*** error: %s\n", msg);
588 }
589 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b));
590 _simple_sfree(b);
591 } else {
592 /*
593 * Should only get here if vm_allocate() can't get a single page of
594 * memory, implying _simple_asl_log() would also fail. So we just
595 * print to the file descriptor.
596 */
597 if (fmt) {
598 va_start(ap, fmt);
599 _malloc_vprintf(MALLOC_PRINTF_NOLOG, fmt, ap);
600 va_end(ap);
601 }
602 if (ptr) {
603 _malloc_printf(MALLOC_PRINTF_NOLOG, "*** error for object %p: %s\n", ptr, msg);
604 } else {
605 _malloc_printf(MALLOC_PRINTF_NOLOG, "*** error: %s\n", msg);
606 }
607 _malloc_printf(MALLOC_PRINTF_NOLOG, "*** set a breakpoint in malloc_error_break to debug\n");
608 }
609 malloc_error_break();
610#if DEBUG_MALLOC
611 szone_print(szone, 1);
612 szone_sleep();
613#endif
614#if DEBUG_CLIENT
615 szone_sleep();
616#endif
617 if (szone->debug_flags & SCALABLE_MALLOC_ABORT_ON_ERROR) abort();
618}
619
620static void
621protect(void *address, size_t size, unsigned protection, unsigned debug_flags)
622{
623 kern_return_t err;
624
625 if (!(debug_flags & SCALABLE_MALLOC_DONT_PROTECT_PRELUDE)) {
626 err = vm_protect(mach_task_self(), (vm_address_t)(uintptr_t)address - vm_page_size, vm_page_size, 0, protection);
627 if (err) {
628 malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
629 protection,address - (1 << vm_page_shift));
630 }
631 }
632 if (!(debug_flags & SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE)) {
633 err = vm_protect(mach_task_self(), (vm_address_t)(uintptr_t)address + size, vm_page_size, 0, protection);
634 if (err) {
635 malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
636 protection, address + size);
637 }
638 }
639}
640
641static void *
642allocate_pages(szone_t *szone, size_t size, unsigned char align, unsigned debug_flags, int vm_page_label)
643{
644 // align specifies a desired alignment (as a log) or 0 if no alignment requested
645 void *vm_addr;
646 uintptr_t addr, aligned_address;
647 boolean_t add_guard_pages = debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES;
648 size_t allocation_size = round_page(size);
649 size_t delta;
650
651 if (align) add_guard_pages = 0; // too cumbersome to deal with that
652 if (!allocation_size) allocation_size = 1 << vm_page_shift;
653 if (add_guard_pages) allocation_size += 2 * (1 << vm_page_shift);
654 if (align) allocation_size += (size_t)1 << align;
655 vm_addr = mmap(0, allocation_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, VM_MAKE_TAG(vm_page_label), 0);
656 if ((uintptr_t)vm_addr == -1) {
657 szone_error(szone, "can't allocate region", NULL, "*** mmap(size=%lld) failed (error code=%d)\n", (long long)allocation_size, errno);
658 return NULL;
659 }
660 addr = (uintptr_t)vm_addr;
661 if (align) {
662 aligned_address = (addr + ((uintptr_t)1 << align) - 1) & ~ (((uintptr_t)1 << align) - 1);
663 if (aligned_address != addr) {
664 delta = aligned_address - addr;
665 if (munmap((void *)addr, delta) == -1)
666 malloc_printf("*** freeing unaligned header failed with %d\n", errno);
667 addr = aligned_address;
668 allocation_size -= delta;
669 }
670 if (allocation_size > size) {
671 if (munmap((void *)(addr + size), allocation_size - size) == -1)
672 malloc_printf("*** freeing unaligned footer failed with %d\n", errno);
673 }
674 }
675 if (add_guard_pages) {
676 addr += (uintptr_t)1 << vm_page_shift;
677 protect((void *)addr, size, 0, debug_flags);
678 }
679 return (void *)addr;
680}
681
682static void
683deallocate_pages(szone_t *szone, void *addr, size_t size, unsigned debug_flags)
684{
685 int err;
686 boolean_t add_guard_pages = debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES;
687
688 if (add_guard_pages) {
689 addr -= 1 << vm_page_shift;
690 size += 2 * (1 << vm_page_shift);
691 }
692 err = munmap(addr, size);
693 if ((err == -1) && szone)
694 szone_error(szone, "Can't deallocate_pages region", addr, NULL);
695}
696
697static kern_return_t
698_szone_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr)
699{
700 *ptr = (void *)address;
701 return 0;
702}
703
704/********************* FREE LIST UTILITIES ************************/
705
706// A free list entry is comprised of a pair of pointers, previous and next.
707// Because the free list entries are previously freed objects, there is a
708// non-zero chance that a misbehaved program will write to an allocated object
709// after it has called free() on the pointer. This write would then potentially
710// corrupt the previous and next pointers, leading to a crash. In order to
711// detect this case, we take advantage of the fact that pointers are known to
712// be at least 16 byte aligned, and thus have at least 4 trailing zero bits.
713// When an entry is added to the free list, the previous and next pointers are
714// shifted right by 2 bits, and then have the high and low 2 bits set, to act
715// as guard bits. Before accessing a free list object, we verify that these
716// bits are still set, and log an error if they are not.
717
718static INLINE void
719free_list_checksum(szone_t *szone, free_list_t *ptr, const char *msg)
720{
721 uintptr_t ptrs = ptr->previous.u & ptr->next.u;
722
723#ifdef __LP64__
724 ptrs = (ptrs << 2) | (ptrs >> (64-2));
725#else
726 ptrs = (ptrs << 2) | (ptrs >> (32-2));
727#endif
728
729 if ((ptrs & 15) != 15)
730 szone_error(szone, "incorrect checksum for freed object "
731 "- object was probably modified after being freed.", ptr, NULL);
732}
733
734static INLINE uintptr_t
735free_list_checksum_ptr(void *p)
736{
737 ptr_union ptr;
738 ptr.p = p;
739
740#ifdef __LP64__
741 return (ptr.u >> 2) | 0xC000000000000003ULL;
742#else
743 return (ptr.u >> 2) | 0xC0000003U;
744#endif
745}
746
747static INLINE void *
748free_list_unchecksum_ptr(ptr_union ptr)
749{
750 uintptr_t u = (ptr.u >> 2) << 4;
751 return (void *)u;
752}
753
754static INLINE void
755free_list_set_checksum(szone_t *szone, free_list_t *ptr)
756{
757 ptr->previous.u = free_list_checksum_ptr(ptr->previous.p);
758 ptr->next.u = free_list_checksum_ptr(ptr->next.p);
759}
760
761static unsigned
762free_list_count(const free_list_t *ptr)
763{
764 unsigned count = 0;
765
766 while (ptr) {
767 count++;
768 ptr = free_list_unchecksum_ptr(ptr->next);
769 }
770 return count;
771}
772
773/* XXX inconsistent use of BITMAP32 and BITARRAY operations could be cleaned up */
774
775#define BITMAP32_SET(bitmap,bit) (bitmap |= 1 << (bit))
776#define BITMAP32_CLR(bitmap,bit) (bitmap &= ~ (1 << (bit)))
777#define BITMAP32_BIT(bitmap,bit) ((bitmap >> (bit)) & 1)
778
779/* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
780#define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap))
781
782/********************* TINY FREE LIST UTILITIES ************************/
783
784// We encode the meta-headers as follows:
785// Each quantum has an associated set of 2 bits:
786// block_header when 1 says this block is the beginning of a block
787// in_use when 1 says this block is in use
788// so a block in use of size 3 is 1-1 0-X 0-X
789// for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
790// for a block middle the bits are 0-0
791
792// Attention double evaluation for these
793#define BITARRAY_SET(bits,index) (bits[index>>3] |= (1 << (index & 7)))
794#define BITARRAY_CLR(bits,index) (bits[index>>3] &= ~(1 << (index & 7)))
795#define BITARRAY_BIT(bits,index) (((bits[index>>3]) >> (index & 7)) & 1)
796
797// Following is for start<8 and end<=start+32
798static void ALWAYSINLINE
799bitarray_mclr(void *bits, unsigned start, unsigned end) {
800 unsigned word = OSReadLittleInt32(bits, 0);
801 unsigned mask = (0xFFFFFFFFU >> (31 - start)) >> 1;
802
803 if (end > 31) {
804 unsigned char *bytes = (unsigned char *)bits;
805 bytes[4] &= ~((1 << (end - 32)) - 1);
806 } else {
807 mask |= (0xFFFFFFFF << end);
808 }
809 OSWriteLittleInt32(bits, 0, word & mask);
810}
811
812/*
813 * Obtain the size of a free tiny block (in msize_t units).
814 */
815static msize_t
816get_tiny_free_size(const void *ptr)
817{
818 void *next_block = (void *)((uintptr_t)ptr + TINY_QUANTUM);
819 void *region_end = TINY_REGION_END(TINY_REGION_FOR_PTR(ptr));
820
821 // check whether the next block is outside the tiny region or a block header
822 // if so, then the size of this block is one, and there is no stored size.
823 if (next_block < region_end)
824 {
825 unsigned char *next_header = TINY_BLOCK_HEADER_FOR_PTR(next_block);
826 msize_t next_index = TINY_INDEX_FOR_PTR(next_block);
827
828 if (!BITARRAY_BIT(next_header, next_index))
829 return TINY_FREE_SIZE(ptr);
830 }
831 return 1;
832}
833
834/*
835 * Get the size of the previous free block, which is stored in the last two
836 * bytes of the block. If the previous block is not free, then the result is
837 * undefined.
838 */
839static msize_t
840get_tiny_previous_free_msize(const void *ptr)
841{
842 // check whether the previous block is in the tiny region and a block header
843 // if so, then the size of the previous block is one, and there is no stored
844 // size.
845 if (ptr != TINY_REGION_FOR_PTR(ptr))
846 {
847 void *prev_block = (void *)((uintptr_t)ptr - TINY_QUANTUM);
848 unsigned char *prev_header = TINY_BLOCK_HEADER_FOR_PTR(prev_block);
849 msize_t prev_index = TINY_INDEX_FOR_PTR(prev_block);
850 if (BITARRAY_BIT(prev_header, prev_index))
851 return 1;
852 return TINY_PREVIOUS_MSIZE(ptr);
853 }
854 // don't read possibly unmapped memory before the beginning of the region
855 return 0;
856}
857
858static INLINE msize_t
859get_tiny_meta_header(const void *ptr, boolean_t *is_free)
860{
861 // returns msize and is_free
862 // may return 0 for the msize component (meaning 65536)
863 unsigned char *block_header;
864 unsigned char *in_use;
865 msize_t index;
866 unsigned byte_index;
867
868 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
869 index = TINY_INDEX_FOR_PTR(ptr);
870 byte_index = index >> 3;
871
872 block_header += byte_index;
873 index &= 7;
874 *is_free = 0;
875 if (!BITMAP32_BIT(*block_header, index))
876 return 0;
877 in_use = TINY_INUSE_FOR_HEADER(block_header);
878 if (!BITMAP32_BIT(*in_use, index)) {
879 *is_free = 1;
880 return get_tiny_free_size(ptr);
881 }
882 uint32_t *addr = (uint32_t *)((uintptr_t)block_header & ~3);
883 uint32_t word0 = OSReadLittleInt32(addr, 0) >> index;
884 uint32_t word1 = OSReadLittleInt32(addr, 4) << (8 - index);
885 uint32_t bits = (((uintptr_t)block_header & 3) * 8); // precision loss on LP64 OK here
886 uint32_t word = (word0 >> bits) | (word1 << (24 - bits));
887 uint32_t result = ffs(word >> 1);
888 return result;
889}
890
891static INLINE void
892set_tiny_meta_header_in_use(const void *ptr, msize_t msize)
893{
894 unsigned char *block_header;
895 unsigned char *in_use;
896 msize_t index;
897 unsigned byte_index;
898 msize_t clr_msize;
899 unsigned end_bit;
900
901 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
902 index = TINY_INDEX_FOR_PTR(ptr);
903 byte_index = index >> 3;
904
905#if DEBUG_MALLOC
906 if (msize >= 32)
907 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr, msize);
908 if ((unsigned)index + (unsigned)msize > 0x10000)
909 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr, msize);
910#endif
911 block_header += byte_index;
912 index &= 7;
913 BITMAP32_SET(*block_header, index);
914 in_use = TINY_INUSE_FOR_HEADER(block_header);
915 BITMAP32_SET(*in_use, index);
916 index++;
917 clr_msize = msize-1;
918 if (clr_msize) {
919 byte_index = index >> 3;
920 block_header += byte_index; in_use += byte_index;
921 index &= 7;
922 end_bit = index + clr_msize;
923 bitarray_mclr(block_header, index, end_bit);
924 bitarray_mclr(in_use, index, end_bit);
925 }
926 BITARRAY_SET(block_header, index+clr_msize); // we set the block_header bit for the following block to reaffirm next block is a block
927#if DEBUG_MALLOC
928 {
929 boolean_t ff;
930 msize_t mf;
931
932 mf = get_tiny_meta_header(ptr, &ff);
933 if (msize != mf) {
934 malloc_printf("setting header for tiny in_use %p : %d\n", ptr, msize);
935 malloc_printf("reading header for tiny %p : %d %d\n", ptr, mf, ff);
936 }
937 }
938#endif
939}
940
941static INLINE void
942set_tiny_meta_header_middle(const void *ptr)
943{
944 // indicates this block is in the middle of an in use block
945 unsigned char *block_header;
946 unsigned char *in_use;
947 msize_t index;
948
949 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
950 in_use = TINY_INUSE_FOR_HEADER(block_header);
951 index = TINY_INDEX_FOR_PTR(ptr);
952
953 BITARRAY_CLR(block_header, index);
954 BITARRAY_CLR(in_use, index);
955}
956
957static INLINE void
958set_tiny_meta_header_free(const void *ptr, msize_t msize)
959{
960 // !msize is acceptable and means 65536
961 unsigned char *block_header;
962 unsigned char *in_use;
963 msize_t index;
964
965 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
966 in_use = TINY_INUSE_FOR_HEADER(block_header);
967 index = TINY_INDEX_FOR_PTR(ptr);
968
969#if DEBUG_MALLOC
970 if ((unsigned)index + (unsigned)msize > 0x10000) {
971 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr, msize);
972 }
973#endif
974 BITARRAY_SET(block_header, index);
975 BITARRAY_CLR(in_use, index);
976 // mark the end of this block if msize is > 1. For msize == 0, the whole
977 // region is free, so there is no following block. For msize == 1, there is
978 // no space to write the size on 64 bit systems. The size for 1 quantum
979 // blocks is computed from the metadata bitmaps.
980 if (msize > 1) {
981 void *follower = FOLLOWING_TINY_PTR(ptr, msize);
982 TINY_PREVIOUS_MSIZE(follower) = msize;
983 TINY_FREE_SIZE(ptr) = msize;
984 }
985 if (msize == 0) {
986 TINY_FREE_SIZE(ptr) = msize;
987 }
988#if DEBUG_MALLOC
989 boolean_t ff;
990 msize_t mf = get_tiny_meta_header(ptr, &ff);
991 if ((msize != mf) || !ff) {
992 malloc_printf("setting header for tiny free %p : %u\n", ptr, msize);
993 malloc_printf("reading header for tiny %p : %u %u\n", ptr, mf, ff);
994 }
995#endif
996}
997
998static INLINE boolean_t
999tiny_meta_header_is_free(const void *ptr)
1000{
1001 unsigned char *block_header;
1002 unsigned char *in_use;
1003 msize_t index;
1004
1005 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1006 in_use = TINY_INUSE_FOR_HEADER(block_header);
1007 index = TINY_INDEX_FOR_PTR(ptr);
1008 if (!BITARRAY_BIT(block_header, index))
1009 return 0;
1010 return !BITARRAY_BIT(in_use, index);
1011}
1012
1013static INLINE void *
1014tiny_previous_preceding_free(void *ptr, msize_t *prev_msize)
1015{
1016 // returns the previous block, assuming and verifying it's free
1017 unsigned char *block_header;
1018 unsigned char *in_use;
1019 msize_t index;
1020 msize_t previous_msize;
1021 msize_t previous_index;
1022 void *previous_ptr;
1023
1024 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1025 in_use = TINY_INUSE_FOR_HEADER(block_header);
1026 index = TINY_INDEX_FOR_PTR(ptr);
1027
1028 if (!index)
1029 return NULL;
1030 if ((previous_msize = get_tiny_previous_free_msize(ptr)) > index)
1031 return NULL;
1032
1033 previous_index = index - previous_msize;
1034 previous_ptr = (void *)(TINY_REGION_FOR_PTR(ptr) + TINY_BYTES_FOR_MSIZE(previous_index));
1035 if (!BITARRAY_BIT(block_header, previous_index))
1036 return NULL;
1037 if (BITARRAY_BIT(in_use, previous_index))
1038 return NULL;
1039 if (get_tiny_free_size(previous_ptr) != previous_msize)
1040 return NULL;
1041
1042 // conservative check did match true check
1043 *prev_msize = previous_msize;
1044 return previous_ptr;
1045}
1046
1047/*
1048 * Adds an item to the proper free list, and also marks the meta-header of the
1049 * block properly.
1050 * Assumes szone has been locked
1051 */
1052static void
1053tiny_free_list_add_ptr(szone_t *szone, void *ptr, msize_t msize)
1054{
1055 grain_t slot = (!msize || (msize >= NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS - 1 : msize - 1;
1056 free_list_t *free_ptr = ptr;
1057 free_list_t *free_head = szone->tiny_free_list[slot];
1058
1059#if DEBUG_MALLOC
1060 if (LOG(szone,ptr)) {
1061 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
1062 }
1063 if (((uintptr_t)ptr) & (TINY_QUANTUM - 1)) {
1064 szone_error(szone, "tiny_free_list_add_ptr: Unaligned ptr", ptr, NULL);
1065 }
1066#endif
1067 set_tiny_meta_header_free(ptr, msize);
1068 if (free_head) {
1069 free_list_checksum(szone, free_head, __PRETTY_FUNCTION__);
1070#if DEBUG_MALLOC
1071 if (free_list_unchecksum_ptr(free_head->previous)) {
1072 szone_error(szone, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr,
1073 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr, slot, free_head, free_head->previous.p);
1074 }
1075 if (! tiny_meta_header_is_free(free_head)) {
1076 szone_error(szone, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr,
1077 "ptr=%p slot=%d free_head=%p\n", ptr, slot, free_head);
1078 }
1079#endif
1080 free_head->previous.u = free_list_checksum_ptr(free_ptr);
1081 } else {
1082 BITMAP32_SET(szone->tiny_bitmap, slot);
1083 }
1084 free_ptr->previous.p = NULL;
1085 free_ptr->next.p = free_head;
1086 free_list_set_checksum(szone, free_ptr);
1087 szone->tiny_free_list[slot] = free_ptr;
1088}
1089
1090/*
1091 * Removes the item pointed to by ptr in the proper free list.
1092 * Assumes szone has been locked
1093 */
1094static INLINE void
1095tiny_free_list_remove_ptr(szone_t *szone, void *ptr, msize_t msize)
1096{
1097 grain_t slot = (!msize || (msize >= NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS - 1 : msize - 1;
1098 free_list_t *free_ptr = ptr, *next, *previous;
1099 free_list_checksum(szone, free_ptr, __PRETTY_FUNCTION__);
1100
1101 next = free_list_unchecksum_ptr(free_ptr->next);
1102 previous = free_list_unchecksum_ptr(free_ptr->previous);
1103
1104#if DEBUG_MALLOC
1105 if (LOG(szone,ptr)) {
1106 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
1107 }
1108#endif
1109 if (!previous) {
1110 // The block to remove is the head of the free list
1111#if DEBUG_MALLOC
1112 if (szone->tiny_free_list[slot] != ptr) {
1113 szone_error(szone, "tiny_free_list_remove_ptr: Internal invariant broken (szone->tiny_free_list[slot])", ptr,
1114 "ptr=%p slot=%d msize=%d szone->tiny_free_list[slot]=%p\n",
1115 ptr, slot, msize, szone->tiny_free_list[slot]);
1116 return;
1117 }
1118#endif
1119 szone->tiny_free_list[slot] = next;
1120 if (!next) BITMAP32_CLR(szone->tiny_bitmap, slot);
1121 } else {
1122 // We know free_ptr is already checksummed, so we don't need to do it
1123 // again.
1124 previous->next = free_ptr->next;
1125 }
1126 if (next) {
1127 // We know free_ptr is already checksummed, so we don't need to do it
1128 // again.
1129 next->previous = free_ptr->previous;
1130 }
1131}
1132
1133/*
1134 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
1135 * or NULL if not found.
1136 */
1137static INLINE region_t *
1138tiny_region_for_ptr_no_lock(szone_t *szone, const void *ptr)
1139{
1140 return hash_lookup_region_no_lock(szone->tiny_regions,
1141 szone->num_tiny_regions_allocated,
1142 TINY_REGION_FOR_PTR(ptr));
1143}
1144
1145static INLINE void
1146tiny_free_no_lock(szone_t *szone, region_t *region, void *ptr, msize_t msize)
1147{
1148 size_t original_size = TINY_BYTES_FOR_MSIZE(msize);
1149 void *next_block = ((char *)ptr + original_size);
1150 msize_t previous_msize;
1151 void *previous;
1152 msize_t next_msize;
1153 free_list_t *big_free_block;
1154 free_list_t *after_next_block;
1155 free_list_t *before_next_block;
1156
1157#if DEBUG_MALLOC
1158 if (LOG(szone,ptr)) {
1159 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
1160 }
1161 if (! msize) {
1162 szone_error(szone, "trying to free tiny block that is too small", ptr,
1163 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
1164 }
1165#endif
1166 // We try to coalesce this block with the preceeding one
1167 previous = tiny_previous_preceding_free(ptr, &previous_msize);
1168 if (previous) {
1169#if DEBUG_MALLOC
1170 if (LOG(szone, ptr) || LOG(szone,previous)) {
1171 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr, previous);
1172 }
1173#endif
1174 // clear the meta_header since this is no longer the start of a block
1175 set_tiny_meta_header_middle(ptr);
1176 tiny_free_list_remove_ptr(szone, previous, previous_msize);
1177 ptr = previous;
1178 msize += previous_msize;
1179 }
1180 // We try to coalesce with the next block
1181 if ((next_block < TINY_REGION_END(*region)) && tiny_meta_header_is_free(next_block)) {
1182 next_msize = get_tiny_free_size(next_block);
1183#if DEBUG_MALLOC
1184 if (LOG(szone, ptr) || LOG(szone, next_block)) {
1185 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
1186 ptr, msize, next_block, next_msize);
1187 }
1188#endif
1189 // If we are coalescing with the next block, and the next block is in
1190 // the last slot of the free list, then we optimize this case here to
1191 // avoid removing next_block from the slot 31 and then adding ptr back
1192 // to slot 31.
1193 if (next_msize >= NUM_TINY_SLOTS) {
1194 msize += next_msize;
1195 big_free_block = (free_list_t *)next_block;
1196 free_list_checksum(szone, big_free_block, __PRETTY_FUNCTION__);
1197 after_next_block = free_list_unchecksum_ptr(big_free_block->next);
1198 before_next_block = free_list_unchecksum_ptr(big_free_block->previous);
1199 if (!before_next_block) {
1200 szone->tiny_free_list[NUM_TINY_SLOTS-1] = ptr;
1201 } else {
1202 before_next_block->next.u = free_list_checksum_ptr(ptr);
1203 }
1204 if (after_next_block) {
1205 after_next_block->previous.u = free_list_checksum_ptr(ptr);
1206 }
1207 // we don't need to checksum these since they are already checksummed
1208 ((free_list_t *)ptr)->previous = big_free_block->previous;
1209 ((free_list_t *)ptr)->next = big_free_block->next;
1210
1211 // clear the meta_header to enable coalescing backwards
1212 set_tiny_meta_header_middle(big_free_block);
1213 set_tiny_meta_header_free(ptr, msize);
1214 goto tiny_free_ending;
1215 }
1216 tiny_free_list_remove_ptr(szone, next_block, next_msize);
1217 set_tiny_meta_header_middle(next_block); // clear the meta_header to enable coalescing backwards
1218 msize += next_msize;
1219 }
1220#if !TINY_CACHE
1221 // The tiny cache already scribbles free blocks as they go through the
1222 // cache, so we do not need to do it here.
1223 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && msize) {
1224 memset(ptr, 0x55, TINY_BYTES_FOR_MSIZE(msize));
1225 }
1226#endif
1227 tiny_free_list_add_ptr(szone, ptr, msize);
1228 tiny_free_ending:
1229 // When in proper debug mode we write on the memory to help debug memory smashers
1230 szone->num_tiny_objects--;
1231 szone->num_bytes_in_tiny_objects -= original_size; // we use original_size and not msize to avoid double counting the coalesced blocks
1232}
1233
1234// Allocates from the last region or a freshly allocated region
1235static void *
1236tiny_malloc_from_region_no_lock(szone_t *szone, msize_t msize)
1237{
1238 void *last_block, *ptr, *aligned_address;
1239 unsigned char *last_header;
1240 msize_t last_msize, last_index;
1241
1242 // Before anything we transform any remaining tiny_bytes_free_at_end into a
1243 // regular free block. We take special care here to update the bitfield
1244 // information, since we are bypassing the normal free codepath. If there
1245 // is more than one quanta worth of memory in tiny_bytes_free_at_end, then
1246 // there will be two block headers:
1247 // 1) header for the free space at end, msize = 1
1248 // 2) header inserted by set_tiny_meta_header_in_use after block
1249 // We must clear the second one so that when the free block's size is
1250 // queried, we do not think the block is only 1 quantum in size because
1251 // of the second set header bit.
1252 if (szone->tiny_bytes_free_at_end) {
1253 last_block = TINY_REGION_END(szone->last_tiny_region) - szone->tiny_bytes_free_at_end;
1254 last_msize = TINY_MSIZE_FOR_BYTES(szone->tiny_bytes_free_at_end);
1255 last_header = TINY_BLOCK_HEADER_FOR_PTR(last_block);
1256 last_index = TINY_INDEX_FOR_PTR(last_block);
1257
1258 if (last_index != (NUM_TINY_BLOCKS - 1))
1259 BITARRAY_CLR(last_header, last_index + 1);
1260
1261 tiny_free_list_add_ptr(szone, last_block, last_msize);
1262 szone->tiny_bytes_free_at_end = 0;
1263 }
1264 // time to create a new region
1265 aligned_address = allocate_pages(szone, TINY_REGION_SIZE, TINY_BLOCKS_ALIGN, 0, VM_MEMORY_MALLOC_TINY);
1266 if (!aligned_address) // out of memory!
1267 return NULL;
1268 // We set the padding after block_header to be all 1
1269 ((uint32_t *)(aligned_address + TINY_HEADER_START + (NUM_TINY_BLOCKS >> 3)))[0] = ~0;
1270
1271 // Check to see if the hash ring of tiny regions needs to grow. Try to
1272 // avoid the hash ring becoming too dense.
1273 if (szone->num_tiny_regions_allocated < (2 * szone->num_tiny_regions)) {
1274 region_t *new_regions;
1275 size_t new_size;
1276 new_regions = hash_regions_grow_no_lock(szone, szone->tiny_regions,
1277 szone->num_tiny_regions_allocated,
1278 &new_size);
1279 // Do not deallocate the current tiny_regions allocation since someone may
1280 // be iterating it. Instead, just leak it.
1281 szone->tiny_regions = new_regions;
1282 szone->num_tiny_regions_allocated = new_size;
1283 }
1284 // Insert the new region into the hash ring, and update malloc statistics
1285 hash_region_insert_no_lock(szone->tiny_regions,
1286 szone->num_tiny_regions_allocated,
1287 aligned_address);
1288 szone->last_tiny_region = aligned_address;
1289
1290 szone->num_tiny_regions++;
1291 ptr = aligned_address;
1292 set_tiny_meta_header_in_use(ptr, msize);
1293 szone->num_tiny_objects++;
1294 szone->num_bytes_in_tiny_objects += TINY_BYTES_FOR_MSIZE(msize);
1295
1296 // We put a header on the last block so that it appears in use (for coalescing, etc...)
1297 set_tiny_meta_header_in_use(ptr + TINY_BYTES_FOR_MSIZE(msize), 1);
1298 szone->tiny_bytes_free_at_end = TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS - msize);
1299#if DEBUG_MALLOC
1300 if (LOG(szone,ptr)) {
1301 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
1302 }
1303#endif
1304 return ptr;
1305}
1306
1307static INLINE boolean_t
1308try_realloc_tiny_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size)
1309{
1310 // returns 1 on success
1311 msize_t index;
1312 msize_t old_msize;
1313 unsigned next_index;
1314 void *next_block;
1315 boolean_t is_free;
1316 msize_t next_msize, coalesced_msize, leftover_msize;
1317 void *leftover;
1318
1319 index = TINY_INDEX_FOR_PTR(ptr);
1320 old_msize = TINY_MSIZE_FOR_BYTES(old_size);
1321 next_index = index + old_msize;
1322
1323 if (next_index >= NUM_TINY_BLOCKS) {
1324 return 0;
1325 }
1326 next_block = (char *)ptr + old_size;
1327 SZONE_LOCK(szone);
1328 is_free = tiny_meta_header_is_free(next_block);
1329 if (!is_free) {
1330 SZONE_UNLOCK(szone);
1331 return 0; // next_block is in use;
1332 }
1333 next_msize = get_tiny_free_size(next_block);
1334 if (old_size + TINY_MSIZE_FOR_BYTES(next_msize) < new_size) {
1335 SZONE_UNLOCK(szone);
1336 return 0; // even with next block, not enough
1337 }
1338 tiny_free_list_remove_ptr(szone, next_block, next_msize);
1339 set_tiny_meta_header_middle(next_block); // clear the meta_header to enable coalescing backwards
1340 coalesced_msize = TINY_MSIZE_FOR_BYTES(new_size - old_size + TINY_QUANTUM - 1);
1341 leftover_msize = next_msize - coalesced_msize;
1342 if (leftover_msize) {
1343 leftover = next_block + TINY_BYTES_FOR_MSIZE(coalesced_msize);
1344 tiny_free_list_add_ptr(szone, leftover, leftover_msize);
1345 }
1346 set_tiny_meta_header_in_use(ptr, old_msize + coalesced_msize);
1347#if DEBUG_MALLOC
1348 if (LOG(szone,ptr)) {
1349 malloc_printf("in try_realloc_tiny_in_place(), ptr=%p, msize=%d\n", ptr, old_msize + coalesced_msize);
1350 }
1351#endif
1352 szone->num_bytes_in_tiny_objects += TINY_BYTES_FOR_MSIZE(coalesced_msize);
1353 SZONE_UNLOCK(szone);
1354 CHECK(szone, __PRETTY_FUNCTION__);
1355 return 1;
1356}
1357
1358static boolean_t
1359tiny_check_region(szone_t *szone, region_t region)
1360{
1361 uintptr_t start, ptr, region_end;
1362 boolean_t prev_free = 0;
1363 boolean_t is_free;
1364 msize_t msize;
1365 free_list_t *free_head;
1366 void *follower, *previous, *next;
1367
1368 /* establish region limits */
1369 start = (uintptr_t)TINY_REGION_ADDRESS(region);
1370 ptr = start;
1371 region_end = (uintptr_t)TINY_REGION_END(region);
1372
1373 /*
1374 * The last region may have a trailing chunk which has not been converted into inuse/freelist
1375 * blocks yet.
1376 */
1377 if (region == szone->last_tiny_region)
1378 region_end -= szone->tiny_bytes_free_at_end;
1379
1380
1381 /*
1382 * Scan blocks within the region.
1383 */
1384 while (ptr < region_end) {
1385 /*
1386 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
1387 * free.
1388 */
1389 msize = get_tiny_meta_header((void *)ptr, &is_free);
1390 if (is_free && !msize && (ptr == start)) {
1391 return 1;
1392 }
1393
1394 /*
1395 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
1396 * corrupt.
1397 */
1398 if (!msize) {
1399 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
1400 ptr, msize);
1401 return 0;
1402 }
1403
1404 if (!is_free) {
1405 /*
1406 * In use blocks cannot be more than 31 quanta large.
1407 */
1408 prev_free = 0;
1409 if (msize > 31 * TINY_QUANTUM) {
1410 malloc_printf("*** invariant broken for %p this tiny msize=%d[%p] - size is too large\n",
1411 ptr, msize, msize);
1412 return 0;
1413 }
1414 /* move to next block */
1415 ptr += TINY_BYTES_FOR_MSIZE(msize);
1416 } else {
1417 /*
1418 * Free blocks must have been coalesced, we cannot have a free block following another
1419 * free block.
1420 */
1421 if (prev_free) {
1422 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
1423 ptr, msize);
1424 return 0;
1425 }
1426 prev_free = 1;
1427 /*
1428 * Check the integrity of this block's entry in its freelist.
1429 */
1430 free_head = (free_list_t *)ptr;
1431 free_list_checksum(szone, free_head, __PRETTY_FUNCTION__);
1432 previous = free_list_unchecksum_ptr(free_head->previous);
1433 next = free_list_unchecksum_ptr(free_head->next);
1434 if (previous && !tiny_meta_header_is_free(previous)) {
1435 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
1436 ptr, previous);
1437 return 0;
1438 }
1439 if (next && !tiny_meta_header_is_free(next)) {
1440 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
1441 ptr, next);
1442 return 0;
1443 }
1444 /*
1445 * Check the free block's trailing size value.
1446 */
1447 follower = FOLLOWING_TINY_PTR(ptr, msize);
1448 if (((uintptr_t)follower != region_end) && (get_tiny_previous_free_msize(follower) != msize)) {
1449 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
1450 "(end marker incorrect) should be %d; in fact %d\n",
1451 ptr, follower, TINY_REGION_ADDRESS(region), region_end, msize, get_tiny_previous_free_msize(follower));
1452 return 0;
1453 }
1454 /* move to next block */
1455 ptr = (uintptr_t)follower;
1456 }
1457 }
1458 /*
1459 * Ensure that we scanned the entire region
1460 */
1461 if (ptr != region_end) {
1462 malloc_printf("*** invariant broken for region end %p - %p\n", ptr, region_end);
1463 return 0;
1464 }
1465 /*
1466 * Check the trailing block's integrity.
1467 */
1468 if (region == szone->last_tiny_region) {
1469 if (szone->tiny_bytes_free_at_end) {
1470 msize = get_tiny_meta_header((void *)ptr, &is_free);
1471 if (is_free || (msize != 1)) {
1472 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr, msize, is_free);
1473 }
1474 }
1475 }
1476 return 1;
1477}
1478
1479static kern_return_t
1480tiny_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone, memory_reader_t reader, vm_range_recorder_t recorder)
1481{
1482 size_t num_regions = szone->num_tiny_regions_allocated;
1483 void *last_tiny_free = szone->last_tiny_free;
1484 size_t index;
1485 region_t *regions;
1486 vm_range_t buffer[MAX_RECORDER_BUFFER];
1487 unsigned count = 0;
1488 kern_return_t err;
1489 region_t region;
1490 vm_range_t range;
1491 vm_range_t admin_range;
1492 vm_range_t ptr_range;
1493 unsigned char *mapped_region;
1494 unsigned char *block_header;
1495 unsigned char *in_use;
1496 unsigned block_index;
1497 unsigned block_limit;
1498 boolean_t is_free;
1499 msize_t msize;
1500 void *mapped_ptr;
1501 unsigned bit;
1502 vm_address_t last_tiny_free_ptr = 0;
1503 msize_t last_tiny_free_msize = 0;
1504
1505 if (last_tiny_free) {
1506 last_tiny_free_ptr = (uintptr_t) last_tiny_free & ~(TINY_QUANTUM - 1);
1507 last_tiny_free_msize = (uintptr_t) last_tiny_free & (TINY_QUANTUM - 1);
1508 }
1509
1510 err = reader(task, (vm_address_t)szone->tiny_regions, sizeof(region_t) * num_regions, (void **)&regions);
1511 if (err) return err;
1512 for (index = 0; index < num_regions; ++index) {
1513 region = regions[index];
1514 if (region) {
1515 range.address = (vm_address_t)TINY_REGION_ADDRESS(region);
1516 range.size = (vm_size_t)TINY_REGION_SIZE;
1517 if (type_mask & MALLOC_ADMIN_REGION_RANGE_TYPE) {
1518 admin_range.address = range.address + TINY_HEADER_START;
1519 admin_range.size = TINY_HEADER_SIZE;
1520 recorder(task, context, MALLOC_ADMIN_REGION_RANGE_TYPE, &admin_range, 1);
1521 }
1522 if (type_mask & (MALLOC_PTR_REGION_RANGE_TYPE | MALLOC_ADMIN_REGION_RANGE_TYPE)) {
1523 ptr_range.address = range.address;
1524 ptr_range.size = NUM_TINY_BLOCKS * TINY_QUANTUM;
1525 recorder(task, context, MALLOC_PTR_REGION_RANGE_TYPE, &ptr_range, 1);
1526 }
1527 if (type_mask & MALLOC_PTR_IN_USE_RANGE_TYPE) {
1528 err = reader(task, range.address, range.size, (void **)&mapped_region);
1529 if (err)
1530 return err;
1531
1532 block_header = (unsigned char *)(mapped_region + TINY_HEADER_START);
1533 in_use = TINY_INUSE_FOR_HEADER(block_header);
1534 block_index = 0;
1535 block_limit = NUM_TINY_BLOCKS;
1536 if (region == szone->last_tiny_region)
1537 block_limit -= TINY_MSIZE_FOR_BYTES(szone->tiny_bytes_free_at_end);
1538
1539 while (block_index < block_limit) {
1540 vm_size_t block_offset = TINY_BYTES_FOR_MSIZE(block_index);
1541 is_free = !BITARRAY_BIT(in_use, block_index);
1542 if (is_free) {
1543 mapped_ptr = mapped_region + block_offset;
1544
1545 // mapped_region, the address at which 'range' in 'task' has been
1546 // mapped into our process, is not necessarily aligned to
1547 // TINY_BLOCKS_ALIGN.
1548 //
1549 // Since the code in get_tiny_free_size() assumes the pointer came
1550 // from a properly aligned tiny region, and mapped_region is not
1551 // necessarily aligned, then do the size calculation directly.
1552 // If the next bit is set in the header bitmap, then the size is one
1553 // quantum. Otherwise, read the size field.
1554 if (!BITARRAY_BIT(block_header, block_index+1))
1555 msize = TINY_FREE_SIZE(mapped_ptr);
1556 else
1557 msize = 1;
1558
1559 if (!msize)
1560 break;
1561 } else if (range.address + block_offset != last_tiny_free_ptr) {
1562 msize = 1;
1563 bit = block_index + 1;
1564 while (! BITARRAY_BIT(block_header, bit)) {
1565 bit++;
1566 msize ++;
1567 }
1568 buffer[count].address = range.address + block_offset;
1569 buffer[count].size = TINY_BYTES_FOR_MSIZE(msize);
1570 count++;
1571 if (count >= MAX_RECORDER_BUFFER) {
1572 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
1573 count = 0;
1574 }
1575 } else {
1576 // Block is not free but it matches last_tiny_free_ptr so even
1577 // though it is not marked free in the bitmap, we treat it as if
1578 // it is and move on
1579 msize = last_tiny_free_msize;
1580 }
1581 block_index += msize;
1582 }
1583 }
1584 }
1585 }
1586 if (count) {
1587 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
1588 }
1589 return 0;
1590}
1591
1592static void *
1593tiny_malloc_from_free_list(szone_t *szone, msize_t msize)
1594{
1595 // Assumes we've locked the region
1596 free_list_t *ptr;
1597 msize_t this_msize;
1598 grain_t slot = msize - 1;
1599 free_list_t **free_list = szone->tiny_free_list;
1600 free_list_t **the_slot = free_list + slot;
1601 free_list_t *next;
1602 free_list_t **limit;
1603 unsigned bitmap;
1604 msize_t leftover_msize;
1605 free_list_t *leftover_ptr;
1606
1607 // Assumes locked
1608 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
1609
1610 // Look for an exact match by checking the freelist for this msize.
1611 //
1612 ptr = *the_slot;
1613 if (ptr) {
1614 next = free_list_unchecksum_ptr(ptr->next);
1615 if (next) {
1616 next->previous = ptr->previous;
1617 } else {
1618 BITMAP32_CLR(szone->tiny_bitmap, slot);
1619 }
1620 *the_slot = next;
1621 this_msize = msize;
1622#if DEBUG_MALLOC
1623 if (LOG(szone, ptr)) {
1624 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr, this_msize);
1625 }
1626#endif
1627 goto return_tiny_alloc;
1628 }
1629
1630 // Mask off the bits representing slots holding free blocks smaller than the
1631 // size we need. If there are no larger free blocks, try allocating from
1632 // the free space at the end of the tiny region.
1633 bitmap = szone->tiny_bitmap & ~ ((1 << slot) - 1);
1634 if (!bitmap)
1635 goto try_tiny_malloc_from_end;
1636
1637 slot = BITMAP32_CTZ(bitmap);
1638 limit = free_list + NUM_TINY_SLOTS - 1;
1639 free_list += slot;
1640
1641 // Iterate over freelists looking for free blocks, starting at first list
1642 // which is not empty, and contains blocks which are large enough to satisfy
1643 // our request.
1644 while (free_list < limit) {
1645 ptr = *free_list;
1646 if (ptr) {
1647 next = free_list_unchecksum_ptr(ptr->next);
1648 *free_list = next;
1649 this_msize = get_tiny_free_size(ptr);
1650 if (next) {
1651 next->previous = ptr->previous;
1652 } else {
1653 BITMAP32_CLR(szone->tiny_bitmap, this_msize - 1);
1654 }
1655 goto add_leftover_and_proceed;
1656 }
1657 free_list++;
1658 }
1659
1660 // We are now looking at the last slot, which contains blocks equal to, or
1661 // due to coalescing of free blocks, larger than 31 * tiny quantum size.
1662 // If the last freelist is not empty, and the head contains a block that is
1663 // larger than our request, then the remainder is put back on the free list.
1664 ptr = *limit;
1665 if (ptr) {
1666 free_list_checksum(szone, ptr, __PRETTY_FUNCTION__);
1667 this_msize = get_tiny_free_size(ptr);
1668 next = free_list_unchecksum_ptr(ptr->next);
1669 if (this_msize - msize >= NUM_TINY_SLOTS) {
1670 // the leftover will go back to the free list, so we optimize by
1671 // modifying the free list rather than a pop and push of the head
1672 leftover_msize = this_msize - msize;
1673 leftover_ptr = (free_list_t *)((unsigned char *)ptr + TINY_BYTES_FOR_MSIZE(msize));
1674 *limit = leftover_ptr;
1675 if (next) {
1676 next->previous.u = free_list_checksum_ptr(leftover_ptr);
1677 }
1678 leftover_ptr->previous = ptr->previous;
1679 leftover_ptr->next = ptr->next;
1680 set_tiny_meta_header_free(leftover_ptr, leftover_msize);
1681#if DEBUG_MALLOC
1682 if (LOG(szone,ptr)) {
1683 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr, msize, this_msize);
1684 }
1685#endif
1686 this_msize = msize;
1687 goto return_tiny_alloc;
1688 }
1689 if (next) {
1690 next->previous = ptr->previous;
1691 }
1692 *limit = next;
1693 goto add_leftover_and_proceed;
1694 }
1695
1696try_tiny_malloc_from_end:
1697 // Let's see if we can use szone->tiny_bytes_free_at_end
1698 if (szone->tiny_bytes_free_at_end >= TINY_BYTES_FOR_MSIZE(msize)) {
1699 ptr = (free_list_t *)(TINY_REGION_END(szone->last_tiny_region) - szone->tiny_bytes_free_at_end);
1700 szone->tiny_bytes_free_at_end -= TINY_BYTES_FOR_MSIZE(msize);
1701 if (szone->tiny_bytes_free_at_end) {
1702 // let's add an in use block after ptr to serve as boundary
1703 set_tiny_meta_header_in_use((unsigned char *)ptr + TINY_BYTES_FOR_MSIZE(msize), 1);
1704 }
1705 this_msize = msize;
1706#if DEBUG_MALLOC
1707 if (LOG(szone, ptr)) {
1708 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr, msize);
1709 }
1710#endif
1711 goto return_tiny_alloc;
1712 }
1713 return NULL;
1714
1715add_leftover_and_proceed:
1716 if (!this_msize || (this_msize > msize)) {
1717 leftover_msize = this_msize - msize;
1718 leftover_ptr = (free_list_t *)((unsigned char *)ptr + TINY_BYTES_FOR_MSIZE(msize));
1719#if DEBUG_MALLOC
1720 if (LOG(szone,ptr)) {
1721 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr, this_msize);
1722 }
1723#endif
1724 tiny_free_list_add_ptr(szone, leftover_ptr, leftover_msize);
1725 this_msize = msize;
1726 }
1727
1728return_tiny_alloc:
1729 szone->num_tiny_objects++;
1730 szone->num_bytes_in_tiny_objects += TINY_BYTES_FOR_MSIZE(this_msize);
1731#if DEBUG_MALLOC
1732 if (LOG(szone,ptr)) {
1733 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr, this_msize, msize);
1734 }
1735#endif
1736 set_tiny_meta_header_in_use(ptr, this_msize);
1737 return ptr;
1738}
1739
1740static INLINE void *
1741tiny_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested)
1742{
1743 boolean_t locked = 0;
1744 void *ptr;
1745
1746#if DEBUG_MALLOC
1747 if (!msize) {
1748 szone_error(szone, "invariant broken (!msize) in allocation (region)", NULL, NULL);
1749 return(NULL);
1750 }
1751#endif
1752#if TINY_CACHE
1753 ptr = szone->last_tiny_free;
1754 if ((((uintptr_t)ptr) & (TINY_QUANTUM - 1)) == msize) {
1755 // we have a candidate - let's lock to make sure
1756 LOCK_AND_NOTE_LOCKED(szone, locked);
1757 if (ptr == szone->last_tiny_free) {
1758 szone->last_tiny_free = NULL;
1759 SZONE_UNLOCK(szone);
1760 CHECK(szone, __PRETTY_FUNCTION__);
1761 ptr = (void *)((uintptr_t)ptr & ~ (TINY_QUANTUM - 1));
1762 if (cleared_requested) {
1763 memset(ptr, 0, TINY_BYTES_FOR_MSIZE(msize));
1764 }
1765#if DEBUG_MALLOC
1766 if (LOG(szone,ptr)) {
1767 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr, msize);
1768 }
1769#endif
1770 return ptr;
1771 }
1772 }
1773#endif
1774 // Except in rare occasions where we need to add a new region, we are going to end up locking, so we might as well lock right away to avoid doing unnecessary optimistic probes
1775 if (!locked) LOCK_AND_NOTE_LOCKED(szone, locked);
1776 ptr = tiny_malloc_from_free_list(szone, msize);
1777 if (ptr) {
1778 SZONE_UNLOCK(szone);
1779 CHECK(szone, __PRETTY_FUNCTION__);
1780 if (cleared_requested) {
1781 memset(ptr, 0, TINY_BYTES_FOR_MSIZE(msize));
1782 }
1783 return ptr;
1784 }
1785 ptr = tiny_malloc_from_region_no_lock(szone, msize);
1786 // we don't clear because this freshly allocated space is pristine
1787 SZONE_UNLOCK(szone);
1788 CHECK(szone, __PRETTY_FUNCTION__);
1789 return ptr;
1790}
1791
1792static INLINE void
1793free_tiny(szone_t *szone, void *ptr, region_t *tiny_region)
1794{
1795 msize_t msize;
1796 boolean_t is_free;
1797#if TINY_CACHE
1798 void *ptr2;
1799#endif
1800
1801 // ptr is known to be in tiny_region
1802 SZONE_LOCK(szone);
1803#if TINY_CACHE
1804 ptr2 = szone->last_tiny_free;
1805 /* check that we don't already have this pointer in the cache */
1806 if (ptr == (void *)((uintptr_t)ptr2 & ~ (TINY_QUANTUM - 1))) {
1807 szone_error(szone, "double free", ptr, NULL);
1808 return;
1809 }
1810#endif /* TINY_CACHE */
1811 msize = get_tiny_meta_header(ptr, &is_free);
1812 if (is_free) {
1813 szone_error(szone, "double free", ptr, NULL);
1814 return;
1815 }
1816#if DEBUG_MALLOC
1817 if (!msize) {
1818 malloc_printf("*** szone_free() block in use is too large: %p\n", ptr);
1819 return;
1820 }
1821#endif
1822#if TINY_CACHE
1823 if (msize < TINY_QUANTUM) { // to see if the bits fit in the last 4 bits
1824 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && msize)
1825 memset(ptr, 0x55, TINY_BYTES_FOR_MSIZE(msize));
1826 szone->last_tiny_free = (void *)(((uintptr_t)ptr) | msize);
1827 if (!ptr2) {
1828 SZONE_UNLOCK(szone);
1829 CHECK(szone, __PRETTY_FUNCTION__);
1830 return;
1831 }
1832 msize = (uintptr_t)ptr2 & (TINY_QUANTUM - 1);
1833 ptr = (void *)(((uintptr_t)ptr2) & ~(TINY_QUANTUM - 1));
1834 tiny_region = tiny_region_for_ptr_no_lock(szone, ptr);
1835 if (!tiny_region) {
1836 szone_error(szone, "double free (tiny cache)", ptr, NULL);
1837 }
1838 }
1839#endif
1840 tiny_free_no_lock(szone, tiny_region, ptr, msize);
1841 SZONE_UNLOCK(szone);
1842 CHECK(szone, __PRETTY_FUNCTION__);
1843}
1844
1845static void
1846print_tiny_free_list(szone_t *szone)
1847{
1848 grain_t slot = 0;
1849 free_list_t *ptr;
1850 _SIMPLE_STRING b = _simple_salloc();
1851
1852 if (b) {
1853 _simple_sappend(b, "tiny free sizes: ");
1854 while (slot < NUM_TINY_SLOTS) {
1855 ptr = szone->tiny_free_list[slot];
1856 if (ptr) {
1857 _simple_sprintf(b, "%s%y[%d]; ", (slot == NUM_TINY_SLOTS-1) ? ">=" : "", (slot+1)*TINY_QUANTUM, free_list_count(ptr));
1858 }
1859 slot++;
1860 }
1861 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
1862 _simple_sfree(b);
1863 }
1864}
1865
1866static void
1867print_tiny_region(boolean_t verbose, region_t region, size_t bytes_at_end)
1868{
1869 unsigned counts[1024];
1870 unsigned in_use = 0;
1871 uintptr_t start = (uintptr_t)TINY_REGION_ADDRESS(region);
1872 uintptr_t current = start;
1873 uintptr_t limit = (uintptr_t)TINY_REGION_END(region) - bytes_at_end;
1874 boolean_t is_free;
1875 msize_t msize;
1876 unsigned ci;
1877 _SIMPLE_STRING b;
1878
1879 memset(counts, 0, 1024 * sizeof(unsigned));
1880 while (current < limit) {
1881 msize = get_tiny_meta_header((void *)current, &is_free);
1882 if (is_free & !msize && (current == start)) {
1883 // first block is all free
1884 break;
1885 }
1886 if (!msize) {
1887 malloc_printf("*** error with %p: msize=%d\n", (void *)current, (unsigned)msize);
1888 break;
1889 }
1890 if (!is_free) {
1891 // block in use
1892 if (msize > 32)
1893 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current, msize);
1894 if (msize < 1024)
1895 counts[msize]++;
1896 in_use++;
1897 }
1898 current += TINY_BYTES_FOR_MSIZE(msize);
1899 }
1900 if ((b = _simple_salloc()) != NULL) {
1901 _simple_sprintf(b, "Tiny region [%p-%p, %y]\t", (void *)start, TINY_REGION_END(region), (int)TINY_REGION_SIZE);
1902 _simple_sprintf(b, "In_use=%d ", in_use);
1903 if (bytes_at_end) _simple_sprintf(b, "untouched=%ly", bytes_at_end);
1904 if (verbose && in_use) {
1905 _simple_sappend(b, "\n\tSizes in use: ");
1906 for (ci = 0; ci < 1024; ci++)
1907 if (counts[ci])
1908 _simple_sprintf(b, "%d[%d]", TINY_BYTES_FOR_MSIZE(ci), counts[ci]);
1909 }
1910 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
1911 _simple_sfree(b);
1912 }
1913}
1914
1915static boolean_t
1916tiny_free_list_check(szone_t *szone, grain_t slot)
1917{
1918 unsigned count = 0;
1919 free_list_t *ptr = szone->tiny_free_list[slot];
1920 free_list_t *previous = NULL;
1921 boolean_t is_free;
1922
1923 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
1924 while (ptr) {
1925 free_list_checksum(szone, ptr, __PRETTY_FUNCTION__);
1926 is_free = tiny_meta_header_is_free(ptr);
1927 if (! is_free) {
1928 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot, count, ptr);
1929 return 0;
1930 }
1931 if (((uintptr_t)ptr) & (TINY_QUANTUM - 1)) {
1932 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot, count, ptr);
1933 return 0;
1934 }
1935 if (!tiny_region_for_ptr_no_lock(szone, ptr)) {
1936 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot, count, ptr);
1937 return 0;
1938 }
1939 if (free_list_unchecksum_ptr(ptr->previous) != previous) {
1940 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot, count, ptr);
1941 return 0;
1942 }
1943 previous = ptr;
1944 ptr = free_list_unchecksum_ptr(ptr->next);
1945 count++;
1946 }
1947 return 1;
1948}
1949
1950/********************* SMALL FREE LIST UTILITIES ************************/
1951
1952/*
1953 * Mark a block as free. Only the first quantum of a block is marked thusly,
1954 * the remainder are marked "middle".
1955 */
1956static INLINE void
1957small_meta_header_set_is_free(msize_t *meta_headers, unsigned index, msize_t msize)
1958{
1959 meta_headers[index] = msize | SMALL_IS_FREE;
1960}
1961
1962/*
1963 * Mark a block as in use. Only the first quantum of a block is marked thusly,
1964 * the remainder are marked "middle".
1965 */
1966static INLINE void
1967small_meta_header_set_in_use(msize_t *meta_headers, msize_t index, msize_t msize)
1968{
1969 meta_headers[index] = msize;
1970}
1971
1972/*
1973 * Mark a quantum as being the second or later in a block.
1974 */
1975static INLINE void
1976small_meta_header_set_middle(msize_t *meta_headers, msize_t index)
1977{
1978 meta_headers[index] = 0;
1979}
1980
1981// Adds an item to the proper free list
1982// Also marks the header of the block properly
1983// Assumes szone has been locked
1984static void
1985small_free_list_add_ptr(szone_t *szone, void *ptr, msize_t msize)
1986{
1987 grain_t slot = (msize <= NUM_SMALL_SLOTS) ? msize - 1 : NUM_SMALL_SLOTS - 1;
1988 free_list_t *free_ptr = ptr;
1989 free_list_t *free_head = szone->small_free_list[slot];
1990 void *follower;
1991
1992#if DEBUG_MALLOC
1993 if (LOG(szone,ptr)) {
1994 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
1995 }
1996 if (((uintptr_t)ptr) & (SMALL_QUANTUM - 1)) {
1997 szone_error(szone, "small_free_list_add_ptr: Unaligned ptr", ptr, NULL);
1998 }
1999#endif
2000 if (free_head) {
2001 free_list_checksum(szone, free_head, __PRETTY_FUNCTION__);
2002#if DEBUG_MALLOC
2003 if (free_list_unchecksum_ptr(free_head->previous)) {
2004 szone_error(szone, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr,
2005 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr, slot, free_head, free_head->previous.p);
2006 }
2007 if (!SMALL_PTR_IS_FREE(free_head)) {
2008 szone_error(szone, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr,
2009 "ptr=%p slot=%d free_head=%p\n", ptr, slot, free_head);
2010 }
2011#endif
2012 free_head->previous.u = free_list_checksum_ptr(free_ptr);
2013 } else {
2014 BITMAP32_SET(szone->small_bitmap, slot);
2015 }
2016 free_ptr->previous.p = NULL;
2017 free_ptr->next.p = free_head;
2018 free_list_set_checksum(szone, free_ptr);
2019 szone->small_free_list[slot] = free_ptr;
2020 follower = ptr + SMALL_BYTES_FOR_MSIZE(msize);
2021 SMALL_PREVIOUS_MSIZE(follower) = msize;
2022}
2023
2024// Removes item in the proper free list
2025// msize could be read, but all callers have it so we pass it in
2026// Assumes szone has been locked
2027static void
2028small_free_list_remove_ptr(szone_t *szone, void *ptr, msize_t msize)
2029{
2030 grain_t slot = (msize <= NUM_SMALL_SLOTS) ? msize - 1 : NUM_SMALL_SLOTS - 1;
2031 free_list_t *free_ptr = ptr, *next, *previous;
2032 free_list_checksum(szone, free_ptr, __PRETTY_FUNCTION__);
2033
2034 next = free_list_unchecksum_ptr(free_ptr->next);
2035 previous = free_list_unchecksum_ptr(free_ptr->previous);
2036
2037#if DEBUG_MALLOC
2038 if (LOG(szone,ptr)) {
2039 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
2040 }
2041#endif
2042 if (!previous) {
2043 // The block to remove is the head of the free list
2044#if DEBUG_MALLOC
2045 if (szone->small_free_list[slot] != ptr) {
2046 szone_error(szone, "small_free_list_remove_ptr: Internal invariant broken (szone->small_free_list[grain])", ptr,
2047 "ptr=%p slot=%d msize=%d szone->small_free_list[slot]=%p\n",
2048 ptr, slot, msize, szone->small_free_list[slot]);
2049 return;
2050 }
2051#endif
2052 szone->small_free_list[slot] = next;
2053 if (!next) BITMAP32_CLR(szone->small_bitmap, slot);
2054 } else {
2055 // We know free_ptr is already checksummed, so we don't need to do it
2056 // again.
2057 previous->next = free_ptr->next;
2058 }
2059 if (next) {
2060 // We know free_ptr is already checksummed, so we don't need to do it
2061 // again.
2062 next->previous = free_ptr->previous;
2063 }
2064}
2065
2066static INLINE region_t *
2067small_region_for_ptr_no_lock(szone_t *szone, const void *ptr)
2068{
2069 return hash_lookup_region_no_lock(szone->small_regions,
2070 szone->num_small_regions_allocated,
2071 SMALL_REGION_FOR_PTR(ptr));
2072}
2073
2074static INLINE void
2075small_free_no_lock(szone_t *szone, region_t *region, void *ptr, msize_t msize)
2076{
2077 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
2078 unsigned index = SMALL_META_INDEX_FOR_PTR(ptr);
2079 size_t original_size = SMALL_BYTES_FOR_MSIZE(msize);
2080 unsigned char *next_block = ((unsigned char *)ptr + original_size);
2081 msize_t next_index = index + msize;
2082 msize_t previous_msize, next_msize;
2083 void *previous;
2084
2085 // Assumes locked
2086 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
2087#if DEBUG_MALLOC
2088 if (LOG(szone,ptr)) {
2089 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
2090 }
2091 if (! msize) {
2092 szone_error(szone, "trying to free small block that is too small", ptr,
2093 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
2094 }
2095#endif
2096 // We try to coalesce this block with the preceeding one
2097 if (index && (SMALL_PREVIOUS_MSIZE(ptr) <= index)) {
2098 previous_msize = SMALL_PREVIOUS_MSIZE(ptr);
2099 if (meta_headers[index - previous_msize] == (previous_msize | SMALL_IS_FREE)) {
2100 previous = ptr - SMALL_BYTES_FOR_MSIZE(previous_msize);
2101 // previous is really to be coalesced
2102#if DEBUG_MALLOC
2103 if (LOG(szone, ptr) || LOG(szone,previous)) {
2104 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr, previous);
2105 }
2106#endif
2107 small_free_list_remove_ptr(szone, previous, previous_msize);
2108 small_meta_header_set_middle(meta_headers, index);
2109 ptr = previous;
2110 msize += previous_msize;
2111 index -= previous_msize;
2112 }
2113 }
2114 // We try to coalesce with the next block
2115 if ((next_block < SMALL_REGION_END(*region)) && (meta_headers[next_index] & SMALL_IS_FREE)) {
2116 // next block is free, we coalesce
2117 next_msize = meta_headers[next_index] & ~ SMALL_IS_FREE;
2118#if DEBUG_MALLOC
2119 if (LOG(szone,ptr)) malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n", ptr, msize, next_block, next_msize);
2120#endif
2121 small_free_list_remove_ptr(szone, next_block, next_msize);
2122 small_meta_header_set_middle(meta_headers, next_index);
2123 msize += next_msize;
2124 }
2125 if (szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) {
2126 if (!msize) {
2127 szone_error(szone, "incorrect size information - block header was damaged", ptr, NULL);
2128 } else {
2129 memset(ptr, 0x55, SMALL_BYTES_FOR_MSIZE(msize));
2130 }
2131 }
2132 small_free_list_add_ptr(szone, ptr, msize);
2133 small_meta_header_set_is_free(meta_headers, index, msize);
2134 szone->num_small_objects--;
2135 szone->num_bytes_in_small_objects -= original_size; // we use original_size and not msize to avoid double counting the coalesced blocks
2136}
2137
2138static void *
2139small_malloc_from_region_no_lock(szone_t *szone, msize_t msize)
2140{
2141 void *last_block;
2142 void *ptr;
2143 void *new_address;
2144 msize_t *meta_headers;
2145 msize_t index ;
2146 msize_t msize_left;
2147
2148 // Allocates from the last region or a freshly allocated region
2149 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
2150 // Before anything we transform the small_bytes_free_at_end - if any - to a regular free block
2151 if (szone->small_bytes_free_at_end) {
2152 last_block = (void *)(SMALL_REGION_END(szone->last_small_region) - szone->small_bytes_free_at_end);
2153 small_free_list_add_ptr(szone, last_block, SMALL_MSIZE_FOR_BYTES(szone->small_bytes_free_at_end));
2154 *SMALL_METADATA_FOR_PTR(last_block) = SMALL_MSIZE_FOR_BYTES(szone->small_bytes_free_at_end) | SMALL_IS_FREE;
2155 szone->small_bytes_free_at_end = 0;
2156 }
2157 // time to create a new region
2158 new_address = allocate_pages(szone, SMALL_REGION_SIZE, SMALL_BLOCKS_ALIGN,
2159 0, VM_MEMORY_MALLOC_SMALL);
2160 if (!new_address)
2161 return NULL;
2162
2163 ptr = new_address;
2164 meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
2165 index = 0;
2166
2167 // Check to see if the hash ring of small regions needs to grow. Try to
2168 // avoid the hash ring becoming too dense.
2169 if (szone->num_small_regions_allocated < (2 * szone->num_small_regions)) {
2170 region_t *new_regions;
2171 size_t new_size;
2172 new_regions = hash_regions_grow_no_lock(szone, szone->small_regions,
2173 szone->num_small_regions_allocated,
2174 &new_size);
2175 // Do not deallocate the current small_regions allocation since someone
2176 // may be iterating it. Instead, just leak it.
2177 szone->small_regions = new_regions;
2178 szone->num_small_regions_allocated = new_size;
2179 }
2180 // Insert the new region into the hash ring, and update malloc statistics
2181 hash_region_insert_no_lock(szone->small_regions,
2182 szone->num_small_regions_allocated,
2183 new_address);
2184 szone->last_small_region = new_address;
2185
2186 // we bump the number of regions AFTER we have changes the regions pointer
2187 // to enable finding a small region without taking the lock
2188 //
2189 // FIXME: naive assumption assumes memory ordering coherence between this
2190 // and other CPUs. This also applies to the near-identical code in
2191 // tiny_malloc_from_region_no_lock.
2192 szone->num_small_regions++;
2193 small_meta_header_set_in_use(meta_headers, index, msize);
2194 msize_left = NUM_SMALL_BLOCKS - index;
2195 szone->num_small_objects++;
2196 szone->num_bytes_in_small_objects += SMALL_BYTES_FOR_MSIZE(msize);
2197 // add a big free block
2198 index += msize; msize_left -= msize;
2199 meta_headers[index] = msize_left;
2200 szone->small_bytes_free_at_end = SMALL_BYTES_FOR_MSIZE(msize_left);
2201 return ptr;
2202}
2203
2204static INLINE boolean_t
2205try_realloc_small_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size)
2206{
2207 // returns 1 on success
2208 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
2209 unsigned index = SMALL_META_INDEX_FOR_PTR(ptr);
2210 msize_t old_msize = SMALL_MSIZE_FOR_BYTES(old_size);
2211 msize_t new_msize = SMALL_MSIZE_FOR_BYTES(new_size + SMALL_QUANTUM - 1);
2212 void *next_block = (char *)ptr + old_size;
2213 unsigned next_index = index + old_msize;
2214 msize_t next_msize_and_free;
2215 msize_t next_msize;
2216 msize_t leftover_msize;
2217 void *leftover;
2218 unsigned leftover_index;
2219
2220 if (next_index >= NUM_SMALL_BLOCKS) {
2221 return 0;
2222 }
2223#if DEBUG_MALLOC
2224 if ((uintptr_t)next_block & (SMALL_QUANTUM - 1)) {
2225 szone_error(szone, "internal invariant broken in realloc(next_block)", next_block, NULL);
2226 }
2227 if (meta_headers[index] != old_msize)
2228 malloc_printf("*** try_realloc_small_in_place incorrect old %d %d\n",
2229 meta_headers[index], old_msize);
2230#endif
2231 SZONE_LOCK(szone);
2232 /*
2233 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2234 * it.
2235 */
2236 next_msize_and_free = meta_headers[next_index];
2237 next_msize = next_msize_and_free & ~ SMALL_IS_FREE;
2238 if (!(next_msize_and_free & SMALL_IS_FREE) || (old_msize + next_msize < new_msize)) {
2239 SZONE_UNLOCK(szone);
2240 return 0;
2241 }
2242 /*
2243 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2244 * our needs.
2245 */
2246 small_free_list_remove_ptr(szone, next_block, next_msize);
2247 small_meta_header_set_middle(meta_headers, next_index);
2248 leftover_msize = old_msize + next_msize - new_msize;
2249 if (leftover_msize) {
2250 /* there's some left, so put the remainder back */
2251 leftover = (unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(new_msize);
2252 small_free_list_add_ptr(szone, leftover, leftover_msize);
2253 leftover_index = index + new_msize;
2254 small_meta_header_set_is_free(meta_headers, leftover_index, leftover_msize);
2255 }
2256#if DEBUG_MALLOC
2257 if (SMALL_BYTES_FOR_MSIZE(new_msize) >= LARGE_THRESHOLD) {
2258 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize);
2259 }
2260#endif
2261 small_meta_header_set_in_use(meta_headers, index, new_msize);
2262#if DEBUG_MALLOC
2263 if (LOG(szone,ptr)) {
2264 malloc_printf("in szone_realloc(), ptr=%p, msize=%d\n", ptr, *SMALL_METADATA_FOR_PTR(ptr));
2265 }
2266#endif
2267 szone->num_bytes_in_small_objects += SMALL_BYTES_FOR_MSIZE(new_msize - old_msize);
2268 SZONE_UNLOCK(szone);
2269 CHECK(szone, __PRETTY_FUNCTION__);
2270 return 1;
2271}
2272
2273static boolean_t
2274szone_check_small_region(szone_t *szone, region_t region)
2275{
2276 unsigned char *ptr = SMALL_REGION_ADDRESS(region);
2277 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
2278 unsigned char *region_end = SMALL_REGION_END(region);
2279 msize_t prev_free = 0;
2280 unsigned index;
2281 msize_t msize_and_free;
2282 msize_t msize;
2283 free_list_t *free_head;
2284 void *previous, *next;
2285 msize_t *follower;
2286
2287 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
2288 if (region == szone->last_small_region) region_end -= szone->small_bytes_free_at_end;
2289 while (ptr < region_end) {
2290 index = SMALL_META_INDEX_FOR_PTR(ptr);
2291 msize_and_free = meta_headers[index];
2292 if (!(msize_and_free & SMALL_IS_FREE)) {
2293 // block is in use
2294 msize = msize_and_free;
2295 if (!msize) {
2296 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
2297 ptr, szone->num_small_regions, region_end);
2298 return 0;
2299 }
2300 if (msize > (LARGE_THRESHOLD / SMALL_QUANTUM)) {
2301 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
2302 ptr, msize_and_free);
2303 return 0;
2304 }
2305 ptr += SMALL_BYTES_FOR_MSIZE(msize);
2306 prev_free = 0;
2307 } else {
2308 // free pointer
2309 msize = msize_and_free & ~ SMALL_IS_FREE;
2310 free_head = (free_list_t *)ptr;
2311 follower = (msize_t *)FOLLOWING_SMALL_PTR(ptr, msize);
2312 if (!msize) {
2313 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr, msize);
2314 return 0;
2315 }
2316 if (prev_free) {
2317 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr);
2318 return 0;
2319 }
2320 free_list_checksum(szone, free_head, __PRETTY_FUNCTION__);
2321 previous = free_list_unchecksum_ptr(free_head->previous);
2322 next = free_list_unchecksum_ptr(free_head->next);
2323 if (previous && !SMALL_PTR_IS_FREE(previous)) {
2324 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2325 ptr, free_head->previous);
2326 return 0;
2327 }
2328 if (next && !SMALL_PTR_IS_FREE(next)) {
2329 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr);
2330 return 0;
2331 }
2332 if (SMALL_PREVIOUS_MSIZE(follower) != msize) {
2333 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
2334 "(end marker incorrect) should be %d; in fact %d\n",
2335 ptr, follower, SMALL_REGION_ADDRESS(region), region_end, msize, SMALL_PREVIOUS_MSIZE(follower));
2336 return 0;
2337 }
2338 ptr = (unsigned char *)follower;
2339 prev_free = SMALL_IS_FREE;
2340 }
2341 }
2342 return 1;
2343}
2344
2345static kern_return_t
2346small_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone, memory_reader_t reader, vm_range_recorder_t recorder)
2347{
2348 size_t num_regions = szone->num_small_regions_allocated;
2349 void *last_small_free = szone->last_small_free;
2350 size_t index;
2351 region_t *regions;
2352 vm_range_t buffer[MAX_RECORDER_BUFFER];
2353 unsigned count = 0;
2354 kern_return_t err;
2355 region_t region;
2356 vm_range_t range;
2357 vm_range_t admin_range;
2358 vm_range_t ptr_range;
2359 unsigned char *mapped_region;
2360 msize_t *block_header;
2361 unsigned block_index;
2362 unsigned block_limit;
2363 msize_t msize_and_free;
2364 msize_t msize;
2365 vm_address_t last_small_free_ptr = 0;
2366 msize_t last_small_free_msize = 0;
2367
2368 if (last_small_free) {
2369 last_small_free_ptr = (uintptr_t)last_small_free & ~(SMALL_QUANTUM - 1);
2370 last_small_free_msize = (uintptr_t)last_small_free & (SMALL_QUANTUM - 1);
2371 }
2372
2373 err = reader(task, (vm_address_t)szone->small_regions, sizeof(region_t) * num_regions, (void **)&regions);
2374 if (err) return err;
2375 for (index = 0; index < num_regions; ++index) {
2376 region = regions[index];
2377 if (region) {
2378 range.address = (vm_address_t)SMALL_REGION_ADDRESS(region);
2379 range.size = SMALL_REGION_SIZE;
2380 if (type_mask & MALLOC_ADMIN_REGION_RANGE_TYPE) {
2381 admin_range.address = range.address + SMALL_HEADER_START;
2382 admin_range.size = SMALL_ARRAY_SIZE;
2383 recorder(task, context, MALLOC_ADMIN_REGION_RANGE_TYPE, &admin_range, 1);
2384 }
2385 if (type_mask & (MALLOC_PTR_REGION_RANGE_TYPE | MALLOC_ADMIN_REGION_RANGE_TYPE)) {
2386 ptr_range.address = range.address;
2387 ptr_range.size = NUM_SMALL_BLOCKS * SMALL_QUANTUM;
2388 recorder(task, context, MALLOC_PTR_REGION_RANGE_TYPE, &ptr_range, 1);
2389 }
2390 if (type_mask & MALLOC_PTR_IN_USE_RANGE_TYPE) {
2391 err = reader(task, range.address, range.size, (void **)&mapped_region);
2392 if (err) return err;
2393 block_header = (msize_t *)(mapped_region + SMALL_HEADER_START);
2394 block_index = 0;
2395 block_limit = NUM_SMALL_BLOCKS;
2396 if (region == szone->last_small_region)
2397 block_limit -= SMALL_MSIZE_FOR_BYTES(szone->small_bytes_free_at_end);
2398 while (block_index < block_limit) {
2399 msize_and_free = block_header[block_index];
2400 msize = msize_and_free & ~ SMALL_IS_FREE;
2401 if (! (msize_and_free & SMALL_IS_FREE) &&
2402 range.address + SMALL_BYTES_FOR_MSIZE(block_index) != last_small_free_ptr) {
2403 // Block in use
2404 buffer[count].address = range.address + SMALL_BYTES_FOR_MSIZE(block_index);
2405 buffer[count].size = SMALL_BYTES_FOR_MSIZE(msize);
2406 count++;
2407 if (count >= MAX_RECORDER_BUFFER) {
2408 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
2409 count = 0;
2410 }
2411 }
2412 block_index += msize;
2413 }
2414 }
2415 }
2416 }
2417 if (count) {
2418 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
2419 }
2420 return 0;
2421}
2422
2423static void *
2424small_malloc_from_free_list(szone_t *szone, msize_t msize)
2425{
2426 free_list_t *ptr;
2427 msize_t this_msize;
2428 grain_t slot = (msize <= NUM_SMALL_SLOTS) ? msize - 1 : NUM_SMALL_SLOTS - 1;
2429 free_list_t **free_list = szone->small_free_list;
2430 free_list_t *next;
2431 free_list_t **limit;
2432 unsigned bitmap = szone->small_bitmap & ~ ((1 << slot) - 1);
2433 msize_t leftover_msize;
2434 free_list_t *leftover_ptr;
2435 msize_t *meta_headers;
2436 unsigned leftover_index;
2437
2438 // Assumes locked
2439 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
2440
2441 // Mask off the bits representing slots holding free blocks smaller than the
2442 // size we need. If there are no larger free blocks, try allocating from
2443 // the free space at the end of the tiny region.
2444 if (!bitmap)
2445 goto try_small_from_end;
2446
2447 slot = BITMAP32_CTZ(bitmap);
2448 limit = free_list + NUM_SMALL_SLOTS - 1;
2449 free_list += slot;
2450
2451 // Iterate over freelists looking for free blocks, starting at first list
2452 // which is not empty, and contains blocks which are large enough to satisfy
2453 // our request.
2454 while (free_list < limit) {
2455 ptr = *free_list;
2456 if (ptr) {
2457 next = free_list_unchecksum_ptr(ptr->next);
2458 *free_list = next;
2459 this_msize = SMALL_PTR_SIZE(ptr);
2460 if (next) {
2461 next->previous = ptr->previous;
2462 } else {
2463 BITMAP32_CLR(szone->small_bitmap, this_msize - 1);
2464 }
2465 goto add_leftover_and_proceed;
2466 }
2467 free_list++;
2468 }
2469
2470 // We are now looking at the last slot, which contains blocks equal to, or
2471 // due to coalescing of free blocks, larger than 31 * small quantum size.
2472 // If the last freelist is not empty, and the head contains a block that is
2473 // larger than our request, then the remainder is put back on the free list.
2474 //
2475 // FIXME: This code doesn't have the optimization from the 'tiny' codepath
2476 // that optimizes for the this_msize >= 2 * num slots
2477 // FIXME: this code also seems somewhat bogus. There's a check for
2478 // this_msize >= msize, but by definition we can't ask for a small
2479 // block larger than 31 small quanta, and every free block in this
2480 // slot has to be at least that large.
2481 ptr = *limit;
2482 while (ptr) {
2483 free_list_checksum(szone, ptr, __PRETTY_FUNCTION__);
2484 next = free_list_unchecksum_ptr(ptr->next);
2485 this_msize = SMALL_PTR_SIZE(ptr);
2486 if (this_msize >= msize) {
2487 small_free_list_remove_ptr(szone, ptr, this_msize);
2488 goto add_leftover_and_proceed;
2489 }
2490 ptr = next;
2491 }
2492
2493try_small_from_end:
2494 // Let's see if we can use szone->small_bytes_free_at_end
2495 if (szone->small_bytes_free_at_end >= SMALL_BYTES_FOR_MSIZE(msize)) {
2496 ptr = (free_list_t *)(SMALL_REGION_END(szone->last_small_region) - szone->small_bytes_free_at_end);
2497 szone->small_bytes_free_at_end -= SMALL_BYTES_FOR_MSIZE(msize);
2498 if (szone->small_bytes_free_at_end) {
2499 // let's mark this block as in use to serve as boundary
2500 *SMALL_METADATA_FOR_PTR((unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(msize)) = SMALL_MSIZE_FOR_BYTES(szone->small_bytes_free_at_end);
2501 }
2502 this_msize = msize;
2503 goto return_small_alloc;
2504 }
2505 return NULL;
2506
2507add_leftover_and_proceed:
2508 if (this_msize > msize) {
2509 leftover_msize = this_msize - msize;
2510 leftover_ptr = (free_list_t *)((unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(msize));
2511#if DEBUG_MALLOC
2512 if (LOG(szone,ptr)) {
2513 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr, this_msize);
2514 }
2515#endif
2516 small_free_list_add_ptr(szone, leftover_ptr, leftover_msize);
2517 meta_headers = SMALL_META_HEADER_FOR_PTR(leftover_ptr);
2518 leftover_index = SMALL_META_INDEX_FOR_PTR(leftover_ptr);
2519 small_meta_header_set_is_free(meta_headers, leftover_index, leftover_msize);
2520 this_msize = msize;
2521 }
2522
2523return_small_alloc:
2524 szone->num_small_objects++;
2525 szone->num_bytes_in_small_objects += SMALL_BYTES_FOR_MSIZE(this_msize);
2526#if DEBUG_MALLOC
2527 if (LOG(szone,ptr)) {
2528 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr, this_msize, msize);
2529 }
2530#endif
2531 *SMALL_METADATA_FOR_PTR(ptr) = this_msize;
2532 return ptr;
2533}
2534
2535static INLINE void *
2536small_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested)
2537{
2538 boolean_t locked = 0;
2539#if SMALL_CACHE
2540 void *ptr;
2541#endif
2542
2543#if SMALL_CACHE
2544 ptr = (void *)szone->last_small_free;
2545 if ((((uintptr_t)ptr) & (SMALL_QUANTUM - 1)) == msize) {
2546 // we have a candidate - let's lock to make sure
2547 LOCK_AND_NOTE_LOCKED(szone, locked);
2548 if (ptr == (void *)szone->last_small_free) {
2549 szone->last_small_free = NULL;
2550 SZONE_UNLOCK(szone);
2551 CHECK(szone, __PRETTY_FUNCTION__);
2552 ptr = (void *)((uintptr_t)ptr & ~ (SMALL_QUANTUM - 1));
2553 if (cleared_requested) {
2554 memset(ptr, 0, SMALL_BYTES_FOR_MSIZE(msize));
2555 }
2556 return ptr;
2557 }
2558 }
2559#endif
2560 // Except in rare occasions where we need to add a new region, we are going to end up locking,
2561 // so we might as well lock right away to avoid doing unnecessary optimistic probes
2562 if (!locked) LOCK_AND_NOTE_LOCKED(szone, locked);
2563 ptr = small_malloc_from_free_list(szone, msize);
2564 if (ptr) {
2565 SZONE_UNLOCK(szone);
2566 CHECK(szone, __PRETTY_FUNCTION__);
2567 if (cleared_requested) {
2568 memset(ptr, 0, SMALL_BYTES_FOR_MSIZE(msize));
2569 }
2570 return ptr;
2571 }
2572 ptr = small_malloc_from_region_no_lock(szone, msize);
2573 // we don't clear because this freshly allocated space is pristine
2574 SZONE_UNLOCK(szone);
2575 CHECK(szone, __PRETTY_FUNCTION__);
2576 return ptr;
2577}
2578
2579// tries to allocate a small, cleared block
2580static INLINE void *
2581small_malloc_cleared_no_lock(szone_t *szone, msize_t msize)
2582{
2583 void *ptr;
2584
2585 // Assumes already locked
2586 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
2587 ptr = small_malloc_from_free_list(szone, msize);
2588 if (ptr) {
2589 memset(ptr, 0, SMALL_BYTES_FOR_MSIZE(msize));
2590 return ptr;
2591 } else {
2592 ptr = small_malloc_from_region_no_lock(szone, msize);
2593 // we don't clear because this freshly allocated space is pristine
2594 }
2595 return ptr;
2596}
2597
2598static INLINE void
2599free_small(szone_t *szone, void *ptr, region_t *small_region)
2600{
2601 msize_t msize = SMALL_PTR_SIZE(ptr);
2602#if SMALL_CACHE
2603 void *ptr2;
2604#endif
2605
2606 // ptr is known to be in small_region
2607 SZONE_LOCK(szone);
2608#if SMALL_CACHE
2609 ptr2 = szone->last_small_free;
2610 /* check that we don't already have this pointer in the cache */
2611 if (ptr == (void *)((uintptr_t)ptr2 & ~ (SMALL_QUANTUM - 1))) {
2612 szone_error(szone, "double free", ptr, NULL);
2613 return;
2614 }
2615#endif
2616 if (SMALL_PTR_IS_FREE(ptr)) {
2617 szone_error(szone, "double free", ptr, NULL);
2618 return;
2619 }
2620#if SMALL_CACHE
2621 szone->last_small_free = (void *)(((uintptr_t)ptr) | msize);
2622 if (!ptr2) {
2623 SZONE_UNLOCK(szone);
2624 CHECK(szone, __PRETTY_FUNCTION__);
2625 return;
2626 }
2627 msize = (uintptr_t)ptr2 & (SMALL_QUANTUM - 1);
2628 ptr = (void *)(((uintptr_t)ptr2) & ~ (SMALL_QUANTUM - 1));
2629 small_region = small_region_for_ptr_no_lock(szone, ptr);
2630 if (!small_region) {
2631 szone_error(szone, "double free (small cache)", ptr, NULL);
2632 return;
2633 }
2634#endif
2635 small_free_no_lock(szone, small_region, ptr, msize);
2636 SZONE_UNLOCK(szone);
2637 CHECK(szone, __PRETTY_FUNCTION__);
2638}
2639
2640static void
2641print_small_free_list(szone_t *szone)
2642{
2643 grain_t grain = 0;
2644 free_list_t *ptr;
2645 _SIMPLE_STRING b = _simple_salloc();
2646
2647 if (b) {
2648 _simple_sappend(b, "small free sizes: ");
2649 while (grain < NUM_SMALL_SLOTS) {
2650 ptr = szone->small_free_list[grain];
2651 if (ptr) {
2652 _simple_sprintf(b, "%s%y[%d]; ", (grain == NUM_SMALL_SLOTS-1) ? ">=" : "", (grain + 1) * SMALL_QUANTUM, free_list_count(ptr));
2653 }
2654 grain++;
2655 }
2656 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
2657 _simple_sfree(b);
2658 }
2659}
2660
2661static void
2662print_small_region(szone_t *szone, boolean_t verbose, region_t region, size_t bytes_at_end)
2663{
2664 unsigned counts[1024];
2665 unsigned in_use = 0;
2666 void *start = SMALL_REGION_ADDRESS(region);
2667 void *limit = SMALL_REGION_END(region) - bytes_at_end;
2668 msize_t msize_and_free;
2669 msize_t msize;
2670 unsigned ci;
2671 _SIMPLE_STRING b;
2672
2673 memset(counts, 0, 1024 * sizeof(unsigned));
2674 while (start < limit) {
2675 msize_and_free = *SMALL_METADATA_FOR_PTR(start);
2676 msize = msize_and_free & ~ SMALL_IS_FREE;
2677 if (!(msize_and_free & SMALL_IS_FREE)) {
2678 // block in use
2679 if (msize < 1024)
2680 counts[msize]++;
2681 in_use++;
2682 }
2683 start += SMALL_BYTES_FOR_MSIZE(msize);
2684 }
2685 if ((b = _simple_salloc()) != NULL) {
2686 _simple_sprintf(b, "Small region [%p-%p, %y]\tIn_use=%d ",
2687 SMALL_REGION_ADDRESS(region), SMALL_REGION_END(region), (int)SMALL_REGION_SIZE, in_use);
2688 if (bytes_at_end)
2689 _simple_sprintf(b, "Untouched=%ly", bytes_at_end);
2690 if (verbose && in_use) {
2691 _simple_sappend(b, "\n\tSizes in use: ");
2692 for (ci = 0; ci < 1024; ci++)
2693 if (counts[ci])
2694 _simple_sprintf(b, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci), counts[ci]);
2695 }
2696 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
2697 _simple_sfree(b);
2698 }
2699}
2700
2701static boolean_t
2702small_free_list_check(szone_t *szone, grain_t grain)
2703{
2704 unsigned count = 0;
2705 free_list_t *ptr = szone->small_free_list[grain];
2706 free_list_t *previous = NULL;
2707 msize_t msize_and_free;
2708
2709 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
2710 while (ptr) {
2711 msize_and_free = *SMALL_METADATA_FOR_PTR(ptr);
2712 count++;
2713 if (!(msize_and_free & SMALL_IS_FREE)) {
2714 malloc_printf("*** in-use ptr in free list grain=%d count=%d ptr=%p\n", grain, count, ptr);
2715 return 0;
2716 }
2717 if (((uintptr_t)ptr) & (SMALL_QUANTUM - 1)) {
2718 malloc_printf("*** unaligned ptr in free list grain=%d count=%d ptr=%p\n", grain, count, ptr);
2719 return 0;
2720 }
2721 if (!small_region_for_ptr_no_lock(szone, ptr)) {
2722 malloc_printf("*** ptr not in szone grain=%d count=%d ptr=%p\n", grain, count, ptr);
2723 return 0;
2724 }
2725 free_list_checksum(szone, ptr, __PRETTY_FUNCTION__);
2726 if (free_list_unchecksum_ptr(ptr->previous) != previous) {
2727 malloc_printf("*** previous incorrectly set grain=%d count=%d ptr=%p\n", grain, count, ptr);
2728 return 0;
2729 }
2730 previous = ptr;
2731 ptr = free_list_unchecksum_ptr(ptr->next);
2732 }
2733 return 1;
2734}
2735
2736/*******************************************************************************
2737 * Region hash implementation
2738 *
2739 * This is essentially a duplicate of the existing Large allocator hash, minus
2740 * the ability to remove entries. The two should be combined eventually.
2741 ******************************************************************************/
2742#pragma mark region hash
2743
2744/*
2745 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
2746 * given region.
2747 *
2748 * FIXME: If consecutive queries of the same region are likely, a one-entry
2749 * cache would likely be a significant performance win here.
2750 */
2751static region_t *
2752hash_lookup_region_no_lock(region_t *regions, size_t num_entries, region_t r) {
2753 size_t index, hash_index;
2754 region_t *entry;
2755
2756 if (!num_entries)
2757 return 0;
2758
2759 index = hash_index = ((uintptr_t)r >> 20) % num_entries;
2760 do {
2761 entry = regions + index;
2762 if (*entry == 0)
2763 return 0;
2764 if (*entry == r)
2765 return entry;
2766 if (++index == num_entries)
2767 index = 0;
2768 } while (index != hash_index);
2769 return 0;
2770}
2771
2772/*
2773 * hash_region_insert_no_lock - Insert a region into the hash ring.
2774 */
2775static void
2776hash_region_insert_no_lock(region_t *regions, size_t num_entries, region_t r) {
2777 size_t index, hash_index;
2778 region_t *entry;
2779
2780 index = hash_index = ((uintptr_t)r >> 20) % num_entries;
2781 do {
2782 entry = regions + index;
2783 if (*entry == 0) {
2784 *entry = r;
2785 return;
2786 }
2787 if (++index == num_entries)
2788 index = 0;
2789 } while (index != hash_index);
2790}
2791
2792/*
2793 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
2794 * must be a VM allocation as to avoid recursing between allocating a new small
2795 * region, and asking the small region to allocate space for the new list of
2796 * regions.
2797 */
2798static region_t *
2799hash_regions_alloc_no_lock(szone_t *szone, size_t num_entries)
2800{
2801 size_t size = num_entries * sizeof(region_t);
2802 return allocate_pages(szone, round_page(size), 0, 0, VM_MEMORY_MALLOC);
2803}
2804
2805/*
2806 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
2807 * Return the new region and new size to update the szone. Do not deallocate
2808 * the old entries since someone may still be allocating them.
2809 */
2810static region_t *
2811hash_regions_grow_no_lock(szone_t *szone, region_t *regions, size_t old_size,
2812 size_t *new_size)
2813{
2814 // double in size and allocate memory for the regions
2815 *new_size = old_size * 2 + 1;
2816 region_t *new_regions = hash_regions_alloc_no_lock(szone, *new_size);
2817
2818 // rehash the entries into the new list
2819 size_t index;
2820 for (index = 0; index < old_size; ++index) {
2821 region_t r = regions[index];
2822 if (r != 0)
2823 hash_region_insert_no_lock(new_regions, *new_size, r);
2824 }
2825 return new_regions;
2826}
2827
2828/*******************************************************************************
2829 * Large allocator implementation
2830 ******************************************************************************/
2831#pragma mark large allocator
2832
2833#if DEBUG_MALLOC
2834
2835static void
2836large_debug_print(szone_t *szone)
2837{
2838 unsigned num_large_entries = szone->num_large_entries;
2839 unsigned index = num_large_entries;
2840 large_entry_t *range;
2841 _SIMPLE_STRING b = _simple_salloc();
2842
2843 if (b) {
2844 for (index = 0, range = szone->large_entries; index < szone->num_large_entries; index++, range++)
2845 if (!LARGE_ENTRY_IS_EMPTY(*range))
2846 _simple_sprintf(b, "%d: %p(%y); ", index, LARGE_ENTRY_ADDRESS(*range), LARGE_ENTRY_SIZE(*range));
2847
2848 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
2849 _simple_sfree(b);
2850 }
2851}
2852#endif
2853
2854/*
2855 * Scan the hash ring looking for an entry for the given pointer.
2856 */
2857static large_entry_t *
2858large_entry_for_pointer_no_lock(szone_t *szone, const void *ptr)
2859{
2860 // result only valid with lock held
2861 unsigned num_large_entries = szone->num_large_entries;
2862 unsigned hash_index;
2863 unsigned index;
2864 large_entry_t *range;
2865
2866 if (!num_large_entries)
2867 return NULL;
2868 hash_index = ((uintptr_t)ptr >> vm_page_shift) % num_large_entries;
2869 index = hash_index;
2870 do {
2871 range = szone->large_entries + index;
2872 if (LARGE_ENTRY_MATCHES(*range, ptr))
2873 return range;
2874 if (LARGE_ENTRY_IS_EMPTY(*range))
2875 return NULL; // end of chain
2876 index++;
2877 if (index == num_large_entries)
2878 index = 0;
2879 } while (index != hash_index);
2880 return NULL;
2881}
2882
2883static void
2884large_entry_insert_no_lock(szone_t *szone, large_entry_t range)
2885{
2886 unsigned num_large_entries = szone->num_large_entries;
2887 unsigned hash_index = (range.address_and_num_pages >> vm_page_shift) % num_large_entries;
2888 unsigned index = hash_index;
2889 large_entry_t *entry;
2890
2891 do {
2892 entry = szone->large_entries + index;
2893 if (LARGE_ENTRY_IS_EMPTY(*entry)) {
2894 *entry = range;
2895 return; // end of chain
2896 }
2897 index++;
2898 if (index == num_large_entries)
2899 index = 0;
2900 } while (index != hash_index);
2901}
2902
2903static INLINE void
2904large_entries_rehash_after_entry_no_lock(szone_t *szone, large_entry_t *entry)
2905{
2906 unsigned num_large_entries = szone->num_large_entries;
2907 unsigned hash_index = entry - szone->large_entries;
2908 unsigned index = hash_index;
2909 large_entry_t range;
2910
2911 do {
2912 index++;
2913 if (index == num_large_entries)
2914 index = 0;
2915 range = szone->large_entries[index];
2916 if (LARGE_ENTRY_IS_EMPTY(range))
2917 return;
2918 szone->large_entries[index].address_and_num_pages = 0;
2919 large_entry_insert_no_lock(szone, range); // this will reinsert in the
2920 // proper place
2921 } while (index != hash_index);
2922}
2923
2924static INLINE large_entry_t *
2925large_entries_alloc_no_lock(szone_t *szone, unsigned num)
2926{
2927 size_t size = num * sizeof(large_entry_t);
2928 boolean_t is_vm_allocation = size >= LARGE_THRESHOLD;
2929
2930 if (is_vm_allocation) {
2931 // Note that we allocate memory (via a system call) under a spin lock
2932 // That is certainly evil, however it's very rare in the lifetime of a process
2933 // The alternative would slow down the normal case
2934 return allocate_pages(szone, round_page(size), 0, 0, VM_MEMORY_MALLOC_LARGE);
2935 } else {
2936 return small_malloc_cleared_no_lock(szone, SMALL_MSIZE_FOR_BYTES(size + SMALL_QUANTUM - 1));
2937 }
2938}
2939
2940static void
2941large_entries_free_no_lock(szone_t *szone, large_entry_t *entries, unsigned num, vm_range_t *range_to_deallocate)
2942{
2943 // returns range to deallocate
2944 size_t size = num * sizeof(large_entry_t);
2945 boolean_t is_vm_allocation = size >= LARGE_THRESHOLD;
2946 region_t *region;
2947 msize_t msize_and_free;
2948
2949 if (is_vm_allocation) {
2950 range_to_deallocate->address = (vm_address_t)entries;
2951 range_to_deallocate->size = round_page(size);
2952 } else {
2953 range_to_deallocate->size = 0;
2954 region = small_region_for_ptr_no_lock(szone, entries);
2955 msize_and_free = *SMALL_METADATA_FOR_PTR(entries);
2956 if (msize_and_free & SMALL_IS_FREE) {
2957 szone_error(szone, "object already freed being freed", entries, NULL);
2958 return;
2959 }
2960 small_free_no_lock(szone, region, entries, msize_and_free);
2961 }
2962}
2963
2964static large_entry_t *
2965large_entries_grow_no_lock(szone_t *szone, vm_range_t *range_to_deallocate)
2966{
2967 // sets range_to_deallocate
2968 unsigned old_num_entries = szone->num_large_entries;
2969 large_entry_t *old_entries = szone->large_entries;
2970 unsigned new_num_entries = (old_num_entries) ? old_num_entries * 2 + 1 : 63; // always an odd number for good hashing
2971 large_entry_t *new_entries = large_entries_alloc_no_lock(szone, new_num_entries);
2972 unsigned index = old_num_entries;
2973 large_entry_t oldRange;
2974
2975 // if the allocation of new entries failed, bail
2976 if (new_entries == NULL)
2977 return NULL;
2978
2979 szone->num_large_entries = new_num_entries;
2980 szone->large_entries = new_entries;
2981
2982 /* rehash entries into the new list */
2983 while (index--) {
2984 oldRange = old_entries[index];
2985 if (!LARGE_ENTRY_IS_EMPTY(oldRange)) {
2986 large_entry_insert_no_lock(szone, oldRange);
2987 }
2988 }
2989 if (old_entries) {
2990 large_entries_free_no_lock(szone, old_entries, old_num_entries, range_to_deallocate);
2991 } else {
2992 range_to_deallocate->size = 0;
2993 }
2994 return new_entries;
2995}
2996
2997// frees the specific entry in the size table
2998// returns a range to truly deallocate
2999static vm_range_t
3000large_free_no_lock(szone_t *szone, large_entry_t *entry)
3001{
3002 vm_range_t range;
3003
3004 range.address = (vm_address_t)LARGE_ENTRY_ADDRESS(*entry);
3005 range.size = (vm_size_t)LARGE_ENTRY_SIZE(*entry);
3006 szone->num_large_objects_in_use--;
3007 szone->num_bytes_in_large_objects -= range.size;
3008 if (szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) {
3009 protect((void *)range.address, range.size, VM_PROT_READ | VM_PROT_WRITE, szone->debug_flags);
3010 range.address -= vm_page_size;
3011 range.size += 2 * vm_page_size;
3012 }
3013 entry->address_and_num_pages = 0;
3014 large_entries_rehash_after_entry_no_lock(szone, entry);
3015#if DEBUG_MALLOC
3016 if (large_entry_for_pointer_no_lock(szone, (void *)range.address)) {
3017 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
3018 range.address, szone->num_large_entries);
3019 large_debug_print(szone);
3020 szone_sleep();
3021 }
3022#endif
3023 return range;
3024}
3025
3026static kern_return_t
3027large_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t large_entries_address, unsigned num_entries, memory_reader_t reader, vm_range_recorder_t recorder)
3028{
3029 unsigned index = 0;
3030 vm_range_t buffer[MAX_RECORDER_BUFFER];
3031 unsigned count = 0;
3032 large_entry_t *entries;
3033 kern_return_t err;
3034 vm_range_t range;
3035 large_entry_t entry;
3036
3037 err = reader(task, large_entries_address, sizeof(large_entry_t) * num_entries, (void **)&entries);
3038 if (err)
3039 return err;
3040 index = num_entries;
3041 if ((type_mask & MALLOC_ADMIN_REGION_RANGE_TYPE) &&
3042 (num_entries * sizeof(large_entry_t) >= LARGE_THRESHOLD)) {
3043 range.address = large_entries_address;
3044 range.size = round_page(num_entries * sizeof(large_entry_t));
3045 recorder(task, context, MALLOC_ADMIN_REGION_RANGE_TYPE, &range, 1);
3046 }
3047 if (type_mask & (MALLOC_PTR_IN_USE_RANGE_TYPE | MALLOC_PTR_REGION_RANGE_TYPE))
3048 while (index--) {
3049 entry = entries[index];
3050 if (!LARGE_ENTRY_IS_EMPTY(entry)) {
3051 range.address = (vm_address_t)LARGE_ENTRY_ADDRESS(entry);
3052 range.size = (vm_size_t)LARGE_ENTRY_SIZE(entry);
3053 buffer[count++] = range;
3054 if (count >= MAX_RECORDER_BUFFER) {
3055 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE | MALLOC_PTR_REGION_RANGE_TYPE, buffer, count);
3056 count = 0;
3057 }
3058 }
3059 }
3060 if (count) {
3061 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE
3062 | MALLOC_PTR_REGION_RANGE_TYPE, buffer, count);
3063 }
3064 return 0;
3065}
3066
3067/********************* HUGE ENTRY UTILITIES ************************/
3068
3069static huge_entry_t *
3070huge_entry_for_pointer_no_lock(szone_t *szone, const void *ptr)
3071{
3072 unsigned index;
3073 huge_entry_t *huge;
3074
3075 for (index = szone->num_huge_entries, huge = szone->huge_entries;
3076 index > 0;
3077 index--, huge++) {
3078
3079 if ((void *)huge->address == ptr)
3080 return huge;
3081 }
3082 return NULL;
3083}
3084
3085static boolean_t
3086huge_entry_append(szone_t *szone, huge_entry_t huge)
3087{
3088 huge_entry_t *new_huge_entries = NULL, *old_huge_entries;
3089 unsigned num_huge_entries;
3090
3091 // We do a little dance with locking because doing allocation (even in the
3092 // default szone) may cause something to get freed in this szone, with a
3093 // deadlock
3094 // Returns 1 on success
3095 SZONE_LOCK(szone);
3096 for (;;) {
3097 num_huge_entries = szone->num_huge_entries;
3098 SZONE_UNLOCK(szone);
3099 /* check for counter wrap */
3100 if ((num_huge_entries + 1) < num_huge_entries)
3101 return 0;
3102 /* stale allocation from last time around the loop? */
3103 if (new_huge_entries)
3104 szone_free(szone, new_huge_entries);
3105 new_huge_entries = szone_malloc(szone, (num_huge_entries + 1) * sizeof(huge_entry_t));
3106 if (new_huge_entries == NULL)
3107 return 0;
3108 SZONE_LOCK(szone);
3109 if (num_huge_entries == szone->num_huge_entries) {
3110 // No change - our malloc still applies
3111 old_huge_entries = szone->huge_entries;
3112 if (num_huge_entries) {
3113 memcpy(new_huge_entries, old_huge_entries, num_huge_entries * sizeof(huge_entry_t));
3114 }
3115 new_huge_entries[szone->num_huge_entries++] = huge;
3116 szone->huge_entries = new_huge_entries;
3117 SZONE_UNLOCK(szone);
3118 szone_free(szone, old_huge_entries);
3119 return 1;
3120 }
3121 // try again!
3122 }
3123}
3124
3125static kern_return_t
3126huge_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t huge_entries_address, unsigned num_entries, memory_reader_t reader, vm_range_recorder_t recorder)
3127{
3128 huge_entry_t *entries;
3129 kern_return_t err;
3130
3131 err = reader(task, huge_entries_address, sizeof(huge_entry_t) * num_entries, (void **)&entries);
3132 if (err)
3133 return err;
3134 if (num_entries)
3135 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE | MALLOC_PTR_REGION_RANGE_TYPE, entries, num_entries);
3136
3137 return 0;
3138}
3139
3140static void *
3141large_and_huge_malloc(szone_t *szone, size_t num_pages)
3142{
3143 void *addr;
3144 vm_range_t range_to_deallocate;
3145 huge_entry_t huge_entry;
3146 size_t size;
3147 large_entry_t large_entry;
3148
3149 if (!num_pages)
3150 num_pages = 1; // minimal allocation size for this szone
3151 size = (size_t)num_pages << vm_page_shift;
3152 range_to_deallocate.size = 0;
3153 if (num_pages >= (1 << vm_page_shift)) {
3154 addr = allocate_pages(szone, size, 0, szone->debug_flags, VM_MEMORY_MALLOC_HUGE);
3155 if (addr == NULL)
3156 return NULL;
3157 huge_entry.size = size;
3158 huge_entry.address = (vm_address_t)addr;
3159 if (!huge_entry_append(szone, huge_entry))
3160 return NULL; // we are leaking the allocation here
3161 SZONE_LOCK(szone);
3162 szone->num_bytes_in_huge_objects += size;
3163 } else {
3164
3165 addr = allocate_pages(szone, size, 0, szone->debug_flags, VM_MEMORY_MALLOC_LARGE);
3166#if DEBUG_MALLOC
3167 if (LOG(szone, addr))
3168 malloc_printf("in szone_malloc true large allocation at %p for %ly\n", (void *)addr, size);
3169#endif
3170 SZONE_LOCK(szone);
3171 if (addr == NULL) {
3172 SZONE_UNLOCK(szone);
3173 return NULL;
3174 }
3175#if DEBUG_MALLOC
3176 if (large_entry_for_pointer_no_lock(szone, addr)) {
3177 malloc_printf("freshly allocated is already in use: %p\n", addr);
3178 large_debug_print(szone);
3179 szone_sleep();
3180 }
3181#endif
3182 if ((szone->num_large_objects_in_use + 1) * 4 > szone->num_large_entries) {
3183 // density of hash table too high; grow table
3184 // we do that under lock to avoid a race
3185 large_entry_t *entries = large_entries_grow_no_lock(szone, &range_to_deallocate);
3186 if (entries == NULL) {
3187 SZONE_UNLOCK(szone);
3188 return NULL;
3189 }
3190 }
3191 large_entry.address_and_num_pages = (uintptr_t)addr | num_pages;
3192#if DEBUG_MALLOC
3193 if (large_entry_for_pointer_no_lock(szone, addr)) {
3194 malloc_printf("entry about to be added already in use: %p\n", addr);
3195 large_debug_print(szone);
3196 szone_sleep();
3197 }
3198#endif
3199 large_entry_insert_no_lock(szone, large_entry);
3200#if DEBUG_MALLOC
3201 if (!large_entry_for_pointer_no_lock(szone, (void *)addr)) {
3202 malloc_printf("can't find entry just added\n");
3203 large_debug_print(szone);
3204 szone_sleep();
3205 }
3206#endif
3207 szone->num_large_objects_in_use ++;
3208 szone->num_bytes_in_large_objects += size;
3209 }
3210 SZONE_UNLOCK(szone);
3211 if (range_to_deallocate.size) {
3212 deallocate_pages(szone, (void *)range_to_deallocate.address, range_to_deallocate.size, 0); // we deallocate outside the lock
3213 }
3214 return (void *)addr;
3215}
3216
3217static INLINE void
3218free_large_or_huge(szone_t *szone, void *ptr)
3219{
3220 // We have established ptr is page-aligned and not tiny nor small
3221 large_entry_t *entry;
3222 vm_range_t vm_range_to_deallocate;
3223 huge_entry_t *huge;
3224
3225 SZONE_LOCK(szone);
3226 entry = large_entry_for_pointer_no_lock(szone, ptr);
3227 if (entry) {
3228 vm_range_to_deallocate = large_free_no_lock(szone, entry);
3229#if DEBUG_MALLOC
3230 if (large_entry_for_pointer_no_lock(szone, ptr)) {
3231 malloc_printf("*** just after freeing %p still in use num_large_entries=%d\n", ptr, szone->num_large_entries);
3232 large_debug_print(szone);
3233 szone_sleep();
3234 }
3235#endif
3236 } else if ((huge = huge_entry_for_pointer_no_lock(szone, ptr))) {
3237 vm_range_to_deallocate = *huge;
3238 *huge = szone->huge_entries[--szone->num_huge_entries]; // last entry fills that spot
3239 szone->num_bytes_in_huge_objects -= (size_t)vm_range_to_deallocate.size;
3240 } else {
3241#if DEBUG_MALLOC
3242 large_debug_print(szone);
3243#endif
3244 szone_error(szone, "pointer being freed was not allocated", ptr, NULL);
3245 return;
3246 }
3247 SZONE_UNLOCK(szone); // we release the lock asap
3248 CHECK(szone, __PRETTY_FUNCTION__);
3249 // we deallocate_pages, including guard pages
3250 if (vm_range_to_deallocate.address) {
3251#if DEBUG_MALLOC
3252 if (large_entry_for_pointer_no_lock(szone, (void *)vm_range_to_deallocate.address)) {
3253 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n", vm_range_to_deallocate.address, szone->num_large_entries);
3254 large_debug_print(szone);
3255 szone_sleep();
3256 }
3257#endif
3258 deallocate_pages(szone, (void *)vm_range_to_deallocate.address, (size_t)vm_range_to_deallocate.size, 0);
3259 }
3260}
3261
3262static INLINE int
3263try_realloc_large_or_huge_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size)
3264{
3265 vm_address_t addr = (vm_address_t)ptr + old_size;
3266 large_entry_t *large_entry, saved_entry;
3267 huge_entry_t *huge_entry, huge;
3268 kern_return_t err;
3269
3270#if DEBUG_MALLOC
3271 if (old_size != ((old_size >> vm_page_shift) << vm_page_shift)) {
3272 malloc_printf("*** old_size is %d\n", old_size);
3273 }
3274#endif
3275 SZONE_LOCK(szone);
3276 large_entry = large_entry_for_pointer_no_lock(szone, (void *)addr);
3277 SZONE_UNLOCK(szone);
3278 if (large_entry) {
3279 return 0; // large pointer already exists in table - extension is not going to work
3280 }
3281 new_size = round_page(new_size);
3282 /*
3283 * Ask for allocation at a specific address, and mark as realloc
3284 * to request coalescing with previous realloc'ed extensions.
3285 */
3286 err = vm_allocate(mach_task_self(), &addr, new_size - old_size, VM_MAKE_TAG(VM_MEMORY_REALLOC));
3287 if (err != KERN_SUCCESS) {
3288 return 0;
3289 }
3290 SZONE_LOCK(szone);
3291 /*
3292 * If the new size is still under the large/huge threshold, we can just
3293 * extend the existing large block.
3294 *
3295 * Note: this logic is predicated on the understanding that an allocated
3296 * block can never really shrink, so that the new size will always be
3297 * larger than the old size.
3298 *
3299 * Note: the use of 1 << vm_page_shift here has to do with the subdivision
3300 * of the bits in the large_entry_t, and not the size of a page (directly).
3301 */
3302 if ((new_size >> vm_page_shift) < (1 << vm_page_shift)) {
3303 /* extend existing large entry */
3304 large_entry = large_entry_for_pointer_no_lock(szone, ptr);
3305 if (!large_entry) {
3306 szone_error(szone, "large entry reallocated is not properly in table", ptr, NULL);
3307 /* XXX will cause fault on next reference to entry */
3308 }
3309 large_entry->address_and_num_pages = (uintptr_t)ptr | (new_size >> vm_page_shift);
3310 szone->num_bytes_in_large_objects += new_size - old_size;
3311 } else if ((old_size >> vm_page_shift) >= (1 << vm_page_shift)) {
3312 /* extend existing huge entry */
3313 huge_entry = huge_entry_for_pointer_no_lock(szone, ptr);
3314 if (!huge_entry) {
3315 szone_error(szone, "huge entry reallocated is not properly in table", ptr, NULL);
3316 /* XXX will cause fault on next reference to huge_entry */
3317 }
3318 huge_entry->size = new_size;
3319 szone->num_bytes_in_huge_objects += new_size - old_size;
3320 } else {
3321 /* need to convert large entry to huge entry */
3322
3323 /* release large entry, note we still have the VM allocation */
3324 large_entry = large_entry_for_pointer_no_lock(szone, ptr);
3325 saved_entry = *large_entry; // in case we need to put it back
3326 large_free_no_lock(szone, large_entry);
3327
3328 /* and get a huge entry */
3329 huge.address = (vm_address_t)ptr;
3330 huge.size = new_size; /* fix up size */
3331 SZONE_UNLOCK(szone);
3332 if (huge_entry_append(szone, huge)) {
3333 szone->num_bytes_in_huge_objects += new_size;
3334 return 1; // success!
3335 }
3336 SZONE_LOCK(szone);
3337 // we leak memory (the extra space appended) but data structures are correct
3338 large_entry_insert_no_lock(szone, saved_entry); // this will reinsert the large entry
3339 }
3340 SZONE_UNLOCK(szone); // we release the lock asap
3341 return 1;
3342}
3343
3344/********************* Zone call backs ************************/
3345
3346static void
3347szone_free(szone_t *szone, void *ptr)
3348{
3349 region_t *tiny_region;
3350 region_t *small_region;
3351
3352#if DEBUG_MALLOC
3353 if (LOG(szone, ptr))
3354 malloc_printf("in szone_free with %p\n", ptr);
3355#endif
3356 if (!ptr)
3357 return;
3358 /*
3359 * Try to free to a tiny region.
3360 */
3361 if ((uintptr_t)ptr & (TINY_QUANTUM - 1)) {
3362 szone_error(szone, "Non-aligned pointer being freed", ptr, NULL);
3363 return;
3364 }
3365 if ((tiny_region = tiny_region_for_ptr_no_lock(szone, ptr)) != NULL) {
3366 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS) {
3367 szone_error(szone, "Pointer to metadata being freed", ptr, NULL);
3368 return;
3369 }
3370 free_tiny(szone, ptr, tiny_region);
3371 return;
3372 }
3373
3374 /*
3375 * Try to free to a small region.
3376 */
3377 if ((uintptr_t)ptr & (SMALL_QUANTUM - 1)) {
3378 szone_error(szone, "Non-aligned pointer being freed (2)", ptr, NULL);
3379 return;
3380 }
3381 if ((small_region = small_region_for_ptr_no_lock(szone, ptr)) != NULL) {
3382 if (SMALL_META_INDEX_FOR_PTR(ptr) >= NUM_SMALL_BLOCKS) {
3383 szone_error(szone, "Pointer to metadata being freed (2)", ptr, NULL);
3384 return;
3385 }
3386 free_small(szone, ptr, small_region);
3387 return;
3388 }
3389
3390 /* check that it's a legal large/huge allocation */
3391 if ((uintptr_t)ptr & (vm_page_size - 1)) {
3392 szone_error(szone, "non-page-aligned, non-allocated pointer being freed", ptr, NULL);
3393 return;
3394 }
3395 free_large_or_huge(szone, ptr);
3396}
3397
3398static INLINE void *
3399szone_malloc_should_clear(szone_t *szone, size_t size, boolean_t cleared_requested)
3400{
3401 void *ptr;
3402 msize_t msize;
3403
3404 if (size <= 31*TINY_QUANTUM) {
3405 // think tiny
3406 msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
3407 if (!msize)
3408 msize = 1;
3409 ptr = tiny_malloc_should_clear(szone, msize, cleared_requested);
3410 } else if (!((szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) && PROTECT_SMALL) && (size < LARGE_THRESHOLD)) {
3411 // think small
3412 msize = SMALL_MSIZE_FOR_BYTES(size + SMALL_QUANTUM - 1);
3413 if (! msize) msize = 1;
3414 ptr = small_malloc_should_clear(szone, msize, cleared_requested);
3415 } else {
3416 // large or huge
3417 size_t num_pages = round_page(size) >> vm_page_shift;
3418 if (num_pages == 0) /* Overflowed */
3419 ptr = 0;
3420 else
3421 ptr = large_and_huge_malloc(szone, num_pages);
3422 }
3423#if DEBUG_MALLOC
3424 if (LOG(szone, ptr))
3425 malloc_printf("szone_malloc returned %p\n", ptr);
3426#endif
3427 /*
3428 * If requested, scribble on allocated memory.
3429 */
3430 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && ptr && !cleared_requested && size)
3431 memset(ptr, 0xaa, size);
3432
3433 return ptr;
3434}
3435
3436static void *
3437szone_malloc(szone_t *szone, size_t size) {
3438 return szone_malloc_should_clear(szone, size, 0);
3439}
3440
3441static void *
3442szone_calloc(szone_t *szone, size_t num_items, size_t size)
3443{
3444 size_t total_bytes = num_items * size;
3445 if ((num_items > 1) && (size != 0) && ((total_bytes / size) != num_items))
3446 return NULL;
3447 return szone_malloc_should_clear(szone, total_bytes, 1);
3448}
3449
3450static void *
3451szone_valloc(szone_t *szone, size_t size)
3452{
3453 void *ptr;
3454 size_t num_pages;
3455
3456 num_pages = round_page(size) >> vm_page_shift;
3457 ptr = large_and_huge_malloc(szone, num_pages);
3458#if DEBUG_MALLOC
3459 if (LOG(szone, ptr))
3460 malloc_printf("szone_valloc returned %p\n", ptr);
3461#endif
3462 return ptr;
3463}
3464
3465static size_t
3466szone_size(szone_t *szone, const void *ptr)
3467{
3468 size_t size = 0;
3469 boolean_t is_free;
3470 msize_t msize, msize_and_free;
3471 large_entry_t *entry;
3472 huge_entry_t *huge;
3473
3474 if (!ptr)
3475 return 0;
3476#if DEBUG_MALLOC
3477 if (LOG(szone, ptr)) {
3478 malloc_printf("in szone_size for %p (szone=%p)\n", ptr, szone);
3479 }
3480#endif
3481
3482 /*
3483 * Look for it in a tiny region.
3484 */
3485 if ((uintptr_t)ptr & (TINY_QUANTUM - 1))
3486 return 0;
3487 if (tiny_region_for_ptr_no_lock(szone, ptr)) {
3488 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS)
3489 return 0;
3490 msize = get_tiny_meta_header(ptr, &is_free);
3491 return (is_free) ? 0 : TINY_BYTES_FOR_MSIZE(msize);
3492 }
3493
3494 /*
3495 * Look for it in a small region.
3496 */
3497 if ((uintptr_t)ptr & (SMALL_QUANTUM - 1))
3498 return 0;
3499 if (small_region_for_ptr_no_lock(szone, ptr)) {
3500 if (SMALL_META_INDEX_FOR_PTR(ptr) >= NUM_SMALL_BLOCKS)
3501 return 0;
3502 msize_and_free = *SMALL_METADATA_FOR_PTR(ptr);
3503 return (msize_and_free & SMALL_IS_FREE) ? 0 : SMALL_BYTES_FOR_MSIZE(msize_and_free);
3504 }
3505
3506 /*
3507 * If not page-aligned, it cannot have come from a large or huge allocation.
3508 */
3509 if ((uintptr_t)ptr & (vm_page_size - 1))
3510 return(0);
3511
3512 /*
3513 * Look for it in a large or huge entry.
3514 */
3515 SZONE_LOCK(szone);
3516 entry = large_entry_for_pointer_no_lock(szone, ptr);
3517 if (entry) {
3518 size = LARGE_ENTRY_SIZE(*entry);
3519 } else if ((huge = huge_entry_for_pointer_no_lock(szone, ptr))) {
3520 size = huge->size;
3521 }
3522 SZONE_UNLOCK(szone);
3523#if DEBUG_MALLOC
3524 if (LOG(szone, ptr)) {
3525 malloc_printf("szone_size for %p returned %d\n", ptr, (unsigned)size);
3526 }
3527#endif
3528 return size;
3529}
3530
3531static void *
3532szone_realloc(szone_t *szone, void *ptr, size_t new_size)
3533{
3534 size_t old_size;
3535 void *new_ptr;
3536
3537#if DEBUG_MALLOC
3538 if (LOG(szone, ptr)) {
3539 malloc_printf("in szone_realloc for %p, %d\n", ptr, (unsigned)new_size);
3540 }
3541#endif
3542 if (!ptr) {
3543 ptr = szone_malloc(szone, new_size);
3544 return ptr;
3545 }
3546 old_size = szone_size(szone, ptr);
3547 if (!old_size) {
3548 szone_error(szone, "pointer being reallocated was not allocated", ptr, NULL);
3549 return NULL;
3550 }
3551 /* we never shrink an allocation */
3552 if (old_size >= new_size)
3553 return ptr;
3554
3555 /*
3556 * If the old and new sizes both suit the tiny allocator, try to reallocate in-place.
3557 */
3558 if ((new_size + TINY_QUANTUM - 1) <= 31 * TINY_QUANTUM) {
3559 if (try_realloc_tiny_in_place(szone, ptr, old_size, new_size)) {
3560 return ptr;
3561 }
3562
3563 /*
3564 * If the old and new sizes both suit the small allocator, and we're not protecting the
3565 * small allocations, try to reallocate in-place.
3566 */
3567 } else if (!((szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) && PROTECT_SMALL) &&
3568 ((new_size + SMALL_QUANTUM - 1) < LARGE_THRESHOLD) &&
3569 (old_size > 31 * TINY_QUANTUM)) {
3570 if (try_realloc_small_in_place(szone, ptr, old_size, new_size)) {
3571 return ptr;
3572 }
3573
3574 /*
3575 * If the allocation's a large or huge allocation, try to reallocate in-place there.
3576 */
3577 } else if (!((szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) && PROTECT_SMALL) && (old_size > LARGE_THRESHOLD)) {
3578 if (try_realloc_large_or_huge_in_place(szone, ptr, old_size, new_size)) {
3579 return ptr;
3580 }
3581 }
3582
3583 /*
3584 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
3585 */
3586 new_ptr = szone_malloc(szone, new_size);
3587 if (new_ptr == NULL)
3588 return NULL;
3589
3590 /*
3591 * If the allocation's large enough, try to copy using VM. If that fails, or
3592 * if it's too small, just copy by hand.
3593 */
3594 if ((old_size < VM_COPY_THRESHOLD) ||
3595 vm_copy(mach_task_self(), (vm_address_t)ptr, old_size, (vm_address_t)new_ptr))
3596 memcpy(new_ptr, ptr, old_size);
3597 szone_free(szone, ptr);
3598
3599#if DEBUG_MALLOC
3600 if (LOG(szone, ptr)) {
3601 malloc_printf("szone_realloc returned %p for %d\n", new_ptr, (unsigned)new_size);
3602 }
3603#endif
3604 return new_ptr;
3605}
3606
3607// given a size, returns the number of pointers allocated capable of holding
3608// that size, up to the limit specified by the 'count' argument. These pointers
3609// are stored in the 'results' array, which must be allocated by the caller.
3610// may return zero, since this function is only a best attempt at allocating
3611// the pointers. clients should be prepared to call malloc for any additional
3612// blocks they need.
3613static unsigned
3614szone_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count)
3615{
3616 msize_t msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
3617 unsigned found = 0;
3618
3619 // only bother implementing this for tiny
3620 if (size > 31*TINY_QUANTUM)
3621 return 0;
3622 // make sure to return objects at least one quantum in size
3623 if (!msize)
3624 msize = 1;
3625
3626 CHECK(szone, __PRETTY_FUNCTION__);
3627
3628 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
3629 // the caller has done so.
3630 SZONE_LOCK(szone);
3631
3632 // with the zone locked, allocate objects from the free list until all
3633 // sufficiently large objects have been exhausted, or we have met our quota
3634 // of objects to allocate.
3635 while (found < count) {
3636 void *ptr = tiny_malloc_from_free_list(szone, msize);
3637 if (!ptr)
3638 break;
3639
3640 *results++ = ptr;
3641 found++;
3642 }
3643 SZONE_UNLOCK(szone);
3644 return found;
3645}
3646
3647static void
3648szone_batch_free(szone_t *szone, void **to_be_freed, unsigned count)
3649{
3650 unsigned cc = 0;
3651 void *ptr;
3652 region_t *tiny_region;
3653 boolean_t is_free;
3654 msize_t msize;
3655
3656 // frees all the pointers in to_be_freed
3657 // note that to_be_freed may be overwritten during the process
3658 if (!count)
3659 return;
3660 CHECK(szone, __PRETTY_FUNCTION__);
3661 SZONE_LOCK(szone);
3662 while (cc < count) {
3663 ptr = to_be_freed[cc];
3664 if (ptr) {
3665 /* XXX this really slows us down */
3666 tiny_region = tiny_region_for_ptr_no_lock(szone, ptr);
3667 if (tiny_region) {
3668 // this is a tiny pointer
3669 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS)
3670 break; // pointer to metadata; let the standard free deal with it
3671 msize = get_tiny_meta_header(ptr, &is_free);
3672 if (is_free)
3673 break; // a double free; let the standard free deal with it
3674 tiny_free_no_lock(szone, tiny_region, ptr, msize);
3675 to_be_freed[cc] = NULL;
3676 }
3677 }
3678 cc++;
3679 }
3680 SZONE_UNLOCK(szone);
3681 CHECK(szone, __PRETTY_FUNCTION__);
3682 while (count--) {
3683 ptr = to_be_freed[count];
3684 if (ptr)
3685 szone_free(szone, ptr);
3686 }
3687}
3688
3689static void
3690szone_destroy(szone_t *szone)
3691{
3692 size_t index;
3693 large_entry_t *large;
3694 vm_range_t range_to_deallocate;
3695 huge_entry_t *huge;
3696
3697 /* destroy large entries */
3698 index = szone->num_large_entries;
3699 while (index--) {
3700 large = szone->large_entries + index;
3701 if (!LARGE_ENTRY_IS_EMPTY(*large)) {
3702 // we deallocate_pages, including guard pages
3703 deallocate_pages(szone, (void *)LARGE_ENTRY_ADDRESS(*large), LARGE_ENTRY_SIZE(*large), szone->debug_flags);
3704 }
3705 }
3706 if (szone->num_large_entries * sizeof(large_entry_t) >= LARGE_THRESHOLD) {
3707 // we do not free in the small chunk case
3708 large_entries_free_no_lock(szone, szone->large_entries, szone->num_large_entries, &range_to_deallocate);
3709 if (range_to_deallocate.size)
3710 deallocate_pages(szone, (void *)range_to_deallocate.address, (size_t)range_to_deallocate.size, 0);
3711 }
3712
3713 /* destroy huge entries */
3714 index = szone->num_huge_entries;
3715 while (index--) {
3716 huge = szone->huge_entries + index;
3717 deallocate_pages(szone, (void *)huge->address, huge->size, szone->debug_flags);
3718 }
3719
3720 /* destroy tiny regions */
3721 for (index = 0; index < szone->num_tiny_regions_allocated; ++index)
3722 if (szone->tiny_regions[index])
3723 deallocate_pages(szone, szone->tiny_regions[index], TINY_REGION_SIZE, 0);
3724
3725 /* destroy small regions */
3726 for (index = 0; index < szone->num_small_regions_allocated; ++index)
3727 if (szone->small_regions[index])
3728 deallocate_pages(szone, szone->small_regions[index], SMALL_REGION_SIZE, 0);
3729
3730 /* destroy region hash rings, if any */
3731 if (szone->tiny_regions != szone->initial_tiny_regions) {
3732 size_t size = round_page(szone->num_tiny_regions_allocated * sizeof(region_t));
3733 deallocate_pages(szone, szone->tiny_regions, size, 0);
3734 }
3735 if (szone->small_regions != szone->initial_small_regions) {
3736 size_t size = round_page(szone->num_small_regions_allocated * sizeof(region_t));
3737 deallocate_pages(szone, szone->small_regions, size, 0);
3738 }
3739 /* Now destroy the separate szone region */
3740 deallocate_pages(szone, (void *)szone, SZONE_PAGED_SIZE, SCALABLE_MALLOC_ADD_GUARD_PAGES);
3741}
3742
3743static size_t
3744szone_good_size(szone_t *szone, size_t size)
3745{
3746 msize_t msize;
3747 unsigned num_pages;
3748
3749 if (size <= 31 * TINY_QUANTUM) {
3750 // think tiny
3751 msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
3752 if (! msize) msize = 1;
3753 return TINY_BYTES_FOR_MSIZE(msize);
3754 }
3755 if (!((szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) && PROTECT_SMALL) && (size < LARGE_THRESHOLD)) {
3756 // think small
3757 msize = SMALL_MSIZE_FOR_BYTES(size + SMALL_QUANTUM - 1);
3758 if (! msize) msize = 1;
3759 return SMALL_BYTES_FOR_MSIZE(msize);
3760 } else {
3761 num_pages = round_page(size) >> vm_page_shift;
3762 if (!num_pages)
3763 num_pages = 1; // minimal allocation size for this
3764 return num_pages << vm_page_shift;
3765 }
3766}
3767
3768unsigned szone_check_counter = 0;
3769unsigned szone_check_start = 0;
3770unsigned szone_check_modulo = 1;
3771
3772static boolean_t
3773szone_check_all(szone_t *szone, const char *function)
3774{
3775 size_t index;
3776
3777 SZONE_LOCK(szone);
3778 CHECK_LOCKED(szone, __PRETTY_FUNCTION__);
3779
3780 /* check tiny regions - chould check region count */
3781 for (index = 0; index < szone->num_tiny_regions_allocated; ++index) {
3782 region_t tiny = szone->tiny_regions[index];
3783 if (tiny && !tiny_check_region(szone, tiny)) {
3784 SZONE_UNLOCK(szone);
3785 szone->debug_flags &= ~ CHECK_REGIONS;
3786 szone_error(szone, "check: tiny region incorrect", NULL,
3787 "*** tiny region %d incorrect szone_check_all(%s) counter=%d\n",
3788 index, function, szone_check_counter);
3789 return 0;
3790 }
3791 }
3792 /* check tiny free lists */
3793 for (index = 0; index < NUM_TINY_SLOTS; ++index) {
3794 if (!tiny_free_list_check(szone, index)) {
3795 SZONE_UNLOCK(szone);
3796 szone->debug_flags &= ~ CHECK_REGIONS;
3797 szone_error(szone, "check: tiny free list incorrect", NULL,
3798 "*** tiny free list incorrect (slot=%d) szone_check_all(%s) counter=%d\n",
3799 index, function, szone_check_counter);
3800 return 0;
3801 }
3802 }
3803 /* check small regions - could check region count */
3804 for (index = 0; index < szone->num_small_regions_allocated; ++index) {
3805 region_t small = szone->small_regions[index];
3806 if (small && !szone_check_small_region(szone, small)) {
3807 SZONE_UNLOCK(szone);
3808 szone->debug_flags &= ~ CHECK_REGIONS;
3809 szone_error(szone, "check: small region incorrect", NULL,
3810 "*** small region %d incorrect szone_check_all(%s) counter=%d\n",
3811 index, function, szone_check_counter);
3812 return 0;
3813 }
3814 }
3815 /* check small free lists */
3816 for (index = 0; index < NUM_SMALL_SLOTS; ++index) {
3817 if (!small_free_list_check(szone, index)) {
3818 SZONE_UNLOCK(szone);
3819 szone->debug_flags &= ~ CHECK_REGIONS;
3820 szone_error(szone, "check: small free list incorrect", NULL,
3821 "*** small free list incorrect (grain=%d) szone_check_all(%s) counter=%d\n",
3822 index, function, szone_check_counter);
3823 return 0;
3824 }
3825 }
3826 SZONE_UNLOCK(szone);
3827 return 1;
3828}
3829
3830static boolean_t
3831szone_check(szone_t *szone)
3832{
3833 if ((++szone_check_counter % 10000) == 0)
3834 _malloc_printf(ASL_LEVEL_NOTICE, "at szone_check counter=%d\n", szone_check_counter);
3835 if (szone_check_counter < szone_check_start)
3836 return 1;
3837 if (szone_check_counter % szone_check_modulo)
3838 return 1;
3839 return szone_check_all(szone, "");
3840}
3841
3842static kern_return_t
3843szone_ptr_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder)
3844{
3845 szone_t *szone;
3846 kern_return_t err;
3847
3848 if (!reader) reader = _szone_default_reader;
3849 err = reader(task, zone_address, sizeof(szone_t), (void **)&szone);
3850 if (err) return err;
3851 err = tiny_in_use_enumerator(task, context, type_mask, szone, reader, recorder);
3852 if (err) return err;
3853 err = small_in_use_enumerator(task, context, type_mask, szone, reader, recorder);
3854 if (err) return err;
3855 err = large_in_use_enumerator(task, context, type_mask,
3856 (vm_address_t)szone->large_entries, szone->num_large_entries, reader,
3857 recorder);
3858 if (err) return err;
3859 err = huge_in_use_enumerator(task, context, type_mask,
3860 (vm_address_t)szone->huge_entries, szone->num_huge_entries, reader,
3861 recorder);
3862 return err;
3863}
3864
3865// Following method is deprecated: use scalable_zone_statistics instead
3866void
3867scalable_zone_info(malloc_zone_t *zone, unsigned *info_to_fill, unsigned count)
3868{
3869 szone_t *szone = (void *)zone;
3870 unsigned info[13];
3871
3872 // We do not lock to facilitate debug
3873 info[4] = szone->num_tiny_objects;
3874 info[5] = szone->num_bytes_in_tiny_objects;
3875 info[6] = szone->num_small_objects;
3876 info[7] = szone->num_bytes_in_small_objects;
3877 info[8] = szone->num_large_objects_in_use;
3878 info[9] = szone->num_bytes_in_large_objects;
3879 info[10] = szone->num_huge_entries;
3880 info[11] = szone->num_bytes_in_huge_objects;
3881 info[12] = szone->debug_flags;
3882 info[0] = info[4] + info[6] + info[8] + info[10];
3883 info[1] = info[5] + info[7] + info[9] + info[11];
3884 info[3] = szone->num_tiny_regions * TINY_REGION_SIZE + szone->num_small_regions * SMALL_REGION_SIZE + info[9] + info[11];
3885 info[2] = info[3] - szone->tiny_bytes_free_at_end - szone->small_bytes_free_at_end;
3886 memcpy(info_to_fill, info, sizeof(unsigned)*count);
3887}
3888
3889static void
3890szone_print(szone_t *szone, boolean_t verbose)
3891{
3892 unsigned info[13];
3893 size_t index;
3894 region_t region;
3895
3896 SZONE_LOCK(szone);
3897 scalable_zone_info((void *)szone, info, 13);
3898 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
3899 "Scalable zone %p: inUse=%d(%y) touched=%y allocated=%y flags=%d\n",
3900 szone, info[0], info[1], info[2], info[3], info[12]);
3901 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
3902 "\ttiny=%d(%y) small=%d(%y) large=%d(%y) huge=%d(%y)\n",
3903 info[4], info[5], info[6], info[7], info[8], info[9], info[10], info[11]);
3904 // tiny
3905 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
3906 "%d tiny regions:\n", szone->num_tiny_regions);
3907 for (index = 0; index < szone->num_tiny_regions_allocated; ++index) {
3908 region = szone->tiny_regions[index];
3909 if (region)
3910 print_tiny_region(verbose, region, (region == szone->last_tiny_region) ?
3911 szone->tiny_bytes_free_at_end : 0);
3912 }
3913 if (verbose)
3914 print_tiny_free_list(szone);
3915 // small
3916 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
3917 "%d small regions:\n", szone->num_small_regions);
3918 for (index = 0; index < szone->num_small_regions_allocated; ++index) {
3919 region = szone->small_regions[index];
3920 if (region)
3921 print_small_region(szone, verbose, region,
3922 (region == szone->last_small_region) ?
3923 szone->small_bytes_free_at_end : 0);
3924 }
3925 if (verbose)
3926 print_small_free_list(szone);
3927 SZONE_UNLOCK(szone);
3928}
3929
3930static void
3931szone_log(malloc_zone_t *zone, void *log_address)
3932{
3933 szone_t *szone = (szone_t *)zone;
3934
3935 szone->log_address = log_address;
3936}
3937
3938static void
3939szone_force_lock(szone_t *szone)
3940{
3941 SZONE_LOCK(szone);
3942}
3943
3944static void
3945szone_force_unlock(szone_t *szone)
3946{
3947 SZONE_UNLOCK(szone);
3948}
3949
3950boolean_t
3951scalable_zone_statistics(malloc_zone_t *zone, malloc_statistics_t *stats, unsigned subzone)
3952{
3953 szone_t *szone = (szone_t *)zone;
3954
3955 switch (subzone) {
3956 case 0:
3957 stats->blocks_in_use = szone->num_tiny_objects;
3958 stats->size_in_use = szone->num_bytes_in_tiny_objects;
3959 stats->size_allocated = szone->num_tiny_regions * TINY_REGION_SIZE;
3960 stats->max_size_in_use = stats->size_allocated - szone->tiny_bytes_free_at_end;
3961 return 1;
3962 case 1:
3963 stats->blocks_in_use = szone->num_small_objects;
3964 stats->size_in_use = szone->num_bytes_in_small_objects;
3965 stats->size_allocated = szone->num_small_regions * SMALL_REGION_SIZE;
3966 stats->max_size_in_use = stats->size_allocated - szone->small_bytes_free_at_end;
3967 return 1;
3968 case 2:
3969 stats->blocks_in_use = szone->num_large_objects_in_use;
3970 stats->size_in_use = szone->num_bytes_in_large_objects;
3971 stats->max_size_in_use = stats->size_allocated = stats->size_in_use;
3972 return 1;
3973 case 3:
3974 stats->blocks_in_use = szone->num_huge_entries;
3975 stats->size_in_use = szone->num_bytes_in_huge_objects;
3976 stats->max_size_in_use = stats->size_allocated = stats->size_in_use;
3977 return 1;
3978 }
3979 return 0;
3980}
3981
3982static void
3983szone_statistics(szone_t *szone, malloc_statistics_t *stats)
3984{
3985 size_t big_and_huge;
3986
3987 stats->blocks_in_use =
3988 szone->num_tiny_objects +
3989 szone->num_small_objects +
3990 szone->num_large_objects_in_use +
3991 szone->num_huge_entries;
3992 big_and_huge = szone->num_bytes_in_large_objects + szone->num_bytes_in_huge_objects;
3993 stats->size_in_use = szone->num_bytes_in_tiny_objects + szone->num_bytes_in_small_objects + big_and_huge;
3994 stats->max_size_in_use = stats->size_allocated =
3995 szone->num_tiny_regions * TINY_REGION_SIZE +
3996 szone->num_small_regions * SMALL_REGION_SIZE +
3997 big_and_huge ;
3998
3999 // Now we account for the untouched areas
4000 stats->max_size_in_use -= szone->tiny_bytes_free_at_end;
4001 stats->max_size_in_use -= szone->small_bytes_free_at_end;
4002}
4003
4004static const struct malloc_introspection_t szone_introspect = {
4005 (void *)szone_ptr_in_use_enumerator,
4006 (void *)szone_good_size,
4007 (void *)szone_check,
4008 (void *)szone_print,
4009 szone_log,
4010 (void *)szone_force_lock,
4011 (void *)szone_force_unlock,
4012 (void *)szone_statistics
4013}; // marked as const to spare the DATA section
4014
4015malloc_zone_t *
4016create_scalable_zone(size_t initial_size, unsigned debug_flags)
4017{
4018 szone_t *szone;
4019
4020 /*
4021 * Sanity-check our build-time assumptions about the size of a page.
4022 * Since we have sized various things assuming the default page size,
4023 * attempting to determine it dynamically is not useful.
4024 */
4025 if ((vm_page_size != _vm_page_size) || (vm_page_shift != _vm_page_shift)) {
4026 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
4027 exit(-1);
4028 }
4029
4030 /* get memory for the zone, which is now separate from any region.
4031 add guard pages to prevent walking from any other vm allocations
4032 to here and overwriting the function pointers in basic_zone. */
4033 szone = allocate_pages(NULL, SZONE_PAGED_SIZE, 0,
4034 SCALABLE_MALLOC_ADD_GUARD_PAGES,
4035 VM_MEMORY_MALLOC);
4036 if (!szone)
4037 return NULL;
4038 /* set up the szone structure */
4039 szone->tiny_regions = szone->initial_tiny_regions;
4040 szone->small_regions = szone->initial_small_regions;
4041 szone->num_tiny_regions_allocated = INITIAL_NUM_REGIONS;
4042 szone->num_small_regions_allocated = INITIAL_NUM_REGIONS;
4043 szone->basic_zone.version = 3;
4044 szone->basic_zone.size = (void *)szone_size;
4045 szone->basic_zone.malloc = (void *)szone_malloc;
4046 szone->basic_zone.calloc = (void *)szone_calloc;
4047 szone->basic_zone.valloc = (void *)szone_valloc;
4048 szone->basic_zone.free = (void *)szone_free;
4049 szone->basic_zone.realloc = (void *)szone_realloc;
4050 szone->basic_zone.destroy = (void *)szone_destroy;
4051 szone->basic_zone.batch_malloc = (void *)szone_batch_malloc;
4052 szone->basic_zone.batch_free = (void *)szone_batch_free;
4053 szone->basic_zone.introspect = (struct malloc_introspection_t *)&szone_introspect;
4054 szone->debug_flags = debug_flags;
4055 LOCK_INIT(szone->lock);
4056
4057#if 0
4058#warning CHECK_REGIONS enabled
4059 debug_flags |= CHECK_REGIONS;
4060#endif
4061#if 0
4062#warning LOG enabled
4063 szone->log_address = ~0;
4064#endif
4065 CHECK(szone, __PRETTY_FUNCTION__);
4066 return (malloc_zone_t *)szone;
4067}
4068
4069/********* Support code for emacs unexec ************/
4070
4071/* History of freezedry version numbers:
4072 *
4073 * 1) Old malloc (before the scalable malloc implementation in this file
4074 * existed).
4075 * 2) Original freezedrying code for scalable malloc. This code was apparently
4076 * based on the old freezedrying code and was fundamentally flawed in its
4077 * assumption that tracking allocated memory regions was adequate to fake
4078 * operations on freezedried memory. This doesn't work, since scalable
4079 * malloc does not store flags in front of large page-aligned allocations.
4080 * 3) Original szone-based freezedrying code.
4081 * 4) Fresher malloc with tiny zone
4082 * 5) 32/64bit compatible malloc
4083 * 6) Metadata within 1MB and 8MB region for tiny and small
4084 *
4085 * No version backward compatibility is provided, but the version number does
4086 * make it possible for malloc_jumpstart() to return an error if the application
4087 * was freezedried with an older version of malloc.
4088 */
4089#define MALLOC_FREEZEDRY_VERSION 6
4090
4091typedef struct {
4092 unsigned version;
4093 unsigned nszones;
4094 szone_t *szones;
4095} malloc_frozen;
4096
4097static void *
4098frozen_malloc(szone_t *zone, size_t new_size)
4099{
4100 return malloc(new_size);
4101}
4102
4103static void *
4104frozen_calloc(szone_t *zone, size_t num_items, size_t size)
4105{
4106 return calloc(num_items, size);
4107}
4108
4109static void *
4110frozen_valloc(szone_t *zone, size_t new_size)
4111{
4112 return valloc(new_size);
4113}
4114
4115static void *
4116frozen_realloc(szone_t *zone, void *ptr, size_t new_size)
4117{
4118 size_t old_size = szone_size(zone, ptr);
4119 void *new_ptr;
4120
4121 if (new_size <= old_size) {
4122 return ptr;
4123 }
4124 new_ptr = malloc(new_size);
4125 if (old_size > 0) {
4126 memcpy(new_ptr, ptr, old_size);
4127 }
4128 return new_ptr;
4129}
4130
4131static void
4132frozen_free(szone_t *zone, void *ptr)
4133{
4134}
4135
4136static void
4137frozen_destroy(szone_t *zone)
4138{
4139}
4140
4141/********* Pseudo-private API for emacs unexec ************/
4142
4143/*
4144 * malloc_freezedry() records all of the szones in use, so that they can be
4145 * partially reconstituted by malloc_jumpstart(). Due to the differences
4146 * between reconstituted memory regions and those created by the szone code,
4147 * care is taken not to reallocate from the freezedried memory, except in the
4148 * case of a non-growing realloc().
4149 *
4150 * Due to the flexibility provided by the zone registration mechanism, it is
4151 * impossible to implement generic freezedrying for any zone type. This code
4152 * only handles applications that use the szone allocator, so malloc_freezedry()
4153 * returns 0 (error) if any non-szone zones are encountered.
4154 */
4155
4156uintptr_t
4157malloc_freezedry(void)
4158{
4159 extern unsigned malloc_num_zones;
4160 extern malloc_zone_t **malloc_zones;
4161 malloc_frozen *data;
4162 unsigned i;
4163
4164 /* Allocate space in which to store the freezedry state. */
4165 data = (malloc_frozen *) malloc(sizeof(malloc_frozen));
4166
4167 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
4168 data->version = MALLOC_FREEZEDRY_VERSION;
4169
4170 /* Allocate the array of szone pointers. */
4171 data->nszones = malloc_num_zones;
4172 data->szones = (szone_t *) calloc(malloc_num_zones, sizeof(szone_t));
4173
4174 /*
4175 * Fill in the array of szone structures. They are copied rather than
4176 * referenced, since the originals are likely to be clobbered during malloc
4177 * initialization.
4178 */
4179 for (i = 0; i < malloc_num_zones; i++) {
4180 if (strcmp(malloc_zones[i]->zone_name, "DefaultMallocZone")) {
4181 /* Unknown zone type. */
4182 free(data->szones);
4183 free(data);
4184 return 0;
4185 }
4186 memcpy(&data->szones[i], malloc_zones[i], sizeof(szone_t));
4187 }
4188
4189 return((uintptr_t)data);
4190}
4191
4192int
4193malloc_jumpstart(uintptr_t cookie)
4194{
4195 malloc_frozen *data = (malloc_frozen *)cookie;
4196 unsigned i;
4197
4198 if (data->version != MALLOC_FREEZEDRY_VERSION) {
4199 /* Unsupported freezedry version. */
4200 return 1;
4201 }
4202
4203 for (i = 0; i < data->nszones; i++) {
4204 /* Set function pointers. Even the functions that stay the same must be
4205 * set, since there are no guarantees that they will be mapped to the
4206 * same addresses. */
4207 data->szones[i].basic_zone.size = (void *) szone_size;
4208 data->szones[i].basic_zone.malloc = (void *) frozen_malloc;
4209 data->szones[i].basic_zone.calloc = (void *) frozen_calloc;
4210 data->szones[i].basic_zone.valloc = (void *) frozen_valloc;
4211 data->szones[i].basic_zone.free = (void *) frozen_free;
4212 data->szones[i].basic_zone.realloc = (void *) frozen_realloc;
4213 data->szones[i].basic_zone.destroy = (void *) frozen_destroy;
4214 data->szones[i].basic_zone.introspect = (struct malloc_introspection_t *)&szone_introspect;
4215
4216 /* Register the freezedried zone. */
4217 malloc_zone_register(&data->szones[i].basic_zone);
4218 }
4219
4220 return 0;
4221}
4222