]> git.saurik.com Git - apple/libc.git/blob - gen/magazine_malloc.c
Libc-825.40.1.tar.gz
[apple/libc.git] / gen / magazine_malloc.c
1 /*
2 * Copyright (c) 1999, 2006, 2008 Apple 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 /*
27 Multithread enhancements for "tiny" allocations introduced February 2008.
28 These are in the spirit of "Hoard". See:
29 Berger, E.D.; McKinley, K.S.; Blumofe, R.D.; Wilson, P.R. (2000).
30 "Hoard: a scalable memory allocator for multithreaded applications".
31 ACM SIGPLAN Notices 35 (11): 117-128. Berger2000.
32 <http://portal.acm.org/citation.cfm?id=356989.357000>
33 Retrieved on 2008-02-22.
34 */
35
36 /* gcc -g -O3 magazine_malloc.c malloc.c -o libmagmalloc.dylib -I. \
37 -I/System/Library/Frameworks/System.framework/PrivateHeaders/ -funit-at-a-time \
38 -dynamiclib -Wall -arch x86_64 -arch i386 -arch ppc */
39
40 #include <TargetConditionals.h>
41
42 #include "scalable_malloc.h"
43 #include "malloc_printf.h"
44 #include "_simple.h"
45 #include "magmallocProvider.h"
46
47 #include <pthread_internals.h> /* for pthread_lock_t SPI */
48 #include <pthread.h> /* for pthread API */
49
50 #include <stdint.h>
51 #include <unistd.h>
52 #include <mach/vm_statistics.h>
53 #include <mach/mach_init.h>
54 #include <sys/types.h>
55 #include <sys/mman.h>
56 #include <sys/param.h>
57
58 #if defined(__i386__) || defined(__x86_64__)
59 #define __APPLE_API_PRIVATE
60 #include <machine/cpu_capabilities.h>
61 #define _COMM_PAGE_VERSION_REQD 9
62 #undef __APPLE_API_PRIVATE
63 #else
64 #include <sys/sysctl.h>
65 #endif
66
67 #include <libkern/OSAtomic.h>
68 #include <mach-o/dyld.h> /* for NSVersionOfLinkTimeLibrary() */
69 #include <mach-o/dyld_priv.h> /* for _dyld_get_image_slide() */
70 #include <crt_externs.h> /* for _NSGetMachExecuteHeader() */
71 #include <mach/vm_param.h>
72 #include <sys/vmparam.h>
73
74 #include <CrashReporterClient.h>
75
76 /********************* DEFINITIONS ************************/
77
78 #define DEBUG_MALLOC 0 // set to one to debug malloc itself
79
80 #define DEBUG_CLIENT 0 // set to one to debug malloc client
81
82 #define DEBUG_MADVISE 0
83
84 // <rdar://problem/10397726>
85 #define RELAXED_INVARIANT_CHECKS 1
86
87 #if DEBUG_MALLOC
88 #warning DEBUG_MALLOC ENABLED
89 # define INLINE
90 # define ALWAYSINLINE
91 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) \
92 do { \
93 if (__is_threaded && TRY_LOCK(mag_ptr->magazine_lock)) { \
94 malloc_printf("*** magazine_lock was not set %p in %s\n", \
95 mag_ptr->magazine_lock, fun); \
96 } \
97 } while (0)
98 #else
99 # define INLINE __inline__
100 # define ALWAYSINLINE __attribute__((always_inline))
101 # define CHECK_MAGAZINE_PTR_LOCKED(szone, mag_ptr, fun) {}
102 #endif
103
104 # define NOINLINE __attribute__((noinline))
105
106 #if defined(__i386__) || defined(__x86_64__)
107 #define CACHE_ALIGN __attribute__ ((aligned (128) )) /* Future-proofing at 128B */
108 #elif defined(__ppc__) || defined(__ppc64__)
109 #define CACHE_ALIGN __attribute__ ((aligned (128) ))
110 #else
111 #define CACHE_ALIGN
112 #endif
113
114 #if !__LP64__
115 #define ASLR_INTERNAL 1
116 #endif
117
118 /*
119 * Access to global variables is slow, so optimise our handling of vm_page_size
120 * and vm_page_shift.
121 */
122 #define _vm_page_size vm_page_size /* to get to the originals */
123 #define _vm_page_shift vm_page_shift
124 #define vm_page_size 4096 /* our normal working sizes */
125 #define vm_page_shift 12
126
127 /*
128 * msize - a type to refer to the number of quanta of a tiny or small
129 * allocation. A tiny block with an msize of 3 would be 3 << SHIFT_TINY_QUANTUM
130 * bytes in size.
131 */
132 typedef unsigned short msize_t;
133
134 typedef union {
135 void *p;
136 uintptr_t u;
137 } ptr_union;
138
139 typedef struct {
140 ptr_union previous;
141 ptr_union next;
142 } free_list_t;
143
144 typedef unsigned int grain_t; // N.B. wide enough to index all free slots
145
146 typedef int mag_index_t;
147
148 #define CHECK_REGIONS (1 << 31)
149 #define DISABLE_ASLR (1 << 30)
150
151 #define MAX_RECORDER_BUFFER 256
152
153 /********************* DEFINITIONS for tiny ************************/
154
155 /*
156 * Memory in the Tiny range is allocated from regions (heaps) pointed to by the
157 * szone's hashed_regions pointer.
158 *
159 * Each region is laid out as a heap, followed by a header block, all within
160 * a 1MB (2^20) block. This means there are 64520 16-byte blocks and the header
161 * is 16138 bytes, making the total 1048458 bytes, leaving 118 bytes unused.
162 *
163 * The header block is arranged as in struct tiny_region defined just below, and
164 * consists of two bitfields (or bit arrays) interleaved 32 bits by 32 bits.
165 *
166 * Each bitfield comprises NUM_TINY_BLOCKS bits, and refers to the corresponding
167 * TINY_QUANTUM block within the heap.
168 *
169 * The bitfields are used to encode the state of memory within the heap. The header bit indicates
170 * that the corresponding quantum is the first quantum in a block (either in use or free). The
171 * in-use bit is set for the header if the block has been handed out (allocated). If the header
172 * bit is not set, the in-use bit is invalid.
173 *
174 * The szone maintains an array of NUM_TINY_SLOTS freelists, each of which is used to hold
175 * free objects of the corresponding quantum size.
176 *
177 * A free block is laid out depending on its size, in order to fit all free
178 * blocks in 16 bytes, on both 32 and 64 bit platforms. One quantum blocks do
179 * not store their size in the block, instead relying on the header information
180 * to determine their size. Blocks of two or more quanta have room to store
181 * their size in the block, and store it both after the 'next' pointer, and in
182 * the last 2 bytes of the block.
183 *
184 * 1-quantum block
185 * Offset (32-bit mode) (64-bit mode)
186 * 0x0 0x0 : previous
187 * 0x4 0x08 : next
188 * end end
189 *
190 * >1-quantum block
191 * Offset (32-bit mode) (64-bit mode)
192 * 0x0 0x0 : previous
193 * 0x4 0x08 : next
194 * 0x8 0x10 : size (in quantum counts)
195 * end - 2 end - 2 : size (in quantum counts)
196 * end end
197 *
198 * All fields are pointer-sized, except for the size which is an unsigned short.
199 *
200 */
201
202 #define SHIFT_TINY_QUANTUM 4 // Required for AltiVec
203 #define TINY_QUANTUM (1 << SHIFT_TINY_QUANTUM)
204
205 #define FOLLOWING_TINY_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_TINY_QUANTUM))
206
207 #ifdef __LP64__
208 #define NUM_TINY_SLOTS 64 // number of slots for free-lists
209 #else
210 #define NUM_TINY_SLOTS 32 // number of slots for free-lists
211 #endif
212
213 #define NUM_TINY_BLOCKS 64520
214 #define SHIFT_TINY_CEIL_BLOCKS 16 // ceil(log2(NUM_TINY_BLOCKS))
215 #define NUM_TINY_CEIL_BLOCKS (1 << SHIFT_TINY_CEIL_BLOCKS)
216 #define TINY_BLOCKS_ALIGN (SHIFT_TINY_CEIL_BLOCKS + SHIFT_TINY_QUANTUM) // 20
217
218 #define TINY_ENTROPY_BITS 15
219 #define TINY_ENTROPY_MASK ((1 << TINY_ENTROPY_BITS) - 1)
220
221 /*
222 * Avoid having so much entropy that the end of a valid tiny allocation
223 * might overrun the end of the tiny region.
224 */
225 #if TINY_ENTROPY_MASK + NUM_TINY_SLOTS > NUM_TINY_BLOCKS
226 #error Too many entropy bits for tiny region requested
227 #endif
228
229 /*
230 * Enough room for the data, followed by the bit arrays (2-bits per block)
231 * plus rounding to the nearest page.
232 */
233 #define CEIL_NUM_TINY_BLOCKS_WORDS (((NUM_TINY_BLOCKS + 31) & ~31) >> 5)
234 #define TINY_METADATA_SIZE (sizeof(region_trailer_t) + sizeof(tiny_header_inuse_pair_t) * CEIL_NUM_TINY_BLOCKS_WORDS)
235 #define TINY_REGION_SIZE \
236 ((NUM_TINY_BLOCKS * TINY_QUANTUM + TINY_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
237
238 #define TINY_METADATA_START (NUM_TINY_BLOCKS * TINY_QUANTUM)
239
240 /*
241 * Beginning and end pointers for a region's heap.
242 */
243 #define TINY_REGION_ADDRESS(region) ((void *)(region))
244 #define TINY_REGION_END(region) ((void *)(((uintptr_t)(region)) + (NUM_TINY_BLOCKS * TINY_QUANTUM)))
245
246 /*
247 * Locate the heap base for a pointer known to be within a tiny region.
248 */
249 #define TINY_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << TINY_BLOCKS_ALIGN) - 1)))
250
251 /*
252 * Convert between byte and msize units.
253 */
254 #define TINY_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_TINY_QUANTUM)
255 #define TINY_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_TINY_QUANTUM)
256
257 #ifdef __LP64__
258 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[8])
259 #else
260 # define TINY_FREE_SIZE(ptr) (((msize_t *)(ptr))[4])
261 #endif
262 #define TINY_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
263
264 /*
265 * Layout of a tiny region
266 */
267 typedef uint32_t tiny_block_t[4]; // assert(TINY_QUANTUM == sizeof(tiny_block_t))
268
269 typedef struct tiny_header_inuse_pair
270 {
271 uint32_t header;
272 uint32_t inuse;
273 } tiny_header_inuse_pair_t;
274
275 typedef struct region_trailer
276 {
277 struct region_trailer *prev;
278 struct region_trailer *next;
279 boolean_t recirc_suitable;
280 boolean_t failedREUSE;
281 volatile int pinned_to_depot;
282 unsigned bytes_used;
283 mag_index_t mag_index;
284 } region_trailer_t;
285
286 typedef struct tiny_region
287 {
288 tiny_block_t blocks[NUM_TINY_BLOCKS];
289
290 region_trailer_t trailer;
291
292 // The interleaved bit arrays comprising the header and inuse bitfields.
293 // The unused bits of each component in the last pair will be initialized to sentinel values.
294 tiny_header_inuse_pair_t pairs[CEIL_NUM_TINY_BLOCKS_WORDS];
295
296 uint8_t pad[TINY_REGION_SIZE - (NUM_TINY_BLOCKS * sizeof(tiny_block_t)) - TINY_METADATA_SIZE];
297 } *tiny_region_t;
298
299 /*
300 * Per-region meta data for tiny allocator
301 */
302 #define REGION_TRAILER_FOR_TINY_REGION(r) (&(((tiny_region_t)(r))->trailer))
303 #define MAGAZINE_INDEX_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->mag_index)
304 #define BYTES_USED_FOR_TINY_REGION(r) (REGION_TRAILER_FOR_TINY_REGION(r)->bytes_used)
305
306 /*
307 * Locate the block header for a pointer known to be within a tiny region.
308 */
309 #define TINY_BLOCK_HEADER_FOR_PTR(_p) ((void *)&(((tiny_region_t)TINY_REGION_FOR_PTR(_p))->pairs))
310
311 /*
312 * Locate the inuse map for a given block header pointer.
313 */
314 #define TINY_INUSE_FOR_HEADER(_h) ((void *)&(((tiny_header_inuse_pair_t *)(_h))->inuse))
315
316 /*
317 * Compute the bitmap index for a pointer known to be within a tiny region.
318 */
319 #define TINY_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_TINY_QUANTUM) & (NUM_TINY_CEIL_BLOCKS - 1))
320
321 #define TINY_CACHE 1 // This governs a last-free cache of 1 that bypasses the free-list
322
323 #if ! TINY_CACHE
324 #warning TINY_CACHE turned off
325 #endif
326
327 #define TINY_REGION_PAYLOAD_BYTES (NUM_TINY_BLOCKS * TINY_QUANTUM)
328
329 /********************* DEFINITIONS for small ************************/
330
331 /*
332 * Memory in the Small range is allocated from regions (heaps) pointed to by the szone's hashed_regions
333 * pointer.
334 *
335 * Each region is laid out as a heap, followed by the metadata array, all within an 8MB (2^23) block.
336 * The array is arranged as an array of shorts, one for each SMALL_QUANTUM in the heap.
337 * This means there are 16320 512-blocks and the array is 16320*2 bytes, which totals 8388480, leaving
338 * 128 bytes unused.
339 *
340 * The MSB of each short is set for the first quantum in a free block. The low 15 bits encode the
341 * block size (in SMALL_QUANTUM units), or are zero if the quantum is not the first in a block.
342 *
343 * The szone maintains an array of 32 freelists, each of which is used to hold free objects
344 * of the corresponding quantum size.
345 *
346 * A free block is laid out as:
347 *
348 * Offset (32-bit mode) (64-bit mode)
349 * 0x0 0x0 : previous
350 * 0x4 0x08 : next
351 * 0x8 0x10 : size (in quantum counts)
352 * end - 2 end - 2 : size (in quantum counts)
353 * end end
354 *
355 * All fields are pointer-sized, except for the size which is an unsigned short.
356 *
357 */
358
359 #define SMALL_IS_FREE (1 << 15)
360
361 #define SHIFT_SMALL_QUANTUM (SHIFT_TINY_QUANTUM + 5) // 9
362 #define SMALL_QUANTUM (1 << SHIFT_SMALL_QUANTUM) // 512 bytes
363
364 #define FOLLOWING_SMALL_PTR(ptr,msize) (((unsigned char *)(ptr)) + ((msize) << SHIFT_SMALL_QUANTUM))
365
366 /*
367 * The number of slots in the free-list for small blocks. To avoid going to
368 * vm system as often on large memory machines, increase the number of free list
369 * spots above some amount of RAM installed in the system.
370 */
371 #define NUM_SMALL_SLOTS 32
372 #define NUM_SMALL_SLOTS_LARGEMEM 256
373 #define SMALL_BITMAP_WORDS 8
374
375 /*
376 * We can only represent up to 1<<15 for msize; but we choose to stay even below that to avoid the
377 * convention msize=0 => msize = (1<<15)
378 */
379 #define NUM_SMALL_BLOCKS 16320
380 #define SHIFT_SMALL_CEIL_BLOCKS 14 // ceil(log2(NUM_SMALL_BLOCKs))
381 #define NUM_SMALL_CEIL_BLOCKS (1 << SHIFT_SMALL_CEIL_BLOCKS)
382 #define SMALL_BLOCKS_ALIGN (SHIFT_SMALL_CEIL_BLOCKS + SHIFT_SMALL_QUANTUM) // 23
383
384 #define SMALL_ENTROPY_BITS 13
385 #define SMALL_ENTROPY_MASK ((1 << SMALL_ENTROPY_BITS) - 1)
386
387 /*
388 * Avoid having so much entropy that the end of a valid small allocation
389 * might overrun the end of the small region.
390 */
391 #if SMALL_ENTROPY_MASK + NUM_SMALL_SLOTS > NUM_SMALL_BLOCKS
392 #error Too many entropy bits for small region requested
393 #endif
394
395 #define SMALL_METADATA_SIZE (sizeof(region_trailer_t) + NUM_SMALL_BLOCKS * sizeof(msize_t))
396 #define SMALL_REGION_SIZE \
397 ((NUM_SMALL_BLOCKS * SMALL_QUANTUM + SMALL_METADATA_SIZE + vm_page_size - 1) & ~ (vm_page_size - 1))
398
399 #define SMALL_METADATA_START (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
400
401 /*
402 * Beginning and end pointers for a region's heap.
403 */
404 #define SMALL_REGION_ADDRESS(region) ((unsigned char *)region)
405 #define SMALL_REGION_END(region) (SMALL_REGION_ADDRESS(region) + (NUM_SMALL_BLOCKS * SMALL_QUANTUM))
406
407 /*
408 * Locate the heap base for a pointer known to be within a small region.
409 */
410 #define SMALL_REGION_FOR_PTR(_p) ((void *)((uintptr_t)(_p) & ~((1 << SMALL_BLOCKS_ALIGN) - 1)))
411
412 /*
413 * Convert between byte and msize units.
414 */
415 #define SMALL_BYTES_FOR_MSIZE(_m) ((_m) << SHIFT_SMALL_QUANTUM)
416 #define SMALL_MSIZE_FOR_BYTES(_b) ((_b) >> SHIFT_SMALL_QUANTUM)
417
418 #define SMALL_PREVIOUS_MSIZE(ptr) ((msize_t *)(ptr))[-1]
419
420 /*
421 * Layout of a small region
422 */
423 typedef uint32_t small_block_t[SMALL_QUANTUM/sizeof(uint32_t)];
424
425 typedef struct small_region
426 {
427 small_block_t blocks[NUM_SMALL_BLOCKS];
428
429 region_trailer_t trailer;
430
431 msize_t small_meta_words[NUM_SMALL_BLOCKS];
432
433 uint8_t pad[SMALL_REGION_SIZE - (NUM_SMALL_BLOCKS * sizeof(small_block_t)) - SMALL_METADATA_SIZE];
434 } *small_region_t;
435
436 /*
437 * Per-region meta data for small allocator
438 */
439 #define REGION_TRAILER_FOR_SMALL_REGION(r) (&(((small_region_t)(r))->trailer))
440 #define MAGAZINE_INDEX_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->mag_index)
441 #define BYTES_USED_FOR_SMALL_REGION(r) (REGION_TRAILER_FOR_SMALL_REGION(r)->bytes_used)
442
443 /*
444 * Locate the metadata base for a pointer known to be within a small region.
445 */
446 #define SMALL_META_HEADER_FOR_PTR(_p) (((small_region_t)SMALL_REGION_FOR_PTR(_p))->small_meta_words)
447
448 /*
449 * Compute the metadata index for a pointer known to be within a small region.
450 */
451 #define SMALL_META_INDEX_FOR_PTR(_p) (((uintptr_t)(_p) >> SHIFT_SMALL_QUANTUM) & (NUM_SMALL_CEIL_BLOCKS - 1))
452
453 /*
454 * Find the metadata word for a pointer known to be within a small region.
455 */
456 #define SMALL_METADATA_FOR_PTR(_p) (SMALL_META_HEADER_FOR_PTR(_p) + SMALL_META_INDEX_FOR_PTR(_p))
457
458 /*
459 * Determine whether a pointer known to be within a small region points to memory which is free.
460 */
461 #define SMALL_PTR_IS_FREE(_p) (*SMALL_METADATA_FOR_PTR(_p) & SMALL_IS_FREE)
462
463 /*
464 * Extract the msize value for a pointer known to be within a small region.
465 */
466 #define SMALL_PTR_SIZE(_p) (*SMALL_METADATA_FOR_PTR(_p) & ~SMALL_IS_FREE)
467
468 #define SMALL_CACHE 1
469 #if !SMALL_CACHE
470 #warning SMALL_CACHE turned off
471 #endif
472
473 #define SMALL_REGION_PAYLOAD_BYTES (NUM_SMALL_BLOCKS * SMALL_QUANTUM)
474
475 /************************* DEFINITIONS for large ****************************/
476
477 #define LARGE_THRESHOLD (15 * 1024) // strictly above this use "large"
478 #define LARGE_THRESHOLD_LARGEMEM (127 * 1024)
479
480 #if (LARGE_THRESHOLD > NUM_SMALL_SLOTS * SMALL_QUANTUM)
481 #error LARGE_THRESHOLD should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
482 #endif
483
484 #if (LARGE_THRESHOLD_LARGEMEM > NUM_SMALL_SLOTS_LARGEMEM * SMALL_QUANTUM)
485 #error LARGE_THRESHOLD_LARGEMEM should always be less than NUM_SMALL_SLOTS * SMALL_QUANTUM
486 #endif
487
488 /*
489 * When all memory is touched after a copy, vm_copy() is always a lose
490 * But if the memory is only read, vm_copy() wins over memmove() at 3 or 4 pages
491 * (on a G3/300MHz)
492 *
493 * This must be larger than LARGE_THRESHOLD
494 */
495 #define VM_COPY_THRESHOLD (40 * 1024)
496 #define VM_COPY_THRESHOLD_LARGEMEM (128 * 1024)
497
498 typedef struct {
499 vm_address_t address;
500 vm_size_t size;
501 boolean_t did_madvise_reusable;
502 } large_entry_t;
503
504 #if !TARGET_OS_EMBEDDED
505 #define LARGE_CACHE 1
506 #else
507 #define LARGE_CACHE 0
508 #endif
509 #if !LARGE_CACHE
510 #warning LARGE_CACHE turned off
511 #endif
512 #if defined(__LP64__)
513 #define LARGE_ENTRY_CACHE_SIZE 16
514 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x80000000) /* 2Gb */
515 #else
516 #define LARGE_ENTRY_CACHE_SIZE 8
517 #define LARGE_CACHE_SIZE_LIMIT ((vm_size_t)0x02000000) /* 32Mb */
518 #endif
519 #define LARGE_CACHE_SIZE_ENTRY_LIMIT (LARGE_CACHE_SIZE_LIMIT/LARGE_ENTRY_CACHE_SIZE)
520
521 #define SZONE_FLOTSAM_THRESHOLD_LOW (1024 * 512)
522 #define SZONE_FLOTSAM_THRESHOLD_HIGH (1024 * 1024)
523
524 /*******************************************************************************
525 * Definitions for region hash
526 ******************************************************************************/
527
528 typedef void * region_t;
529 typedef region_t * rgnhdl_t; /* A pointer into hashed_regions array. */
530
531 #define INITIAL_NUM_REGIONS_SHIFT 6 // log2(INITIAL_NUM_REGIONS)
532 #define INITIAL_NUM_REGIONS (1 << INITIAL_NUM_REGIONS_SHIFT) // Must be a power of 2!
533 #define HASHRING_OPEN_ENTRY ((region_t) 0) // Initial value and sentinel marking end of collision chain
534 #define HASHRING_REGION_DEALLOCATED ((region_t)-1) // Region at this slot reclaimed by OS
535 #define HASH_BLOCKS_ALIGN TINY_BLOCKS_ALIGN // MIN( TINY_BLOCKS_ALIGN, SMALL_BLOCKS_ALIGN, ... )
536
537 typedef struct region_hash_generation {
538 size_t num_regions_allocated;
539 size_t num_regions_allocated_shift; // log2(num_regions_allocated)
540 region_t *hashed_regions; // hashed by location
541 struct region_hash_generation *nextgen;
542 } region_hash_generation_t;
543
544 /*******************************************************************************
545 * Per-processor magazine for tiny and small allocators
546 ******************************************************************************/
547
548 typedef struct { // vm_allocate()'d, so the array of magazines is page-aligned to begin with.
549 // Take magazine_lock first, Depot lock when needed for recirc, then szone->{tiny,small}_regions_lock when needed for alloc
550 pthread_lock_t magazine_lock CACHE_ALIGN;
551 // Protection for the crtical section that does allocate_pages outside the magazine_lock
552 volatile boolean_t alloc_underway;
553
554 // One element deep "death row", optimizes malloc/free/malloc for identical size.
555 void *mag_last_free; // low SHIFT_{TINY,SMALL}_QUANTUM bits indicate the msize
556 region_t mag_last_free_rgn; // holds the region for mag_last_free
557
558 free_list_t *mag_free_list[256]; // assert( 256 >= MAX( NUM_TINY_SLOTS, NUM_SMALL_SLOTS_LARGEMEM ))
559 unsigned mag_bitmap[8]; // assert( sizeof(mag_bitmap) << 3 >= sizeof(mag_free_list)/sizeof(free_list_t) )
560
561 // the first and last free region in the last block are treated as big blocks in use that are not accounted for
562 size_t mag_bytes_free_at_end;
563 size_t mag_bytes_free_at_start;
564 region_t mag_last_region; // Valid iff mag_bytes_free_at_end || mag_bytes_free_at_start > 0
565
566 // bean counting ...
567 unsigned mag_num_objects;
568 size_t mag_num_bytes_in_objects;
569 size_t num_bytes_in_magazine;
570
571 // recirculation list -- invariant: all regions owned by this magazine that meet the emptiness criteria
572 // are located nearer to the head of the list than any region that doesn't satisfy that criteria.
573 // Doubly linked list for efficient extraction.
574 unsigned recirculation_entries;
575 region_trailer_t *firstNode;
576 region_trailer_t *lastNode;
577
578 #if __LP64__
579 uint64_t pad[48]; // So sizeof(magazine_t) is 2560 bytes. FIXME: assert this at compile time
580 #else
581 uint32_t pad[12]; // So sizeof(magazine_t) is 1280 bytes. FIXME: assert this at compile time
582 #endif
583 } magazine_t;
584
585 #define TINY_MAX_MAGAZINES 32 /* MUST BE A POWER OF 2! */
586 #define TINY_MAGAZINE_PAGED_SIZE \
587 (((sizeof(magazine_t) * (TINY_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
588 ~ (vm_page_size - 1)) /* + 1 for the Depot */
589
590 #define SMALL_MAX_MAGAZINES 32 /* MUST BE A POWER OF 2! */
591 #define SMALL_MAGAZINE_PAGED_SIZE \
592 (((sizeof(magazine_t) * (SMALL_MAX_MAGAZINES + 1)) + vm_page_size - 1) &\
593 ~ (vm_page_size - 1)) /* + 1 for the Depot */
594
595 #define DEPOT_MAGAZINE_INDEX -1
596
597 /****************************** zone itself ***********************************/
598
599 /*
600 * Note that objects whose adddress are held in pointers here must be pursued
601 * individually in the {tiny,small}_in_use_enumeration() routines. See for
602 * example the treatment of region_hash_generation and tiny_magazines below.
603 */
604
605 typedef struct szone_s { // vm_allocate()'d, so page-aligned to begin with.
606 malloc_zone_t basic_zone; // first page will be given read-only protection
607 uint8_t pad[vm_page_size - sizeof(malloc_zone_t)];
608
609 pthread_key_t cpu_id_key; // remainder of structure is R/W (contains no function pointers)
610 unsigned debug_flags;
611 void *log_address;
612
613 /* Regions for tiny objects */
614 pthread_lock_t tiny_regions_lock CACHE_ALIGN;
615 size_t num_tiny_regions;
616 size_t num_tiny_regions_dealloc;
617 region_hash_generation_t *tiny_region_generation;
618 region_hash_generation_t trg[2];
619
620 int num_tiny_magazines;
621 unsigned num_tiny_magazines_mask;
622 int num_tiny_magazines_mask_shift;
623 magazine_t *tiny_magazines; // array of per-processor magazines
624
625 #if TARGET_OS_EMBEDDED
626 uintptr_t last_tiny_advise;
627 #endif
628
629 /* Regions for small objects */
630 pthread_lock_t small_regions_lock CACHE_ALIGN;
631 size_t num_small_regions;
632 size_t num_small_regions_dealloc;
633 region_hash_generation_t *small_region_generation;
634 region_hash_generation_t srg[2];
635
636 unsigned num_small_slots; // determined by physmem size
637
638 int num_small_magazines;
639 unsigned num_small_magazines_mask;
640 int num_small_magazines_mask_shift;
641 magazine_t *small_magazines; // array of per-processor magazines
642
643 #if TARGET_OS_EMBEDDED
644 uintptr_t last_small_advise;
645 #endif
646
647 /* large objects: all the rest */
648 pthread_lock_t large_szone_lock CACHE_ALIGN; // One customer at a time for large
649 unsigned num_large_objects_in_use;
650 unsigned num_large_entries;
651 large_entry_t *large_entries; // hashed by location; null entries don't count
652 size_t num_bytes_in_large_objects;
653
654 #if LARGE_CACHE
655 int large_entry_cache_oldest;
656 int large_entry_cache_newest;
657 large_entry_t large_entry_cache[LARGE_ENTRY_CACHE_SIZE]; // "death row" for large malloc/free
658 boolean_t large_legacy_reset_mprotect;
659 size_t large_entry_cache_reserve_bytes;
660 size_t large_entry_cache_reserve_limit;
661 size_t large_entry_cache_bytes; // total size of death row, bytes
662 #endif
663
664 /* flag and limits pertaining to altered malloc behavior for systems with
665 large amounts of physical memory */
666 unsigned is_largemem;
667 unsigned large_threshold;
668 unsigned vm_copy_threshold;
669
670 /* security cookie */
671 uintptr_t cookie;
672
673 /* Initial region list */
674 region_t initial_tiny_regions[INITIAL_NUM_REGIONS];
675 region_t initial_small_regions[INITIAL_NUM_REGIONS];
676
677 /* The purgeable zone constructed by create_purgeable_zone() would like to hand off tiny and small
678 * allocations to the default scalable zone. Record the latter as the "helper" zone here. */
679 struct szone_s *helper_zone;
680
681 boolean_t flotsam_enabled;
682 } szone_t;
683
684 #define SZONE_PAGED_SIZE ((sizeof(szone_t) + vm_page_size - 1) & ~ (vm_page_size - 1))
685
686 #if DEBUG_MALLOC || DEBUG_CLIENT
687 static void szone_sleep(void);
688 #endif
689 __private_extern__ void malloc_error_break(void);
690
691 // msg prints after fmt, ...
692 static NOINLINE void szone_error(szone_t *szone, int is_corruption, const char *msg, const void *ptr, const char *fmt, ...)
693 __printflike(5, 6);
694
695 static void protect(void *address, size_t size, unsigned protection, unsigned debug_flags);
696 static void *allocate_pages(szone_t *szone, size_t size, unsigned char align, unsigned debug_flags,
697 int vm_page_label);
698 static void *allocate_pages_securely(szone_t *szone, size_t size, unsigned char align,
699 int vm_page_label);
700 static void deallocate_pages(szone_t *szone, void *addr, size_t size, unsigned debug_flags);
701 #if TARGET_OS_EMBEDDED
702 static int madvise_free_range(szone_t *szone, region_t r, uintptr_t pgLo, uintptr_t pgHi, uintptr_t *last);
703 #else
704 static int madvise_free_range(szone_t *szone, region_t r, uintptr_t pgLo, uintptr_t pgHi);
705 #endif
706 static kern_return_t _szone_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr);
707
708 static INLINE mag_index_t mag_get_thread_index(szone_t *szone) ALWAYSINLINE;
709 static magazine_t *mag_lock_zine_for_region_trailer(szone_t *szone, magazine_t *magazines, region_trailer_t *trailer,
710 mag_index_t mag_index);
711
712 static INLINE rgnhdl_t hash_lookup_region_no_lock(region_t *regions, size_t num_entries, size_t shift, region_t r)
713 ALWAYSINLINE;
714 static void hash_region_insert_no_lock(region_t *regions, size_t num_entries, size_t shift, region_t r);
715 static region_t *hash_regions_alloc_no_lock(szone_t *szone, size_t num_entries);
716 static region_t *hash_regions_grow_no_lock(szone_t *szone, region_t *regions, size_t old_size,
717 size_t *mutable_shift, size_t *new_size);
718
719 static INLINE uintptr_t free_list_gen_checksum(uintptr_t ptr) ALWAYSINLINE;
720 static INLINE uintptr_t free_list_checksum_ptr(szone_t *szone, void *p) ALWAYSINLINE;
721 static INLINE void *free_list_unchecksum_ptr(szone_t *szone, ptr_union *ptr) ALWAYSINLINE;
722 static unsigned free_list_count(szone_t *szone, free_list_t *ptr);
723
724 static INLINE void recirc_list_extract(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node) ALWAYSINLINE;
725 static INLINE void recirc_list_splice_last(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node) ALWAYSINLINE;
726 static INLINE void recirc_list_splice_first(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node) ALWAYSINLINE;
727
728 static INLINE void BITARRAY_SET(uint32_t *bits, msize_t index) ALWAYSINLINE;
729 static INLINE void BITARRAY_CLR(uint32_t *bits, msize_t index) ALWAYSINLINE;
730 static INLINE boolean_t BITARRAY_BIT(uint32_t *bits, msize_t index) ALWAYSINLINE;
731
732 static msize_t get_tiny_free_size(const void *ptr);
733 static msize_t get_tiny_previous_free_msize(const void *ptr);
734 static INLINE msize_t get_tiny_meta_header(const void *ptr, boolean_t *is_free) ALWAYSINLINE;
735 static INLINE void set_tiny_meta_header_in_use(const void *ptr, msize_t msize) ALWAYSINLINE;
736 static INLINE void set_tiny_meta_header_in_use_1(const void *ptr) ALWAYSINLINE;
737 static INLINE void set_tiny_meta_header_middle(const void *ptr) ALWAYSINLINE;
738 static INLINE void set_tiny_meta_header_free(const void *ptr, msize_t msize) ALWAYSINLINE;
739 static INLINE boolean_t tiny_meta_header_is_free(const void *ptr) ALWAYSINLINE;
740 static INLINE void *tiny_previous_preceding_free(void *ptr, msize_t *prev_msize) ALWAYSINLINE;
741
742 static void tiny_free_list_add_ptr(szone_t *szone, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize);
743 static void tiny_free_list_remove_ptr(szone_t *szone, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize);
744 static INLINE region_t tiny_region_for_ptr_no_lock(szone_t *szone, const void *ptr) ALWAYSINLINE;
745
746 static void tiny_finalize_region(szone_t *szone, magazine_t *tiny_mag_ptr);
747 static int tiny_free_detach_region(szone_t *szone, magazine_t *tiny_mag_ptr, region_t r);
748 static size_t tiny_free_reattach_region(szone_t *szone, magazine_t *tiny_mag_ptr, region_t r);
749 static void tiny_free_scan_madvise_free(szone_t *szone, magazine_t *depot_ptr, region_t r);
750 static region_t tiny_free_try_depot_unmap_no_lock(szone_t *szone, magazine_t *depot_ptr, region_trailer_t *node);
751 static boolean_t tiny_free_do_recirc_to_depot(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index);
752 static region_t tiny_find_msize_region(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, msize_t msize);
753 static boolean_t tiny_get_region_from_depot(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, msize_t msize);
754
755 static INLINE boolean_t tiny_free_no_lock(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, region_t region,
756 void *ptr, msize_t msize) ALWAYSINLINE;
757 static void *tiny_malloc_from_region_no_lock(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index,
758 msize_t msize, void *fresh_region);
759 static boolean_t tiny_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size);
760 static boolean_t tiny_check_region(szone_t *szone, region_t region);
761 static kern_return_t tiny_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone,
762 memory_reader_t reader, vm_range_recorder_t recorder);
763 static void *tiny_malloc_from_free_list(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index,
764 msize_t msize);
765 static INLINE void *tiny_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested) ALWAYSINLINE;
766 static INLINE void free_tiny(szone_t *szone, void *ptr, region_t tiny_region, size_t known_size) ALWAYSINLINE;
767 static void print_tiny_free_list(szone_t *szone);
768 static void print_tiny_region(boolean_t verbose, region_t region, size_t bytes_at_start, size_t bytes_at_end);
769 static boolean_t tiny_free_list_check(szone_t *szone, grain_t slot);
770
771 static INLINE void small_meta_header_set_is_free(msize_t *meta_headers, unsigned index, msize_t msize) ALWAYSINLINE;
772 static INLINE void small_meta_header_set_in_use(msize_t *meta_headers, msize_t index, msize_t msize) ALWAYSINLINE;
773 static INLINE void small_meta_header_set_middle(msize_t *meta_headers, msize_t index) ALWAYSINLINE;
774 static void small_free_list_add_ptr(szone_t *szone, magazine_t *small_mag_ptr, void *ptr, msize_t msize);
775 static void small_free_list_remove_ptr(szone_t *szone, magazine_t *small_mag_ptr, void *ptr, msize_t msize);
776 static INLINE region_t small_region_for_ptr_no_lock(szone_t *szone, const void *ptr) ALWAYSINLINE;
777
778 static void small_finalize_region(szone_t *szone, magazine_t *small_mag_ptr);
779 static int small_free_detach_region(szone_t *szone, magazine_t *small_mag_ptr, region_t r);
780 static size_t small_free_reattach_region(szone_t *szone, magazine_t *small_mag_ptr, region_t r);
781 static void small_free_scan_madvise_free(szone_t *szone, magazine_t *depot_ptr, region_t r);
782 static region_t small_free_try_depot_unmap_no_lock(szone_t *szone, magazine_t *depot_ptr, region_trailer_t *node);
783 static boolean_t small_free_do_recirc_to_depot(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index);
784 static region_t small_find_msize_region(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, msize_t msize);
785 static boolean_t small_get_region_from_depot(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, msize_t msize);
786 static INLINE boolean_t small_free_no_lock(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, region_t region,
787 void *ptr, msize_t msize) ALWAYSINLINE;
788 static void *small_malloc_from_region_no_lock(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index,
789 msize_t msize, void *fresh_region);
790 static boolean_t small_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size);
791 static boolean_t small_check_region(szone_t *szone, region_t region);
792 static kern_return_t small_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone,
793 memory_reader_t reader, vm_range_recorder_t recorder);
794 static void *small_malloc_from_free_list(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index,
795 msize_t msize);
796 static INLINE void *small_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested) ALWAYSINLINE;
797 static INLINE void free_small(szone_t *szone, void *ptr, region_t small_region, size_t known_size) ALWAYSINLINE;
798 static void print_small_free_list(szone_t *szone);
799 static void print_small_region(szone_t *szone, boolean_t verbose, region_t region, size_t bytes_at_start, size_t bytes_at_end);
800 static boolean_t small_free_list_check(szone_t *szone, grain_t grain);
801
802 #if DEBUG_MALLOC
803 static void large_debug_print(szone_t *szone);
804 #endif
805 static large_entry_t *large_entry_for_pointer_no_lock(szone_t *szone, const void *ptr);
806 static void large_entry_insert_no_lock(szone_t *szone, large_entry_t range);
807 static INLINE void large_entries_rehash_after_entry_no_lock(szone_t *szone, large_entry_t *entry) ALWAYSINLINE;
808 static INLINE large_entry_t *large_entries_alloc_no_lock(szone_t *szone, unsigned num) ALWAYSINLINE;
809 static void large_entries_free_no_lock(szone_t *szone, large_entry_t *entries, unsigned num,
810 vm_range_t *range_to_deallocate);
811 static large_entry_t *large_entries_grow_no_lock(szone_t *szone, vm_range_t *range_to_deallocate);
812 static vm_range_t large_entry_free_no_lock(szone_t *szone, large_entry_t *entry);
813 static NOINLINE kern_return_t large_in_use_enumerator(task_t task, void *context,
814 unsigned type_mask, vm_address_t large_entries_address,
815 unsigned num_entries, memory_reader_t reader,
816 vm_range_recorder_t recorder);
817 static void *large_malloc(szone_t *szone, size_t num_pages, unsigned char alignment, boolean_t cleared_requested);
818 static NOINLINE void free_large(szone_t *szone, void *ptr);
819 static INLINE int large_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size) ALWAYSINLINE;
820
821 /*
822 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
823 */
824 static NOINLINE void szone_free(szone_t *szone, void *ptr);
825 static NOINLINE void *szone_malloc_should_clear(szone_t *szone, size_t size, boolean_t cleared_requested);
826 static NOINLINE void *szone_malloc(szone_t *szone, size_t size);
827 static NOINLINE void *szone_calloc(szone_t *szone, size_t num_items, size_t size);
828 static NOINLINE void *szone_valloc(szone_t *szone, size_t size);
829 static NOINLINE size_t szone_size_try_large(szone_t *szone, const void *ptr);
830 static NOINLINE size_t szone_size(szone_t *szone, const void *ptr);
831 static NOINLINE void *szone_realloc(szone_t *szone, void *ptr, size_t new_size);
832 static NOINLINE void *szone_memalign(szone_t *szone, size_t alignment, size_t size);
833 static NOINLINE void szone_free_definite_size(szone_t *szone, void *ptr, size_t size);
834 static NOINLINE unsigned szone_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count);
835 static NOINLINE void szone_batch_free(szone_t *szone, void **to_be_freed, unsigned count);
836 static void szone_destroy(szone_t *szone);
837 static NOINLINE size_t szone_good_size(szone_t *szone, size_t size);
838
839 static NOINLINE boolean_t szone_check_all(szone_t *szone, const char *function);
840 static boolean_t szone_check(szone_t *szone);
841 static kern_return_t szone_ptr_in_use_enumerator(task_t task, void *context,
842 unsigned type_mask, vm_address_t zone_address,
843 memory_reader_t reader, vm_range_recorder_t recorder);
844 static NOINLINE void szone_print(szone_t *szone, boolean_t verbose);
845 static void szone_log(malloc_zone_t *zone, void *log_address);
846 static void szone_force_lock(szone_t *szone);
847 static void szone_force_unlock(szone_t *szone);
848 static boolean_t szone_locked(szone_t *szone);
849
850 static void szone_statistics(szone_t *szone, malloc_statistics_t *stats);
851
852 static void purgeable_free(szone_t *szone, void *ptr);
853 static void *purgeable_malloc(szone_t *szone, size_t size);
854 static void *purgeable_calloc(szone_t *szone, size_t num_items, size_t size);
855 static void *purgeable_valloc(szone_t *szone, size_t size);
856 static size_t purgeable_size(szone_t *szone, const void *ptr);
857 static void *purgeable_realloc(szone_t *szone, void *ptr, size_t new_size);
858 static void *purgeable_memalign(szone_t *szone, size_t alignment, size_t size);
859 static void purgeable_free_definite_size(szone_t *szone, void *ptr, size_t size);
860 static unsigned purgeable_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count);
861 static void purgeable_batch_free(szone_t *szone, void **to_be_freed, unsigned count);
862 static void purgeable_destroy(szone_t *szone);
863 static size_t purgeable_good_size(szone_t *szone, size_t size);
864
865 static boolean_t purgeable_check(szone_t *szone);
866 static kern_return_t purgeable_ptr_in_use_enumerator(task_t task, void *context,
867 unsigned type_mask, vm_address_t zone_address,
868 memory_reader_t reader, vm_range_recorder_t recorder);
869 static void purgeable_print(szone_t *szone, boolean_t verbose);
870 static void purgeable_log(malloc_zone_t *zone, void *log_address);
871 static void purgeable_force_lock(szone_t *szone);
872 static void purgeable_force_unlock(szone_t *szone);
873 static boolean_t purgeable_locked(szone_t *szone);
874
875 static void purgeable_statistics(szone_t *szone, malloc_statistics_t *stats);
876
877 static void *frozen_malloc(szone_t *zone, size_t new_size);
878 static void *frozen_calloc(szone_t *zone, size_t num_items, size_t size);
879 static void *frozen_valloc(szone_t *zone, size_t new_size);
880 static void *frozen_realloc(szone_t *zone, void *ptr, size_t new_size);
881 static void frozen_free(szone_t *zone, void *ptr);
882 static void frozen_destroy(szone_t *zone);
883
884 static volatile uintptr_t entropic_address = 0;
885 static volatile uintptr_t entropic_limit = 0;
886 #define ENTROPIC_KABILLION 0x10000000 /* 256Mb */
887
888 __private_extern__ uint64_t malloc_entropy[2];
889
890 #define SZONE_LOCK(szone) \
891 do { \
892 LOCK(szone->large_szone_lock); \
893 } while (0)
894
895 #define SZONE_UNLOCK(szone) \
896 do { \
897 UNLOCK(szone->large_szone_lock); \
898 } while (0)
899
900 #define SZONE_TRY_LOCK(szone) \
901 TRY_LOCK(szone->large_szone_lock);
902
903 #define SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr) \
904 do { \
905 LOCK(mag_ptr->magazine_lock); \
906 } while(0)
907
908 #define SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr) \
909 do { \
910 UNLOCK(mag_ptr->magazine_lock); \
911 } while(0)
912
913 #define SZONE_MAGAZINE_PTR_TRY_LOCK(szone, mag_ptr) \
914 TRY_LOCK(mag_ptr->magazine_lock);
915
916 #if DEBUG_MALLOC
917 # define LOG(szone,ptr) \
918 (szone->log_address && (((uintptr_t)szone->log_address == -1) || \
919 (szone->log_address == (void *)(ptr))))
920 #else
921 # define LOG(szone,ptr) 0
922 #endif
923
924 #if DEBUG_MALLOC || DEBUG_CLIENT
925 # define CHECK(szone,fun) \
926 if ((szone)->debug_flags & CHECK_REGIONS) \
927 szone_check_all(szone, fun)
928 #else
929 # define CHECK(szone,fun) \
930 do {} while (0)
931 #endif
932
933 /********************* VERY LOW LEVEL UTILITIES ************************/
934
935 static void
936 szone_sleep(void)
937 {
938 if (getenv("MallocErrorStop")) {
939 _malloc_printf(ASL_LEVEL_NOTICE, "*** sending SIGSTOP to help debug\n");
940 kill(getpid(), SIGSTOP);
941 } else if (getenv("MallocErrorSleep")) {
942 _malloc_printf(ASL_LEVEL_NOTICE, "*** sleeping to help debug\n");
943 sleep(3600); // to help debug
944 }
945 }
946
947 // msg prints after fmt, ...
948 static NOINLINE void
949 szone_error(szone_t *szone, int is_corruption, const char *msg, const void *ptr, const char *fmt, ...)
950 {
951 va_list ap;
952 _SIMPLE_STRING b = _simple_salloc();
953
954 if (szone) SZONE_UNLOCK(szone); // FIXME: unlock magazine and region locks?
955 if (b) {
956 if (fmt) {
957 va_start(ap, fmt);
958 _simple_vsprintf(b, fmt, ap);
959 va_end(ap);
960 }
961 if (ptr) {
962 _simple_sprintf(b, "*** error for object %p: %s\n", ptr, msg);
963 } else {
964 _simple_sprintf(b, "*** error: %s\n", msg);
965 }
966 malloc_printf("%s*** set a breakpoint in malloc_error_break to debug\n", _simple_string(b));
967 } else {
968 /*
969 * Should only get here if vm_allocate() can't get a single page of
970 * memory, implying _simple_asl_log() would also fail. So we just
971 * print to the file descriptor.
972 */
973 if (fmt) {
974 va_start(ap, fmt);
975 _malloc_vprintf(MALLOC_PRINTF_NOLOG, fmt, ap);
976 va_end(ap);
977 }
978 if (ptr) {
979 _malloc_printf(MALLOC_PRINTF_NOLOG, "*** error for object %p: %s\n", ptr, msg);
980 } else {
981 _malloc_printf(MALLOC_PRINTF_NOLOG, "*** error: %s\n", msg);
982 }
983 _malloc_printf(MALLOC_PRINTF_NOLOG, "*** set a breakpoint in malloc_error_break to debug\n");
984 }
985 malloc_error_break();
986 #if DEBUG_MALLOC
987 szone_print(szone, 1);
988 #endif
989 szone_sleep();
990 // Call abort() if this is a memory corruption error and the abort on
991 // corruption flag is set, or if any error should abort.
992 if ((is_corruption && (szone->debug_flags & SCALABLE_MALLOC_ABORT_ON_CORRUPTION)) ||
993 (szone->debug_flags & SCALABLE_MALLOC_ABORT_ON_ERROR)) {
994 CRSetCrashLogMessage(b ? _simple_string(b) : msg);
995 abort();
996 } else if (b) {
997 _simple_sfree(b);
998 }
999 }
1000
1001 static void
1002 protect(void *address, size_t size, unsigned protection, unsigned debug_flags)
1003 {
1004 kern_return_t err;
1005
1006 if (!(debug_flags & SCALABLE_MALLOC_DONT_PROTECT_PRELUDE)) {
1007 err = mprotect((void *)((uintptr_t)address - vm_page_size), vm_page_size, protection);
1008 if (err) {
1009 malloc_printf("*** can't protect(%p) region for prelude guard page at %p\n",
1010 protection,(uintptr_t)address - (1 << vm_page_shift));
1011 }
1012 }
1013 if (!(debug_flags & SCALABLE_MALLOC_DONT_PROTECT_POSTLUDE)) {
1014 err = mprotect((void *)((uintptr_t)address + size), vm_page_size, protection);
1015 if (err) {
1016 malloc_printf("*** can't protect(%p) region for postlude guard page at %p\n",
1017 protection, (uintptr_t)address + size);
1018 }
1019 }
1020 }
1021
1022 static void *
1023 allocate_pages(szone_t *szone, size_t size, unsigned char align, unsigned debug_flags, int vm_page_label)
1024 {
1025 // align specifies a desired alignment (as a log) or 0 if no alignment requested
1026 void *vm_addr;
1027 uintptr_t addr = 0, aligned_address;
1028 boolean_t add_guard_pages = debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES;
1029 boolean_t purgeable = debug_flags & SCALABLE_MALLOC_PURGEABLE;
1030 size_t allocation_size = round_page(size);
1031 size_t delta;
1032 int alloc_flags = VM_MAKE_TAG(vm_page_label);
1033
1034 if (align) add_guard_pages = 0; // too cumbersome to deal with that
1035 if (!allocation_size) allocation_size = 1 << vm_page_shift;
1036 if (add_guard_pages) allocation_size += 2 * (1 << vm_page_shift);
1037 if (align) allocation_size += (size_t)1 << align;
1038 if (purgeable) alloc_flags |= VM_FLAGS_PURGABLE;
1039
1040 if (allocation_size < size) // size_t arithmetic wrapped!
1041 return NULL;
1042
1043 vm_addr = mmap(0 /* addr */,
1044 allocation_size /* size */,
1045 PROT_READ | PROT_WRITE /* prot */,
1046 MAP_ANON | MAP_PRIVATE /* flags */,
1047 alloc_flags /* fd being used to pass "purgeable" and "vm_page_label" */,
1048 0 /* offset */);
1049 if ((uintptr_t)vm_addr == -1) {
1050 szone_error(szone, 0, "can't allocate region", NULL, "*** mmap(size=%lu) failed (error code=%d)\n",
1051 allocation_size, errno);
1052 return NULL;
1053 }
1054 addr = (uintptr_t)vm_addr;
1055
1056 if (align) {
1057 aligned_address = (addr + ((uintptr_t)1 << align) - 1) & ~ (((uintptr_t)1 << align) - 1);
1058 if (aligned_address != addr) {
1059 delta = aligned_address - addr;
1060 if (munmap((void *)addr, delta) == -1)
1061 malloc_printf("*** munmap unaligned header failed with %d\n", errno);
1062 addr = aligned_address;
1063 allocation_size -= delta;
1064 }
1065 if (allocation_size > size) {
1066 if (munmap((void *)(addr + size), allocation_size - size) == -1)
1067 malloc_printf("*** munmap unaligned footer failed with %d\n", errno);
1068 }
1069 }
1070 if (add_guard_pages) {
1071 addr += (uintptr_t)1 << vm_page_shift;
1072 protect((void *)addr, size, PROT_NONE, debug_flags);
1073 }
1074 return (void *)addr;
1075 }
1076
1077 static void *
1078 allocate_pages_securely(szone_t *szone, size_t size, unsigned char align, int vm_page_label)
1079 {
1080 // align specifies a desired alignment (as a log) or 0 if no alignment requested
1081 void *vm_addr;
1082 uintptr_t addr, aligned_address;
1083 size_t delta, allocation_size = MAX(round_page(size), vm_page_size);
1084 int alloc_flags = VM_MAKE_TAG(vm_page_label);
1085
1086 if (szone->debug_flags & DISABLE_ASLR)
1087 return allocate_pages(szone, size, align, 0, vm_page_label);
1088
1089 if (align)
1090 allocation_size += (size_t)1 << align;
1091
1092 if (allocation_size < size) // size_t arithmetic wrapped!
1093 return NULL;
1094
1095 retry:
1096 vm_addr = mmap((void *)entropic_address /* kernel finds next available range at or above this address */,
1097 allocation_size /* size */,
1098 PROT_READ | PROT_WRITE /* prot */,
1099 MAP_ANON | MAP_PRIVATE /* flags */,
1100 alloc_flags /* fd being used to pass "vm_page_label" */,
1101 0 /* offset */);
1102 if (MAP_FAILED == vm_addr) {
1103 szone_error(szone, 0, "can't allocate region securely", NULL, "*** mmap(size=%lu) failed (error code=%d)\n",
1104 size, errno);
1105 return NULL;
1106 }
1107 addr = (uintptr_t)vm_addr;
1108
1109 // Don't allow allocation to rise above entropic_limit (for tidiness).
1110 if (addr + allocation_size > entropic_limit) { // Exhausted current range?
1111 uintptr_t t = entropic_address;
1112 uintptr_t u = t - ENTROPIC_KABILLION;
1113
1114 if (u < t) { // provided we don't wrap, unmap and retry, in the expanded entropic range
1115 munmap((void *)addr, allocation_size);
1116 (void)__sync_bool_compare_and_swap(&entropic_address, t, u); // Just one reduction please
1117 goto retry;
1118 }
1119 // fall through to use what we got
1120 }
1121
1122 if (addr < entropic_address) { // mmap wrapped to find this allocation, expand the entropic range
1123 uintptr_t t = entropic_address;
1124 uintptr_t u = t - ENTROPIC_KABILLION;
1125 if (u < t)
1126 (void)__sync_bool_compare_and_swap(&entropic_address, t, u); // Just one reduction please
1127 // fall through to use what we got
1128 }
1129
1130 // unmap any excess address range used for alignment padding
1131 if (align) {
1132 aligned_address = (addr + ((uintptr_t)1 << align) - 1) & ~ (((uintptr_t)1 << align) - 1);
1133 if (aligned_address != addr) {
1134 delta = aligned_address - addr;
1135 if (munmap((void *)addr, delta) == -1)
1136 malloc_printf("*** munmap unaligned header failed with %d\n", errno);
1137 addr = aligned_address;
1138 allocation_size -= delta;
1139 }
1140 if (allocation_size > size) {
1141 if (munmap((void *)(addr + size), allocation_size - size) == -1)
1142 malloc_printf("*** munmap unaligned footer failed with %d\n", errno);
1143 }
1144 }
1145 return (void *)addr;
1146 }
1147
1148 static void
1149 deallocate_pages(szone_t *szone, void *addr, size_t size, unsigned debug_flags)
1150 {
1151 int err;
1152 boolean_t add_guard_pages = debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES;
1153
1154 if (add_guard_pages) {
1155 addr = (void *)((uintptr_t)addr - (1 << vm_page_shift));
1156 size += 2 * (1 << vm_page_shift);
1157 }
1158 err = munmap(addr, size);
1159 if ((err == -1) && szone)
1160 szone_error(szone, 0, "Can't deallocate_pages region", addr, NULL);
1161 }
1162
1163 static int
1164 #if TARGET_OS_EMBEDDED
1165 madvise_free_range(szone_t *szone, region_t r, uintptr_t pgLo, uintptr_t pgHi, uintptr_t *last)
1166 #else
1167 madvise_free_range(szone_t *szone, region_t r, uintptr_t pgLo, uintptr_t pgHi)
1168 #endif
1169 {
1170 if (pgHi > pgLo) {
1171 size_t len = pgHi - pgLo;
1172
1173 #if DEBUG_MALLOC
1174 if (szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE)
1175 memset((void *)pgLo, 0xed, len); // Scribble on MADV_FREEd memory
1176 #endif
1177
1178 #if TARGET_OS_EMBEDDED
1179 if (last) {
1180 if (*last == pgLo)
1181 return 0;
1182
1183 *last = pgLo;
1184 }
1185 #endif
1186
1187 MAGMALLOC_MADVFREEREGION((void *)szone, (void *)r, (void *)pgLo, len); // DTrace USDT Probe
1188 #if TARGET_OS_EMBEDDED
1189 if (-1 == madvise((void *)pgLo, len, MADV_FREE)) {
1190 #else
1191 if (-1 == madvise((void *)pgLo, len, MADV_FREE_REUSABLE)) {
1192 #endif
1193 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
1194 #if DEBUG_MADVISE
1195 szone_error(szone, 0, "madvise_free_range madvise(..., MADV_FREE_REUSABLE) failed",
1196 (void *)pgLo, "length=%d\n", len);
1197 #endif
1198 }
1199 }
1200 return 0;
1201 }
1202
1203 static kern_return_t
1204 _szone_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr)
1205 {
1206 *ptr = (void *)address;
1207 return 0;
1208 }
1209
1210 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1211 // pthread_t's are page aligned, (sometimes even in ascending sequence). These hash well.
1212 // See Knuth TAOCP, Vol. 3.
1213 #if __LP64__
1214 #define HASH_SELF() \
1215 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 11400714819323198549ULL) >> (64 - szone->num_tiny_magazines_mask_shift)
1216 #else
1217 #define HASH_SELF() \
1218 ((((uintptr_t)pthread_self()) >> vm_page_shift) * 2654435761UL) >> (32 - szone->num_tiny_magazines_mask_shift)
1219 #endif
1220
1221 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
1222 /*
1223 * These commpage routines provide fast access to the logical cpu number
1224 * of the calling processor assuming no pre-emption occurs.
1225 */
1226
1227 static INLINE mag_index_t
1228 mag_get_thread_index(szone_t *szone)
1229 {
1230 if (!__is_threaded)
1231 return 0;
1232 else
1233 return cpu_number() & (TINY_MAX_MAGAZINES - 1);
1234 }
1235
1236 #else
1237 #warning deriving magazine index from pthread_self() [want processor number]
1238
1239 static INLINE mag_index_t
1240 mag_get_thread_index(szone_t *szone)
1241 {
1242 if (!__is_threaded)
1243 return 0;
1244 else if ((pthread_key_t) -1 == szone->cpu_id_key) { // In case pthread_key_create() failed.
1245 return HASH_SELF();
1246 } else {
1247 mag_index_t idx = (mag_index_t)(intptr_t)pthread_getspecific(szone->cpu_id_key);
1248
1249 // Has this thread been hinted with a non-zero value [i.e. 1 + cpuid()] ?
1250 // If so, bump down the hint to a zero-based magazine index and return it.
1251 if (idx) {
1252 return idx - 1;
1253 } else {
1254 // No hint available. Contruct a magazine index for this thread ...
1255 idx = HASH_SELF();
1256
1257 // bump up the hint to exclude zero and try to memorize it ...
1258 pthread_setspecific(szone->cpu_id_key, (const void *)((uintptr_t)idx + 1));
1259
1260 // and return the (zero-based) magazine index.
1261 return idx;
1262 }
1263 }
1264 }
1265 #endif
1266
1267 static magazine_t *
1268 mag_lock_zine_for_region_trailer(szone_t *szone, magazine_t *magazines, region_trailer_t *trailer, mag_index_t mag_index)
1269 {
1270 mag_index_t refreshed_index;
1271 magazine_t *mag_ptr = &(magazines[mag_index]);
1272
1273 // Take the lock on entry.
1274 SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr);
1275
1276 // Now in the time it took to acquire the lock, the region may have migrated
1277 // from one magazine to another. In which case the magazine lock we obtained
1278 // (namely magazines[mag_index].mag_lock) is stale. If so, keep on tryin' ...
1279 while (mag_index != (refreshed_index = trailer->mag_index)) { // Note assignment
1280
1281 SZONE_MAGAZINE_PTR_UNLOCK(szone, mag_ptr);
1282
1283 mag_index = refreshed_index;
1284 mag_ptr = &(magazines[mag_index]);
1285 SZONE_MAGAZINE_PTR_LOCK(szone, mag_ptr);
1286 }
1287
1288 return mag_ptr;
1289 }
1290
1291 /*******************************************************************************
1292 * Region hash implementation
1293 *
1294 * This is essentially a duplicate of the existing Large allocator hash, minus
1295 * the ability to remove entries. The two should be combined eventually.
1296 ******************************************************************************/
1297 #pragma mark region hash
1298
1299 /*
1300 * hash_lookup_region_no_lock - Scan a hash ring looking for an entry for a
1301 * given region.
1302 *
1303 * FIXME: If consecutive queries of the same region are likely, a one-entry
1304 * cache would likely be a significant performance win here.
1305 */
1306 static INLINE rgnhdl_t
1307 hash_lookup_region_no_lock(region_t *regions, size_t num_entries, size_t shift, region_t r) {
1308 size_t index, hash_index;
1309 rgnhdl_t entry;
1310
1311 if (!num_entries)
1312 return 0;
1313
1314 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1315 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1316 // this hash works really well. See Knuth TAOCP, Vol. 3.
1317 #if __LP64__
1318 index = hash_index = (((uintptr_t)r >> HASH_BLOCKS_ALIGN) * 11400714819323198549ULL) >> (64 - shift);
1319 #else
1320 index = hash_index = (((uintptr_t)r >> HASH_BLOCKS_ALIGN) * 2654435761UL) >> (32 - shift);
1321 #endif
1322 do {
1323 entry = regions + index;
1324 if (*entry == 0)
1325 return 0;
1326 if (*entry == r)
1327 return entry;
1328 if (++index == num_entries)
1329 index = 0;
1330 } while (index != hash_index);
1331 return 0;
1332 }
1333
1334 /*
1335 * hash_region_insert_no_lock - Insert a region into the hash ring.
1336 */
1337 static void
1338 hash_region_insert_no_lock(region_t *regions, size_t num_entries, size_t shift, region_t r) {
1339 size_t index, hash_index;
1340 rgnhdl_t entry;
1341
1342 // Multiplicative hash where the multiplier is a prime near (ULONG_MAX / phi). [phi = 1.618033...]
1343 // Since the values of (((uintptr_t)r >> HASH_BLOCKS_ALIGN) are (roughly) an ascending sequence of integers,
1344 // this hash works really well. See Knuth TAOCP, Vol. 3.
1345 #if __LP64__
1346 index = hash_index = (((uintptr_t)r >> HASH_BLOCKS_ALIGN) * 11400714819323198549ULL) >> (64 - shift);
1347 #else
1348 index = hash_index = (((uintptr_t)r >> HASH_BLOCKS_ALIGN) * 2654435761UL) >> (32 - shift);
1349 #endif
1350 do {
1351 entry = regions + index;
1352 if (*entry == HASHRING_OPEN_ENTRY || *entry == HASHRING_REGION_DEALLOCATED) {
1353 *entry = r;
1354 return;
1355 }
1356 if (++index == num_entries)
1357 index = 0;
1358 } while (index != hash_index);
1359 }
1360
1361 /*
1362 * hash_regions_alloc_no_lock - Allocate space for a number of entries. This
1363 * must be a VM allocation as to avoid recursing between allocating a new small
1364 * region, and asking the small region to allocate space for the new list of
1365 * regions.
1366 */
1367 static region_t *
1368 hash_regions_alloc_no_lock(szone_t *szone, size_t num_entries)
1369 {
1370 size_t size = num_entries * sizeof(region_t);
1371
1372 return allocate_pages(szone, round_page(size), 0, 0, VM_MEMORY_MALLOC);
1373 }
1374
1375 /*
1376 * hash_regions_grow_no_lock - Grow the hash ring, and rehash the entries.
1377 * Return the new region and new size to update the szone. Do not deallocate
1378 * the old entries since someone may still be allocating them.
1379 */
1380 static region_t *
1381 hash_regions_grow_no_lock(szone_t *szone, region_t *regions, size_t old_size, size_t *mutable_shift,
1382 size_t *new_size)
1383 {
1384 // double in size and allocate memory for the regions
1385 *new_size = old_size + old_size;
1386 *mutable_shift = *mutable_shift + 1;
1387 region_t *new_regions = hash_regions_alloc_no_lock(szone, *new_size);
1388
1389 // rehash the entries into the new list
1390 size_t index;
1391 for (index = 0; index < old_size; ++index) {
1392 region_t r = regions[index];
1393 if (r != HASHRING_OPEN_ENTRY && r != HASHRING_REGION_DEALLOCATED)
1394 hash_region_insert_no_lock(new_regions, *new_size, *mutable_shift, r);
1395 }
1396 return new_regions;
1397 }
1398
1399 /********************* FREE LIST UTILITIES ************************/
1400
1401 // A free list entry is comprised of a pair of pointers, previous and next.
1402 // These are used to implement a doubly-linked list, which permits efficient
1403 // extraction.
1404 //
1405 // Because the free list entries are previously freed objects, a misbehaved
1406 // program may write to a pointer after it has called free() on that pointer,
1407 // either by dereference or buffer overflow from an adjacent pointer. This write
1408 // would then corrupt the free list's previous and next pointers, leading to a
1409 // crash. In order to detect this case, we take advantage of the fact that
1410 // malloc'd pointers are known to be at least 16 byte aligned, and thus have
1411 // at least 4 trailing zero bits.
1412 //
1413 // When an entry is added to the free list, a checksum of the previous and next
1414 // pointers is calculated and written to the high four bits of the respective
1415 // pointers. Upon detection of an invalid checksum, an error is logged and NULL
1416 // is returned. Since all code which un-checksums pointers checks for a NULL
1417 // return, a potentially crashing or malicious dereference is avoided at the
1418 // cost of leaking the corrupted block, and any subsequent blocks on the free
1419 // list of that size.
1420
1421 static NOINLINE void
1422 free_list_checksum_botch(szone_t *szone, free_list_t *ptr)
1423 {
1424 szone_error(szone, 1, "incorrect checksum for freed object "
1425 "- object was probably modified after being freed.", ptr, NULL);
1426 }
1427
1428 static INLINE uintptr_t free_list_gen_checksum(uintptr_t ptr)
1429 {
1430 uint8_t chk;
1431
1432 chk = (unsigned char)(ptr >> 0);
1433 chk += (unsigned char)(ptr >> 8);
1434 chk += (unsigned char)(ptr >> 16);
1435 chk += (unsigned char)(ptr >> 24);
1436 #if __LP64__
1437 chk += (unsigned char)(ptr >> 32);
1438 chk += (unsigned char)(ptr >> 40);
1439 chk += (unsigned char)(ptr >> 48);
1440 chk += (unsigned char)(ptr >> 56);
1441 #endif
1442
1443 return chk & (uintptr_t)0xF;
1444 }
1445
1446 #define NYBBLE 4
1447 #if __LP64__
1448 #define ANTI_NYBBLE (64 - NYBBLE)
1449 #else
1450 #define ANTI_NYBBLE (32 - NYBBLE)
1451 #endif
1452
1453 static INLINE uintptr_t
1454 free_list_checksum_ptr(szone_t *szone, void *ptr)
1455 {
1456 uintptr_t p = (uintptr_t)ptr;
1457 return (p >> NYBBLE) | (free_list_gen_checksum(p ^ szone->cookie) << ANTI_NYBBLE); // compiles to rotate instruction
1458 }
1459
1460 static INLINE void *
1461 free_list_unchecksum_ptr(szone_t *szone, ptr_union *ptr)
1462 {
1463 ptr_union p;
1464 uintptr_t t = ptr->u;
1465
1466 t = (t << NYBBLE) | (t >> ANTI_NYBBLE); // compiles to rotate instruction
1467 p.u = t & ~(uintptr_t)0xF;
1468
1469 if ((t & (uintptr_t)0xF) != free_list_gen_checksum(p.u ^ szone->cookie))
1470 {
1471 free_list_checksum_botch(szone, (free_list_t *)ptr);
1472 return NULL;
1473 }
1474 return p.p;
1475 }
1476
1477 #undef ANTI_NYBBLE
1478 #undef NYBBLE
1479
1480 static unsigned
1481 free_list_count(szone_t *szone, free_list_t *ptr)
1482 {
1483 unsigned count = 0;
1484
1485 while (ptr) {
1486 count++;
1487 ptr = free_list_unchecksum_ptr(szone, &ptr->next);
1488 }
1489 return count;
1490 }
1491
1492 static INLINE void
1493 recirc_list_extract(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node)
1494 {
1495 // excise node from list
1496 if (NULL == node->prev)
1497 mag_ptr->firstNode = node->next;
1498 else
1499 node->prev->next = node->next;
1500
1501 if (NULL == node->next)
1502 mag_ptr->lastNode = node->prev;
1503 else
1504 node->next->prev = node->prev;
1505
1506 mag_ptr->recirculation_entries--;
1507 }
1508
1509 static INLINE void
1510 recirc_list_splice_last(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node)
1511 {
1512 if (NULL == mag_ptr->lastNode) {
1513 mag_ptr->firstNode = node;
1514 node->prev = NULL;
1515 } else {
1516 node->prev = mag_ptr->lastNode;
1517 mag_ptr->lastNode->next = node;
1518 }
1519 mag_ptr->lastNode = node;
1520 node->next = NULL;
1521 node->recirc_suitable = FALSE;
1522 mag_ptr->recirculation_entries++;
1523 }
1524
1525 static INLINE void
1526 recirc_list_splice_first(szone_t *szone, magazine_t *mag_ptr, region_trailer_t *node)
1527 {
1528 if (NULL == mag_ptr->firstNode) {
1529 mag_ptr->lastNode = node;
1530 node->next = NULL;
1531 } else {
1532 node->next = mag_ptr->firstNode;
1533 mag_ptr->firstNode->prev = node;
1534 }
1535 mag_ptr->firstNode = node;
1536 node->prev = NULL;
1537 node->recirc_suitable = FALSE;
1538 mag_ptr->recirculation_entries++;
1539 }
1540
1541 /* Macros used to manipulate the uint32_t quantity mag_bitmap. */
1542
1543 /* BITMAPV variants are used by tiny. */
1544 #if defined(__LP64__)
1545 // assert(NUM_SLOTS == 64) in which case (slot >> 5) is either 0 or 1
1546 #define BITMAPV_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1547 #define BITMAPV_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1548 #define BITMAPV_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1549 #define BITMAPV_CTZ(bitmap) (__builtin_ctzl(bitmap))
1550 #else
1551 // assert(NUM_SLOTS == 32) in which case (slot >> 5) is always 0, so code it that way
1552 #define BITMAPV_SET(bitmap,slot) (bitmap[0] |= 1 << (slot))
1553 #define BITMAPV_CLR(bitmap,slot) (bitmap[0] &= ~ (1 << (slot)))
1554 #define BITMAPV_BIT(bitmap,slot) ((bitmap[0] >> (slot)) & 1)
1555 #define BITMAPV_CTZ(bitmap) (__builtin_ctz(bitmap))
1556 #endif
1557
1558 /* BITMAPN is used by small. (slot >> 5) takes on values from 0 to 7. */
1559 #define BITMAPN_SET(bitmap,slot) (bitmap[(slot) >> 5] |= 1 << ((slot) & 31))
1560 #define BITMAPN_CLR(bitmap,slot) (bitmap[(slot) >> 5] &= ~ (1 << ((slot) & 31)))
1561 #define BITMAPN_BIT(bitmap,slot) ((bitmap[(slot) >> 5] >> ((slot) & 31)) & 1)
1562
1563 /* returns bit # of least-significant one bit, starting at 0 (undefined if !bitmap) */
1564 #define BITMAP32_CTZ(bitmap) (__builtin_ctz(bitmap[0]))
1565
1566 /********************* TINY FREE LIST UTILITIES ************************/
1567
1568 // We encode the meta-headers as follows:
1569 // Each quantum has an associated set of 2 bits:
1570 // block_header when 1 says this block is the beginning of a block
1571 // in_use when 1 says this block is in use
1572 // so a block in use of size 3 is 1-1 0-X 0-X
1573 // for a free block TINY_FREE_SIZE(ptr) carries the size and the bits are 1-0 X-X X-X
1574 // for a block middle the bits are 0-0
1575
1576 // We store the meta-header bit arrays by interleaving them 32 bits at a time.
1577 // Initial 32 bits of block_header, followed by initial 32 bits of in_use, followed
1578 // by next 32 bits of block_header, followed by next 32 bits of in_use, etc.
1579 // This localizes memory references thereby reducing cache and TLB pressures.
1580
1581 static INLINE void
1582 BITARRAY_SET(uint32_t *bits, msize_t index)
1583 {
1584 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1585 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1586 bits[(index >> 5) << 1] |= (1 << (index & 31));
1587 }
1588
1589 static INLINE void
1590 BITARRAY_CLR(uint32_t *bits, msize_t index)
1591 {
1592 bits[(index >> 5) << 1] &= ~(1 << (index & 31));
1593 }
1594
1595 static INLINE boolean_t
1596 BITARRAY_BIT(uint32_t *bits, msize_t index)
1597 {
1598 return ((bits[(index >> 5) << 1]) >> (index & 31)) & 1;
1599 }
1600
1601 #if 0
1602 static INLINE void bitarray_mclr(uint32_t *bits, unsigned start, unsigned end) ALWAYSINLINE;
1603
1604 static INLINE void
1605 bitarray_mclr(uint32_t *bits, unsigned start, unsigned end)
1606 {
1607 // start >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1608 // (start >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1609 uint32_t *addr = bits + ((start >> 5) << 1);
1610
1611 uint32_t span = end - start;
1612 start = start & 31;
1613 end = start + span;
1614
1615 if (end > 31) {
1616 addr[0] &= (0xFFFFFFFFU >> (31 - start)) >> 1;
1617 addr[2] &= (0xFFFFFFFFU << (end - 32));
1618 } else {
1619 unsigned mask = (0xFFFFFFFFU >> (31 - start)) >> 1;
1620 mask |= (0xFFFFFFFFU << end);
1621 addr[0] &= mask;
1622 }
1623 }
1624 #endif
1625
1626 /*
1627 * Obtain the size of a free tiny block (in msize_t units).
1628 */
1629 static msize_t
1630 get_tiny_free_size(const void *ptr)
1631 {
1632 void *next_block = (void *)((uintptr_t)ptr + TINY_QUANTUM);
1633 void *region_end = TINY_REGION_END(TINY_REGION_FOR_PTR(ptr));
1634
1635 // check whether the next block is outside the tiny region or a block header
1636 // if so, then the size of this block is one, and there is no stored size.
1637 if (next_block < region_end)
1638 {
1639 uint32_t *next_header = TINY_BLOCK_HEADER_FOR_PTR(next_block);
1640 msize_t next_index = TINY_INDEX_FOR_PTR(next_block);
1641
1642 if (!BITARRAY_BIT(next_header, next_index))
1643 return TINY_FREE_SIZE(ptr);
1644 }
1645 return 1;
1646 }
1647
1648 /*
1649 * Get the size of the previous free block, which is stored in the last two
1650 * bytes of the block. If the previous block is not free, then the result is
1651 * undefined.
1652 */
1653 static msize_t
1654 get_tiny_previous_free_msize(const void *ptr)
1655 {
1656 // check whether the previous block is in the tiny region and a block header
1657 // if so, then the size of the previous block is one, and there is no stored
1658 // size.
1659 if (ptr != TINY_REGION_FOR_PTR(ptr))
1660 {
1661 void *prev_block = (void *)((uintptr_t)ptr - TINY_QUANTUM);
1662 uint32_t *prev_header = TINY_BLOCK_HEADER_FOR_PTR(prev_block);
1663 msize_t prev_index = TINY_INDEX_FOR_PTR(prev_block);
1664 if (BITARRAY_BIT(prev_header, prev_index))
1665 return 1;
1666 return TINY_PREVIOUS_MSIZE(ptr);
1667 }
1668 // don't read possibly unmapped memory before the beginning of the region
1669 return 0;
1670 }
1671
1672 static INLINE msize_t
1673 get_tiny_meta_header(const void *ptr, boolean_t *is_free)
1674 {
1675 // returns msize and is_free
1676 // may return 0 for the msize component (meaning 65536)
1677 uint32_t *block_header;
1678 msize_t index;
1679
1680 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1681 index = TINY_INDEX_FOR_PTR(ptr);
1682
1683 msize_t midx = (index >> 5) << 1;
1684 uint32_t mask = 1 << (index & 31);
1685 *is_free = 0;
1686 if (0 == (block_header[midx] & mask)) // if (!BITARRAY_BIT(block_header, index))
1687 return 0;
1688 if (0 == (block_header[midx + 1] & mask)) { // if (!BITARRAY_BIT(in_use, index))
1689 *is_free = 1;
1690 return get_tiny_free_size(ptr);
1691 }
1692
1693 // index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
1694 // (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
1695 #if defined(__LP64__)
1696 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1697 // That's guaranteed to be somewhwere in the next 64 bits. And those bits could span three
1698 // uint32_t block_header elements. Collect the bits into a single uint64_t and measure up with ffsl.
1699 uint32_t *addr = ((uint32_t *)block_header) + ((index >> 5) << 1);
1700 uint32_t bitidx = index & 31;
1701 uint64_t word_lo = addr[0];
1702 uint64_t word_mid = addr[2];
1703 uint64_t word_hi = addr[4];
1704 uint64_t word_lomid = (word_lo >> bitidx) | (word_mid << (32 - bitidx));
1705 uint64_t word = bitidx ? word_lomid | (word_hi << (64 - bitidx)) : word_lomid;
1706 uint32_t result = __builtin_ffsl(word >> 1);
1707 #else
1708 // The return value, msize, is computed as the distance to the next 1 bit in block_header.
1709 // That's guaranteed to be somwhwere in the next 32 bits. And those bits could span two
1710 // uint32_t block_header elements. Collect the bits into a single uint32_t and measure up with ffs.
1711 uint32_t *addr = ((uint32_t *)block_header) + ((index >> 5) << 1);
1712 uint32_t bitidx = index & 31;
1713 uint32_t word = bitidx ? (addr[0] >> bitidx) | (addr[2] << (32 - bitidx)) : addr[0];
1714 uint32_t result = __builtin_ffs(word >> 1);
1715 #endif
1716 return result;
1717 }
1718
1719 static INLINE void
1720 set_tiny_meta_header_in_use(const void *ptr, msize_t msize)
1721 {
1722 uint32_t *block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1723 msize_t index = TINY_INDEX_FOR_PTR(ptr);
1724 msize_t clr_msize = msize - 1;
1725 msize_t midx = (index >> 5) << 1;
1726 uint32_t val = (1 << (index & 31));
1727
1728 #if DEBUG_MALLOC
1729 if (msize >= NUM_TINY_SLOTS)
1730 malloc_printf("set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr, msize);
1731 if ((unsigned)index + (unsigned)msize > 0x10000)
1732 malloc_printf("set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr, msize);
1733 #endif
1734
1735 block_header[midx] |= val; // BITARRAY_SET(block_header, index);
1736 block_header[midx + 1] |= val; // BITARRAY_SET(in_use, index);
1737
1738 // bitarray_mclr(block_header, index, end_bit);
1739 // bitarray_mclr(in_use, index, end_bit);
1740
1741 index++;
1742 midx = (index >> 5) << 1;
1743
1744 unsigned start = index & 31;
1745 unsigned end = start + clr_msize;
1746
1747 #if defined(__LP64__)
1748 if (end > 63) {
1749 unsigned mask0 = (0xFFFFFFFFU >> (31 - start)) >> 1;
1750 unsigned mask1 = (0xFFFFFFFFU << (end - 64));
1751 block_header[midx + 0] &= mask0; // clear header
1752 block_header[midx + 1] &= mask0; // clear in_use
1753 block_header[midx + 2] = 0; // clear header
1754 block_header[midx + 3] = 0; // clear in_use
1755 block_header[midx + 4] &= mask1; // clear header
1756 block_header[midx + 5] &= mask1; // clear in_use
1757 } else
1758 #endif
1759 if (end > 31) {
1760 unsigned mask0 = (0xFFFFFFFFU >> (31 - start)) >> 1;
1761 unsigned mask1 = (0xFFFFFFFFU << (end - 32));
1762 block_header[midx + 0] &= mask0;
1763 block_header[midx + 1] &= mask0;
1764 block_header[midx + 2] &= mask1;
1765 block_header[midx + 3] &= mask1;
1766 } else {
1767 unsigned mask = (0xFFFFFFFFU >> (31 - start)) >> 1;
1768 mask |= (0xFFFFFFFFU << end);
1769 block_header[midx + 0] &= mask;
1770 block_header[midx + 1] &= mask;
1771 }
1772
1773 // we set the block_header bit for the following block to reaffirm next block is a block
1774 index += clr_msize;
1775 midx = (index >> 5) << 1;
1776 val = (1 << (index & 31));
1777 block_header[midx] |= val; // BITARRAY_SET(block_header, (index+clr_msize));
1778 #if DEBUG_MALLOC
1779 {
1780 boolean_t ff;
1781 msize_t mf;
1782
1783 mf = get_tiny_meta_header(ptr, &ff);
1784 if (msize != mf) {
1785 malloc_printf("setting header for tiny in_use %p : %d\n", ptr, msize);
1786 malloc_printf("reading header for tiny %p : %d %d\n", ptr, mf, ff);
1787 }
1788 }
1789 #endif
1790 }
1791
1792 static INLINE void
1793 set_tiny_meta_header_in_use_1(const void *ptr) // As above with msize == 1
1794 {
1795 uint32_t *block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1796 msize_t index = TINY_INDEX_FOR_PTR(ptr);
1797 msize_t midx = (index >> 5) << 1;
1798 uint32_t val = (1 << (index & 31));
1799
1800 block_header[midx] |= val; // BITARRAY_SET(block_header, index);
1801 block_header[midx + 1] |= val; // BITARRAY_SET(in_use, index);
1802
1803 index++;
1804 midx = (index >> 5) << 1;
1805 val = (1 << (index & 31));
1806
1807 block_header[midx] |= val; // BITARRAY_SET(block_header, (index+clr_msize))
1808 }
1809
1810 static INLINE void
1811 set_tiny_meta_header_middle(const void *ptr)
1812 {
1813 // indicates this block is in the middle of an in use block
1814 uint32_t *block_header;
1815 uint32_t *in_use;
1816 msize_t index;
1817
1818 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1819 in_use = TINY_INUSE_FOR_HEADER(block_header);
1820 index = TINY_INDEX_FOR_PTR(ptr);
1821
1822 BITARRAY_CLR(block_header, index);
1823 BITARRAY_CLR(in_use, index);
1824 }
1825
1826 static INLINE void
1827 set_tiny_meta_header_free(const void *ptr, msize_t msize)
1828 {
1829 // !msize is acceptable and means 65536
1830 uint32_t *block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1831 msize_t index = TINY_INDEX_FOR_PTR(ptr);
1832 msize_t midx = (index >> 5) << 1;
1833 uint32_t val = (1 << (index & 31));
1834
1835 #if DEBUG_MALLOC
1836 if ((unsigned)index + (unsigned)msize > 0x10000) {
1837 malloc_printf("setting header for tiny free %p msize too large: %d\n", ptr, msize);
1838 }
1839 #endif
1840
1841 block_header[midx] |= val; // BITARRAY_SET(block_header, index);
1842 block_header[midx + 1] &= ~val; // BITARRAY_CLR(in_use, index);
1843
1844 // mark the end of this block if msize is > 1. For msize == 0, the whole
1845 // region is free, so there is no following block. For msize == 1, there is
1846 // no space to write the size on 64 bit systems. The size for 1 quantum
1847 // blocks is computed from the metadata bitmaps.
1848 if (msize > 1) {
1849 void *follower = FOLLOWING_TINY_PTR(ptr, msize);
1850 TINY_PREVIOUS_MSIZE(follower) = msize;
1851 TINY_FREE_SIZE(ptr) = msize;
1852 }
1853 if (msize == 0) {
1854 TINY_FREE_SIZE(ptr) = msize;
1855 }
1856 #if DEBUG_MALLOC
1857 boolean_t ff;
1858 msize_t mf = get_tiny_meta_header(ptr, &ff);
1859 if ((msize != mf) || !ff) {
1860 malloc_printf("setting header for tiny free %p : %u\n", ptr, msize);
1861 malloc_printf("reading header for tiny %p : %u %u\n", ptr, mf, ff);
1862 }
1863 #endif
1864 }
1865
1866 static INLINE boolean_t
1867 tiny_meta_header_is_free(const void *ptr)
1868 {
1869 uint32_t *block_header;
1870 uint32_t *in_use;
1871 msize_t index;
1872
1873 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1874 in_use = TINY_INUSE_FOR_HEADER(block_header);
1875 index = TINY_INDEX_FOR_PTR(ptr);
1876 if (!BITARRAY_BIT(block_header, index))
1877 return 0;
1878 return !BITARRAY_BIT(in_use, index);
1879 }
1880
1881 static INLINE void *
1882 tiny_previous_preceding_free(void *ptr, msize_t *prev_msize)
1883 {
1884 // returns the previous block, assuming and verifying it's free
1885 uint32_t *block_header;
1886 uint32_t *in_use;
1887 msize_t index;
1888 msize_t previous_msize;
1889 msize_t previous_index;
1890 void *previous_ptr;
1891
1892 block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
1893 in_use = TINY_INUSE_FOR_HEADER(block_header);
1894 index = TINY_INDEX_FOR_PTR(ptr);
1895
1896 if (!index)
1897 return NULL;
1898 if ((previous_msize = get_tiny_previous_free_msize(ptr)) > index)
1899 return NULL;
1900
1901 previous_index = index - previous_msize;
1902 previous_ptr = (void *)((uintptr_t)TINY_REGION_FOR_PTR(ptr) + TINY_BYTES_FOR_MSIZE(previous_index));
1903 if (!BITARRAY_BIT(block_header, previous_index))
1904 return NULL;
1905 if (BITARRAY_BIT(in_use, previous_index))
1906 return NULL;
1907 if (get_tiny_free_size(previous_ptr) != previous_msize)
1908 return NULL;
1909
1910 // conservative check did match true check
1911 *prev_msize = previous_msize;
1912 return previous_ptr;
1913 }
1914
1915 /*
1916 * Adds an item to the proper free list, and also marks the meta-header of the
1917 * block properly.
1918 * Assumes szone has been locked
1919 */
1920 static void
1921 tiny_free_list_add_ptr(szone_t *szone, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize)
1922 {
1923 grain_t slot = (!msize || (msize >= NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS - 1 : msize - 1;
1924 free_list_t *free_ptr = ptr;
1925 free_list_t *free_head = tiny_mag_ptr->mag_free_list[slot];
1926
1927 #if DEBUG_MALLOC
1928 if (LOG(szone,ptr)) {
1929 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
1930 }
1931 if (((uintptr_t)ptr) & (TINY_QUANTUM - 1)) {
1932 szone_error(szone, 1, "tiny_free_list_add_ptr: Unaligned ptr", ptr, NULL);
1933 }
1934 #endif
1935 set_tiny_meta_header_free(ptr, msize);
1936 if (free_head) {
1937 #if DEBUG_MALLOC
1938 if (free_list_unchecksum_ptr(szone, &free_head->previous)) {
1939 szone_error(szone, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr,
1940 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr, slot, (void *)free_head, free_head->previous.p);
1941 }
1942 if (! tiny_meta_header_is_free(free_head)) {
1943 szone_error(szone, 1, "tiny_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr,
1944 "ptr=%p slot=%d free_head=%p\n", ptr, slot, (void *)free_head);
1945 }
1946 #endif
1947 free_head->previous.u = free_list_checksum_ptr(szone, free_ptr);
1948 } else {
1949 BITMAPV_SET(tiny_mag_ptr->mag_bitmap, slot);
1950 }
1951 free_ptr->previous.u = free_list_checksum_ptr(szone, NULL);
1952 free_ptr->next.u = free_list_checksum_ptr(szone, free_head);
1953
1954 tiny_mag_ptr->mag_free_list[slot] = free_ptr;
1955 }
1956
1957 /*
1958 * Removes the item pointed to by ptr in the proper free list.
1959 * Assumes szone has been locked
1960 */
1961 static void
1962 tiny_free_list_remove_ptr(szone_t *szone, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize)
1963 {
1964 grain_t slot = (!msize || (msize >= NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS - 1 : msize - 1;
1965 free_list_t *free_ptr = ptr, *next, *previous;
1966
1967 next = free_list_unchecksum_ptr(szone, &free_ptr->next);
1968 previous = free_list_unchecksum_ptr(szone, &free_ptr->previous);
1969
1970 #if DEBUG_MALLOC
1971 if (LOG(szone,ptr)) {
1972 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
1973 }
1974 #endif
1975 if (!previous) {
1976 // The block to remove is the head of the free list
1977 #if DEBUG_MALLOC
1978 if (tiny_mag_ptr->mag_free_list[slot] != ptr) {
1979 szone_error(szone, 1, "tiny_free_list_remove_ptr: Internal invariant broken (tiny_mag_ptr->mag_free_list[slot])", ptr,
1980 "ptr=%p slot=%d msize=%d tiny_mag_ptr->mag_free_list[slot]=%p\n",
1981 ptr, slot, msize, (void *)tiny_mag_ptr->mag_free_list[slot]);
1982 return;
1983 }
1984 #endif
1985 tiny_mag_ptr->mag_free_list[slot] = next;
1986 if (!next) BITMAPV_CLR(tiny_mag_ptr->mag_bitmap, slot);
1987 } else {
1988 // We know free_ptr is already checksummed, so we don't need to do it
1989 // again.
1990 previous->next = free_ptr->next;
1991 }
1992 if (next) {
1993 // We know free_ptr is already checksummed, so we don't need to do it
1994 // again.
1995 next->previous = free_ptr->previous;
1996 }
1997 }
1998
1999 /*
2000 * tiny_region_for_ptr_no_lock - Returns the tiny region containing the pointer,
2001 * or NULL if not found.
2002 */
2003 static INLINE region_t
2004 tiny_region_for_ptr_no_lock(szone_t *szone, const void *ptr)
2005 {
2006 rgnhdl_t r = hash_lookup_region_no_lock(szone->tiny_region_generation->hashed_regions,
2007 szone->tiny_region_generation->num_regions_allocated,
2008 szone->tiny_region_generation->num_regions_allocated_shift,
2009 TINY_REGION_FOR_PTR(ptr));
2010 return r ? *r : r;
2011 }
2012
2013 static void
2014 tiny_finalize_region(szone_t *szone, magazine_t *tiny_mag_ptr) {
2015 void *last_block, *previous_block;
2016 uint32_t *last_header;
2017 msize_t last_msize, previous_msize, last_index;
2018
2019 // It is possible that the block prior to the last block in the region has
2020 // been free'd, but was not coalesced with the free bytes at the end of the
2021 // block, since we treat the bytes at the end of the region as "in use" in
2022 // the meta headers. Attempt to coalesce the last block with the previous
2023 // block, so we don't violate the "no consecutive free blocks" invariant.
2024 //
2025 // FIXME: Need to investigate how much work would be required to increase
2026 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
2027 // than performing this workaround.
2028 //
2029
2030 if (tiny_mag_ptr->mag_bytes_free_at_end) {
2031 last_block = (void *)
2032 ((uintptr_t)TINY_REGION_END(tiny_mag_ptr->mag_last_region) - tiny_mag_ptr->mag_bytes_free_at_end);
2033 last_msize = TINY_MSIZE_FOR_BYTES(tiny_mag_ptr->mag_bytes_free_at_end);
2034 last_header = TINY_BLOCK_HEADER_FOR_PTR(last_block);
2035 last_index = TINY_INDEX_FOR_PTR(last_block);
2036
2037 // Before anything we transform any remaining mag_bytes_free_at_end into a
2038 // regular free block. We take special care here to update the bitfield
2039 // information, since we are bypassing the normal free codepath. If there
2040 // is more than one quanta worth of memory in mag_bytes_free_at_end, then
2041 // there will be two block headers:
2042 // 1) header for the free space at end, msize = 1
2043 // 2) header inserted by set_tiny_meta_header_in_use after block
2044 // We must clear the second one so that when the free block's size is
2045 // queried, we do not think the block is only 1 quantum in size because
2046 // of the second set header bit.
2047 if (last_index != (NUM_TINY_BLOCKS - 1))
2048 BITARRAY_CLR(last_header, (last_index + 1));
2049
2050 previous_block = tiny_previous_preceding_free(last_block, &previous_msize);
2051 if (previous_block) {
2052 set_tiny_meta_header_middle(last_block);
2053 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, previous_block, previous_msize);
2054 last_block = previous_block;
2055 last_msize += previous_msize;
2056 }
2057
2058 // splice last_block into the free list
2059 tiny_free_list_add_ptr(szone, tiny_mag_ptr, last_block, last_msize);
2060 tiny_mag_ptr->mag_bytes_free_at_end = 0;
2061 }
2062
2063 #if ASLR_INTERNAL
2064 // Coalesce the big free block at start with any following free blocks
2065 if (tiny_mag_ptr->mag_bytes_free_at_start) {
2066 last_block = TINY_REGION_ADDRESS(tiny_mag_ptr->mag_last_region);
2067 last_msize = TINY_MSIZE_FOR_BYTES(tiny_mag_ptr->mag_bytes_free_at_start);
2068
2069 void *next_block = (void *) ((uintptr_t)last_block + tiny_mag_ptr->mag_bytes_free_at_start);
2070
2071 // clear the in use bit we were using to mark the end of the big start block
2072 set_tiny_meta_header_middle((uintptr_t)next_block - TINY_QUANTUM);
2073
2074 // Coalesce the big start block with any following free blocks
2075 if (tiny_meta_header_is_free(next_block)) {
2076 msize_t next_msize = get_tiny_free_size(next_block);
2077 set_tiny_meta_header_middle(next_block);
2078 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, next_block, next_msize);
2079 last_msize += next_msize;
2080 }
2081
2082 // splice last_block into the free list
2083 tiny_free_list_add_ptr(szone, tiny_mag_ptr, last_block, last_msize);
2084 tiny_mag_ptr->mag_bytes_free_at_start = 0;
2085 }
2086 #endif
2087
2088 tiny_mag_ptr->mag_last_region = NULL;
2089 }
2090
2091 static int
2092 tiny_free_detach_region(szone_t *szone, magazine_t *tiny_mag_ptr, region_t r) {
2093 uintptr_t start = (uintptr_t)TINY_REGION_ADDRESS(r);
2094 uintptr_t current = start;
2095 uintptr_t limit = (uintptr_t)TINY_REGION_END(r);
2096 boolean_t is_free;
2097 msize_t msize;
2098 int total_alloc = 0;
2099
2100 while (current < limit) {
2101 msize = get_tiny_meta_header((void *)current, &is_free);
2102 if (is_free && !msize && (current == start)) {
2103 // first block is all free
2104 break;
2105 }
2106 if (!msize) {
2107 #if DEBUG_MALLOC
2108 malloc_printf("*** tiny_free_detach_region error with %p: msize=%d is_free =%d\n",
2109 (void *)current, msize, is_free);
2110 #endif
2111 break;
2112 }
2113 if (is_free) {
2114 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, (void *)current, msize);
2115 } else {
2116 total_alloc++;
2117 }
2118 current += TINY_BYTES_FOR_MSIZE(msize);
2119 }
2120 return total_alloc;
2121 }
2122
2123 static size_t
2124 tiny_free_reattach_region(szone_t *szone, magazine_t *tiny_mag_ptr, region_t r) {
2125 uintptr_t start = (uintptr_t)TINY_REGION_ADDRESS(r);
2126 uintptr_t current = start;
2127 uintptr_t limit = (uintptr_t)TINY_REGION_END(r);
2128 boolean_t is_free;
2129 msize_t msize;
2130 size_t total_alloc = 0;
2131
2132 while (current < limit) {
2133 msize = get_tiny_meta_header((void *)current, &is_free);
2134 if (is_free && !msize && (current == start)) {
2135 // first block is all free
2136 break;
2137 }
2138 if (!msize) {
2139 #if DEBUG_MALLOC
2140 malloc_printf("*** tiny_free_reattach_region error with %p: msize=%d is_free =%d\n",
2141 (void *)current, msize, is_free);
2142 #endif
2143 break;
2144 }
2145 if (is_free) {
2146 tiny_free_list_add_ptr(szone, tiny_mag_ptr, (void *)current, msize);
2147 } else {
2148 total_alloc += TINY_BYTES_FOR_MSIZE(msize);
2149 }
2150 current += TINY_BYTES_FOR_MSIZE(msize);
2151 }
2152 return total_alloc;
2153 }
2154
2155 typedef struct {
2156 uint8_t pnum, size;
2157 } tiny_pg_pair_t;
2158
2159 static void NOINLINE /* want private stack frame for automatic array */
2160 tiny_free_scan_madvise_free(szone_t *szone, magazine_t *depot_ptr, region_t r) {
2161 uintptr_t start = (uintptr_t)TINY_REGION_ADDRESS(r);
2162 uintptr_t current = start;
2163 uintptr_t limit = (uintptr_t)TINY_REGION_END(r);
2164 boolean_t is_free;
2165 msize_t msize;
2166 tiny_pg_pair_t advisory[((TINY_REGION_PAYLOAD_BYTES + vm_page_size - 1) >> vm_page_shift) >> 1]; // 256bytes stack allocated
2167 int advisories = 0;
2168
2169 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
2170 // management data.
2171 while (current < limit) {
2172 msize = get_tiny_meta_header((void *)current, &is_free);
2173 if (is_free && !msize && (current == start)) {
2174 // first block is all free
2175 #if DEBUG_MALLOC
2176 malloc_printf("*** tiny_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
2177 (void *)current, msize, is_free);
2178 #endif
2179 uintptr_t pgLo = round_page(start + sizeof(free_list_t) + sizeof(msize_t));
2180 uintptr_t pgHi = trunc_page(start + TINY_REGION_SIZE - sizeof(msize_t));
2181
2182 if (pgLo < pgHi) {
2183 advisory[advisories].pnum = (pgLo - start) >> vm_page_shift;
2184 advisory[advisories].size = (pgHi - pgLo) >> vm_page_shift;
2185 advisories++;
2186 }
2187 break;
2188 }
2189 if (!msize) {
2190 #if DEBUG_MALLOC
2191 malloc_printf("*** tiny_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
2192 (void *)current, msize, is_free);
2193 #endif
2194 break;
2195 }
2196 if (is_free) {
2197 uintptr_t pgLo = round_page(current + sizeof(free_list_t) + sizeof(msize_t));
2198 uintptr_t pgHi = trunc_page(current + TINY_BYTES_FOR_MSIZE(msize) - sizeof(msize_t));
2199
2200 if (pgLo < pgHi) {
2201 advisory[advisories].pnum = (pgLo - start) >> vm_page_shift;
2202 advisory[advisories].size = (pgHi - pgLo) >> vm_page_shift;
2203 advisories++;
2204 }
2205 }
2206 current += TINY_BYTES_FOR_MSIZE(msize);
2207 }
2208
2209 if (advisories > 0) {
2210 int i;
2211
2212 // So long as the following hold for this region:
2213 // (1) No malloc()'s are ever performed from the depot (hence free pages remain free,)
2214 // (2) The region is not handed over to a per-CPU magazine (where malloc()'s could be performed),
2215 // (3) The entire region is not mumap()'d (so the madvise's are applied to the intended addresses),
2216 // then the madvise opportunities collected just above can be applied outside all locks.
2217 // (1) is ensured by design, (2) and (3) are ensured by bumping the globally visible counter node->pinned_to_depot.
2218
2219 OSAtomicIncrement32Barrier(&(REGION_TRAILER_FOR_TINY_REGION(r)->pinned_to_depot));
2220 SZONE_MAGAZINE_PTR_UNLOCK(szone, depot_ptr);
2221 for (i = 0; i < advisories; ++i) {
2222 uintptr_t addr = (advisory[i].pnum << vm_page_shift) + start;
2223 size_t size = advisory[i].size << vm_page_shift;
2224
2225 #if TARGET_OS_EMBEDDED
2226 madvise_free_range(szone, r, addr, addr + size, NULL);
2227 #else
2228 madvise_free_range(szone, r, addr, addr + size);
2229 #endif
2230 }
2231 SZONE_MAGAZINE_PTR_LOCK(szone, depot_ptr);
2232 OSAtomicDecrement32Barrier(&(REGION_TRAILER_FOR_TINY_REGION(r)->pinned_to_depot));
2233 }
2234 }
2235
2236 static region_t
2237 tiny_free_try_depot_unmap_no_lock(szone_t *szone, magazine_t *depot_ptr, region_trailer_t *node)
2238 {
2239 if (0 < node->bytes_used ||
2240 0 < node->pinned_to_depot ||
2241 depot_ptr->recirculation_entries < (szone->num_tiny_magazines * 2)) {
2242 return NULL;
2243 }
2244
2245 // disconnect node from Depot
2246 recirc_list_extract(szone, depot_ptr, node);
2247
2248 // Iterate the region pulling its free entries off the (locked) Depot's free list
2249 region_t sparse_region = TINY_REGION_FOR_PTR(node);
2250 int objects_in_use = tiny_free_detach_region(szone, depot_ptr, sparse_region);
2251
2252 if (0 == objects_in_use) {
2253 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
2254 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
2255 rgnhdl_t pSlot = hash_lookup_region_no_lock(szone->tiny_region_generation->hashed_regions,
2256 szone->tiny_region_generation->num_regions_allocated,
2257 szone->tiny_region_generation->num_regions_allocated_shift, sparse_region);
2258 if (NULL == pSlot) {
2259 szone_error(szone, 1, "tiny_free_try_depot_unmap_no_lock hash lookup failed:", NULL, "%p\n", sparse_region);
2260 return NULL;
2261 }
2262 *pSlot = HASHRING_REGION_DEALLOCATED;
2263 depot_ptr->num_bytes_in_magazine -= TINY_REGION_PAYLOAD_BYTES;
2264 __sync_fetch_and_add( &(szone->num_tiny_regions_dealloc), 1); // Atomically increment num_tiny_regions_dealloc
2265
2266 // Caller will transfer ownership of the region back to the OS with no locks held
2267 MAGMALLOC_DEALLOCREGION((void *)szone, (void *)sparse_region, TINY_REGION_SIZE); // DTrace USDT Probe
2268 return sparse_region;
2269 } else {
2270 szone_error(szone, 1, "tiny_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL, "%d\n", objects_in_use);
2271 return NULL;
2272 }
2273 }
2274
2275 static boolean_t
2276 tiny_free_do_recirc_to_depot(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index)
2277 {
2278 // The entire magazine crossed the "emptiness threshold". Transfer a region
2279 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2280 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2281 region_trailer_t *node = tiny_mag_ptr->firstNode;
2282
2283 while (node && !node->recirc_suitable) {
2284 node = node->next;
2285 }
2286
2287 if (NULL == node) {
2288 #if DEBUG_MALLOC
2289 malloc_printf("*** tiny_free_do_recirc_to_depot end of list\n");
2290 #endif
2291 return TRUE; // Caller must SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
2292 }
2293
2294 region_t sparse_region = TINY_REGION_FOR_PTR(node);
2295
2296 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at_start
2297 if (sparse_region == tiny_mag_ptr->mag_last_region && (tiny_mag_ptr->mag_bytes_free_at_end || tiny_mag_ptr->mag_bytes_free_at_start)) {
2298 tiny_finalize_region(szone, tiny_mag_ptr);
2299 }
2300
2301 // disconnect "suitable" node from magazine
2302 recirc_list_extract(szone, tiny_mag_ptr, node);
2303
2304 // Iterate the region pulling its free entries off its (locked) magazine's free list
2305 int objects_in_use = tiny_free_detach_region(szone, tiny_mag_ptr, sparse_region);
2306 magazine_t *depot_ptr = &(szone->tiny_magazines[DEPOT_MAGAZINE_INDEX]);
2307
2308 // hand over the region to the (locked) Depot
2309 SZONE_MAGAZINE_PTR_LOCK(szone,depot_ptr);
2310 // this will cause tiny_free_list_add_ptr called by tiny_free_reattach_region to use
2311 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
2312 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region) = DEPOT_MAGAZINE_INDEX;
2313 node->pinned_to_depot = 0;
2314
2315 // Iterate the region putting its free entries on Depot's free list
2316 size_t bytes_inplay = tiny_free_reattach_region(szone, depot_ptr, sparse_region);
2317
2318 tiny_mag_ptr->mag_num_bytes_in_objects -= bytes_inplay;
2319 tiny_mag_ptr->num_bytes_in_magazine -= TINY_REGION_PAYLOAD_BYTES;
2320 tiny_mag_ptr->mag_num_objects -= objects_in_use;
2321
2322 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr); // Unlock the originating magazine
2323
2324 depot_ptr->mag_num_bytes_in_objects += bytes_inplay;
2325 depot_ptr->num_bytes_in_magazine += TINY_REGION_PAYLOAD_BYTES;
2326 depot_ptr->mag_num_objects += objects_in_use;
2327
2328 // connect to Depot as last node
2329 recirc_list_splice_last(szone, depot_ptr, node);
2330
2331 MAGMALLOC_RECIRCREGION((void *)szone, (int)mag_index, (void *)sparse_region, TINY_REGION_SIZE,
2332 (int)BYTES_USED_FOR_TINY_REGION(sparse_region)); // DTrace USDT Probe
2333
2334 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
2335 tiny_free_scan_madvise_free(szone, depot_ptr, sparse_region);
2336
2337 // If the region is entirely empty vm_deallocate() it outside the depot lock
2338 region_t r_dealloc = tiny_free_try_depot_unmap_no_lock(szone, depot_ptr, node);
2339 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
2340 if (r_dealloc)
2341 deallocate_pages(szone, r_dealloc, TINY_REGION_SIZE, 0);
2342 return FALSE; // Caller need not unlock the originating magazine
2343 }
2344
2345 static region_t
2346 tiny_find_msize_region(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, msize_t msize)
2347 {
2348 free_list_t *ptr;
2349 grain_t slot = msize - 1;
2350 free_list_t **free_list = tiny_mag_ptr->mag_free_list;
2351 free_list_t **the_slot = free_list + slot;
2352 free_list_t **limit;
2353 #if defined(__LP64__)
2354 uint64_t bitmap;
2355 #else
2356 uint32_t bitmap;
2357 #endif
2358 // Assumes we've locked the magazine
2359 CHECK_MAGAZINE_PTR_LOCKED(szone, tiny_mag_ptr, __PRETTY_FUNCTION__);
2360
2361 // Look for an exact match by checking the freelist for this msize.
2362 ptr = *the_slot;
2363 if (ptr)
2364 return TINY_REGION_FOR_PTR(ptr);
2365
2366 // Mask off the bits representing slots holding free blocks smaller than the
2367 // size we need. If there are no larger free blocks, try allocating from
2368 // the free space at the end of the tiny region.
2369 #if defined(__LP64__)
2370 bitmap = ((uint64_t *)(tiny_mag_ptr->mag_bitmap))[0] & ~ ((1ULL << slot) - 1);
2371 #else
2372 bitmap = tiny_mag_ptr->mag_bitmap[0] & ~ ((1 << slot) - 1);
2373 #endif
2374 if (!bitmap)
2375 return NULL;
2376
2377 slot = BITMAPV_CTZ(bitmap);
2378 limit = free_list + NUM_TINY_SLOTS - 1;
2379 free_list += slot;
2380
2381 if (free_list < limit) {
2382 ptr = *free_list;
2383 if (ptr)
2384 return TINY_REGION_FOR_PTR(ptr);
2385 else {
2386 /* Shouldn't happen. Fall through to look at last slot. */
2387 #if DEBUG_MALLOC
2388 malloc_printf("in tiny_find_msize_region(), mag_bitmap out of sync, slot=%d\n",slot);
2389 #endif
2390 }
2391 }
2392
2393 // We are now looking at the last slot, which contains blocks equal to, or
2394 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
2395 ptr = *limit;
2396 if (ptr)
2397 return TINY_REGION_FOR_PTR(ptr);
2398
2399 return NULL;
2400 }
2401
2402 static boolean_t
2403 tiny_get_region_from_depot(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, msize_t msize)
2404 {
2405 magazine_t *depot_ptr = &(szone->tiny_magazines[DEPOT_MAGAZINE_INDEX]);
2406
2407 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2408 if (szone->num_tiny_magazines == 1) // Uniprocessor, single magazine, so no recirculation necessary
2409 return 0;
2410
2411 #if DEBUG_MALLOC
2412 if (DEPOT_MAGAZINE_INDEX == mag_index) {
2413 szone_error(szone, 1, "tiny_get_region_from_depot called for magazine index -1", NULL, NULL);
2414 return 0;
2415 }
2416 #endif
2417
2418 SZONE_MAGAZINE_PTR_LOCK(szone,depot_ptr);
2419
2420 // Appropriate a Depot'd region that can satisfy requested msize.
2421 region_trailer_t *node;
2422 region_t sparse_region;
2423
2424 while (1) {
2425 sparse_region = tiny_find_msize_region(szone, depot_ptr, DEPOT_MAGAZINE_INDEX, msize);
2426 if (NULL == sparse_region) { // Depot empty?
2427 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
2428 return 0;
2429 }
2430
2431 node = REGION_TRAILER_FOR_TINY_REGION(sparse_region);
2432 if (0 >= node->pinned_to_depot)
2433 break;
2434
2435 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
2436 pthread_yield_np();
2437 SZONE_MAGAZINE_PTR_LOCK(szone,depot_ptr);
2438 }
2439
2440 // disconnect node from Depot
2441 recirc_list_extract(szone, depot_ptr, node);
2442
2443 // Iterate the region pulling its free entries off the (locked) Depot's free list
2444 int objects_in_use = tiny_free_detach_region(szone, depot_ptr, sparse_region);
2445
2446 // Transfer ownership of the region
2447 MAGAZINE_INDEX_FOR_TINY_REGION(sparse_region) = mag_index;
2448 node->pinned_to_depot = 0;
2449
2450 // Iterate the region putting its free entries on its new (locked) magazine's free list
2451 size_t bytes_inplay = tiny_free_reattach_region(szone, tiny_mag_ptr, sparse_region);
2452
2453 depot_ptr->mag_num_bytes_in_objects -= bytes_inplay;
2454 depot_ptr->num_bytes_in_magazine -= TINY_REGION_PAYLOAD_BYTES;
2455 depot_ptr->mag_num_objects -= objects_in_use;
2456
2457 tiny_mag_ptr->mag_num_bytes_in_objects += bytes_inplay;
2458 tiny_mag_ptr->num_bytes_in_magazine += TINY_REGION_PAYLOAD_BYTES;
2459 tiny_mag_ptr->mag_num_objects += objects_in_use;
2460
2461 // connect to magazine as first node
2462 recirc_list_splice_first(szone, tiny_mag_ptr, node);
2463
2464 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
2465
2466 // madvise() outside the Depot lock
2467 #if TARGET_OS_EMBEDDED
2468 if (node->failedREUSE) {
2469 #else
2470 if (node->failedREUSE ||
2471 -1 == madvise((void *)sparse_region, TINY_REGION_PAYLOAD_BYTES, MADV_FREE_REUSE)) {
2472 #endif
2473 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
2474 #if DEBUG_MADVISE
2475 szone_error(szone, 0, "tiny_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed",
2476 sparse_region, "length=%d\n", TINY_REGION_PAYLOAD_BYTES);
2477 #endif
2478 node->failedREUSE = TRUE;
2479 }
2480
2481 MAGMALLOC_DEPOTREGION((void *)szone, (int)mag_index, (void *)sparse_region, TINY_REGION_SIZE,
2482 (int)BYTES_USED_FOR_TINY_REGION(sparse_region)); // DTrace USDT Probe
2483
2484 return 1;
2485 }
2486
2487 #define K 1.5 // headroom measured in number of 1Mb regions
2488 #define DENSITY_THRESHOLD(a) \
2489 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
2490
2491 static INLINE boolean_t
2492 tiny_free_no_lock(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, region_t region, void *ptr,
2493 msize_t msize)
2494 {
2495 void *original_ptr = ptr;
2496 size_t original_size = TINY_BYTES_FOR_MSIZE(msize);
2497 void *next_block = ((unsigned char *)ptr + original_size);
2498 msize_t previous_msize, next_msize;
2499 void *previous;
2500 free_list_t *big_free_block;
2501 free_list_t *after_next_block;
2502 free_list_t *before_next_block;
2503
2504 #if DEBUG_MALLOC
2505 if (LOG(szone,ptr)) {
2506 malloc_printf("in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
2507 }
2508 if (!msize) {
2509 szone_error(szone, 1, "trying to free tiny block that is too small", ptr,
2510 "in tiny_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
2511 }
2512 #endif
2513
2514 // We try to coalesce this block with the preceeding one
2515 previous = tiny_previous_preceding_free(ptr, &previous_msize);
2516 if (previous) {
2517 #if DEBUG_MALLOC
2518 if (LOG(szone, ptr) || LOG(szone,previous)) {
2519 malloc_printf("in tiny_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr, previous);
2520 }
2521 #endif
2522
2523 // clear the meta_header since this is no longer the start of a block
2524 set_tiny_meta_header_middle(ptr);
2525 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, previous, previous_msize);
2526 ptr = previous;
2527 msize += previous_msize;
2528 }
2529 // We try to coalesce with the next block
2530 if ((next_block < TINY_REGION_END(region)) && tiny_meta_header_is_free(next_block)) {
2531 next_msize = get_tiny_free_size(next_block);
2532 #if DEBUG_MALLOC
2533 if (LOG(szone, ptr) || LOG(szone, next_block)) {
2534 malloc_printf("in tiny_free_no_lock(), for ptr=%p, msize=%d coalesced forward=%p next_msize=%d\n",
2535 ptr, msize, next_block, next_msize);
2536 }
2537 #endif
2538 // If we are coalescing with the next block, and the next block is in
2539 // the last slot of the free list, then we optimize this case here to
2540 // avoid removing next_block from the slot (NUM_TINY_SLOTS - 1) and then adding ptr back
2541 // to slot (NUM_TINY_SLOTS - 1).
2542 if (next_msize >= NUM_TINY_SLOTS) {
2543 msize += next_msize;
2544
2545 big_free_block = (free_list_t *)next_block;
2546 after_next_block = free_list_unchecksum_ptr(szone, &big_free_block->next);
2547 before_next_block = free_list_unchecksum_ptr(szone, &big_free_block->previous);
2548
2549 if (!before_next_block) {
2550 tiny_mag_ptr->mag_free_list[NUM_TINY_SLOTS-1] = ptr;
2551 } else {
2552 before_next_block->next.u = free_list_checksum_ptr(szone, ptr);
2553 }
2554
2555 if (after_next_block) {
2556 after_next_block->previous.u = free_list_checksum_ptr(szone, ptr);
2557 }
2558
2559 // we don't need to checksum these since they are already checksummed
2560 ((free_list_t *)ptr)->previous = big_free_block->previous;
2561 ((free_list_t *)ptr)->next = big_free_block->next;
2562
2563 // clear the meta_header to enable coalescing backwards
2564 set_tiny_meta_header_middle(big_free_block);
2565 set_tiny_meta_header_free(ptr, msize);
2566
2567 goto tiny_free_ending;
2568 }
2569 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, next_block, next_msize);
2570 set_tiny_meta_header_middle(next_block); // clear the meta_header to enable coalescing backwards
2571 msize += next_msize;
2572 }
2573
2574 // The tiny cache already scribbles free blocks as they go through the
2575 // cache whenever msize < TINY_QUANTUM , so we do not need to do it here.
2576 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && msize && (msize >= TINY_QUANTUM))
2577 memset(ptr, 0x55, TINY_BYTES_FOR_MSIZE(msize));
2578
2579 tiny_free_list_add_ptr(szone, tiny_mag_ptr, ptr, msize);
2580
2581 tiny_free_ending:
2582
2583 tiny_mag_ptr->mag_num_objects--;
2584 // we use original_size and not msize to avoid double counting the coalesced blocks
2585 tiny_mag_ptr->mag_num_bytes_in_objects -= original_size;
2586
2587 // Update this region's bytes in use count
2588 region_trailer_t *node = REGION_TRAILER_FOR_TINY_REGION(region);
2589 size_t bytes_used = node->bytes_used - original_size;
2590 node->bytes_used = bytes_used;
2591
2592 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
2593 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
2594 if (szone->num_tiny_magazines == 1) { // Uniprocessor, single magazine, so no recirculation necessary
2595 /* NOTHING */
2596 } else if (DEPOT_MAGAZINE_INDEX != mag_index) {
2597 // Emptiness discriminant
2598 if (bytes_used < DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES)) {
2599 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
2600 recirculation candidates list. */
2601 node->recirc_suitable = TRUE;
2602 } else {
2603 /* After this free, we've found the region is still dense, so it must have been even more so before
2604 the free. That implies the region is already correctly marked. Do nothing. */
2605 }
2606
2607 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
2608 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
2609 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
2610 size_t a = tiny_mag_ptr->num_bytes_in_magazine; // Total bytes allocated to this magazine
2611 size_t u = tiny_mag_ptr->mag_num_bytes_in_objects; // In use (malloc'd) from this magaqzine
2612
2613 if (a - u > ((3 * TINY_REGION_PAYLOAD_BYTES) / 2) && u < DENSITY_THRESHOLD(a)) {
2614 return tiny_free_do_recirc_to_depot(szone, tiny_mag_ptr, mag_index);
2615 }
2616
2617 } else {
2618 #endif
2619 // Freed to Depot. N.B. Lock on tiny_magazines[DEPOT_MAGAZINE_INDEX] is already held
2620 // Calcuate the first page in the coalesced block that would be safe to mark MADV_FREE
2621 uintptr_t safe_ptr = (uintptr_t)ptr + sizeof(free_list_t) + sizeof(msize_t);
2622 uintptr_t round_safe = round_page(safe_ptr);
2623
2624 // Calcuate the last page in the coalesced block that would be safe to mark MADV_FREE
2625 uintptr_t safe_extent = (uintptr_t)ptr + TINY_BYTES_FOR_MSIZE(msize) - sizeof(msize_t);
2626 uintptr_t trunc_extent = trunc_page(safe_extent);
2627
2628 // The newly freed block may complete a span of bytes that cover a page. Mark it with MADV_FREE.
2629 if (round_safe < trunc_extent) { // Safe area covers a page (perhaps many)
2630 uintptr_t lo = trunc_page((uintptr_t)original_ptr);
2631 uintptr_t hi = round_page((uintptr_t)original_ptr + original_size);
2632
2633 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, ptr, msize);
2634 set_tiny_meta_header_in_use(ptr, msize);
2635
2636 OSAtomicIncrement32Barrier(&(node->pinned_to_depot));
2637 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
2638 #if TARGET_OS_EMBEDDED
2639 madvise_free_range(szone, region, MAX(round_safe, lo), MIN(trunc_extent, hi), &szone->last_tiny_advise);
2640 #else
2641 madvise_free_range(szone, region, MAX(round_safe, lo), MIN(trunc_extent, hi));
2642 #endif
2643 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
2644 OSAtomicDecrement32Barrier(&(node->pinned_to_depot));
2645
2646 set_tiny_meta_header_free(ptr, msize);
2647 tiny_free_list_add_ptr(szone, tiny_mag_ptr, ptr, msize);
2648 }
2649
2650 #if !TARGET_OS_EMBEDDED
2651 if (0 < bytes_used || 0 < node->pinned_to_depot) {
2652 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
2653 so as to avoid thrashing between the Depot's free list and a magazines's free list
2654 with detach_region/reattach_region */
2655 } else {
2656 /* Depot'd region is just now empty. Consider return to OS. */
2657 region_t r_dealloc = tiny_free_try_depot_unmap_no_lock(szone, tiny_mag_ptr, node);
2658 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
2659 if (r_dealloc)
2660 deallocate_pages(szone, r_dealloc, TINY_REGION_SIZE, 0);
2661 return FALSE; // Caller need not unlock
2662 }
2663 }
2664 #endif
2665
2666 return TRUE; // Caller must do SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr)
2667 }
2668
2669 // Allocates from the last region or a freshly allocated region
2670 static void *
2671 tiny_malloc_from_region_no_lock(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index,
2672 msize_t msize, void * aligned_address)
2673 {
2674 void *ptr;
2675
2676 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at_start
2677 if (tiny_mag_ptr->mag_bytes_free_at_end || tiny_mag_ptr->mag_bytes_free_at_start)
2678 tiny_finalize_region(szone, tiny_mag_ptr);
2679
2680 // We set the unused bits of the header in the last pair to be all ones, and those of the inuse to zeroes.
2681 ((tiny_region_t)aligned_address)->pairs[CEIL_NUM_TINY_BLOCKS_WORDS-1].header =
2682 (NUM_TINY_BLOCKS & 31) ? (0xFFFFFFFFU << (NUM_TINY_BLOCKS & 31)) : 0;
2683 ((tiny_region_t)aligned_address)->pairs[CEIL_NUM_TINY_BLOCKS_WORDS-1].inuse = 0;
2684
2685 // Here find the only place in tinyland that (infrequently) takes the tiny_regions_lock.
2686 // Only one thread at a time should be permitted to assess the density of the hash
2687 // ring and adjust if needed.
2688 // Only one thread at a time should be permitted to insert its new region on
2689 // the hash ring.
2690 // It is safe for all other threads to read the hash ring (hashed_regions) and
2691 // the associated sizes (num_regions_allocated and num_tiny_regions).
2692
2693 LOCK(szone->tiny_regions_lock);
2694
2695 // Check to see if the hash ring of tiny regions needs to grow. Try to
2696 // avoid the hash ring becoming too dense.
2697 if (szone->tiny_region_generation->num_regions_allocated < (2 * szone->num_tiny_regions)) {
2698 region_t *new_regions;
2699 size_t new_size;
2700 size_t new_shift = szone->tiny_region_generation->num_regions_allocated_shift; // In/Out parameter
2701 new_regions = hash_regions_grow_no_lock(szone, szone->tiny_region_generation->hashed_regions,
2702 szone->tiny_region_generation->num_regions_allocated,
2703 &new_shift,
2704 &new_size);
2705 // Do not deallocate the current hashed_regions allocation since someone may
2706 // be iterating it. Instead, just leak it.
2707
2708 // Prepare to advance to the "next generation" of the hash ring.
2709 szone->tiny_region_generation->nextgen->hashed_regions = new_regions;
2710 szone->tiny_region_generation->nextgen->num_regions_allocated = new_size;
2711 szone->tiny_region_generation->nextgen->num_regions_allocated_shift = new_shift;
2712
2713 // Throw the switch to atomically advance to the next generation.
2714 szone->tiny_region_generation = szone->tiny_region_generation->nextgen;
2715 // Ensure everyone sees the advance.
2716 OSMemoryBarrier();
2717 }
2718 // Tag the region at "aligned_address" as belonging to us,
2719 // and so put it under the protection of the magazine lock we are holding.
2720 // Do this before advertising "aligned_address" on the hash ring(!)
2721 MAGAZINE_INDEX_FOR_TINY_REGION(aligned_address) = mag_index;
2722
2723 // Insert the new region into the hash ring, and update malloc statistics
2724 hash_region_insert_no_lock(szone->tiny_region_generation->hashed_regions,
2725 szone->tiny_region_generation->num_regions_allocated,
2726 szone->tiny_region_generation->num_regions_allocated_shift,
2727 aligned_address);
2728
2729 szone->num_tiny_regions++;
2730 UNLOCK(szone->tiny_regions_lock);
2731
2732 tiny_mag_ptr->mag_last_region = aligned_address;
2733 BYTES_USED_FOR_TINY_REGION(aligned_address) = TINY_BYTES_FOR_MSIZE(msize);
2734 #if ASLR_INTERNAL
2735 int offset_msize = malloc_entropy[0] & TINY_ENTROPY_MASK;
2736 #if DEBUG_MALLOC
2737 if (getenv("MallocASLRForce")) offset_msize = strtol(getenv("MallocASLRForce"), NULL, 0) & TINY_ENTROPY_MASK;
2738 if (getenv("MallocASLRPrint")) malloc_printf("Region: %p offset: %d\n", aligned_address, offset_msize);
2739 #endif
2740 #else
2741 int offset_msize = 0;
2742 #endif
2743 ptr = (void *)((uintptr_t) aligned_address + TINY_BYTES_FOR_MSIZE(offset_msize));
2744 set_tiny_meta_header_in_use(ptr, msize);
2745 tiny_mag_ptr->mag_num_objects++;
2746 tiny_mag_ptr->mag_num_bytes_in_objects += TINY_BYTES_FOR_MSIZE(msize);
2747 tiny_mag_ptr->num_bytes_in_magazine += TINY_REGION_PAYLOAD_BYTES;
2748
2749 // We put a header on the last block so that it appears in use (for coalescing, etc...)
2750 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr + TINY_BYTES_FOR_MSIZE(msize)));
2751 tiny_mag_ptr->mag_bytes_free_at_end = TINY_BYTES_FOR_MSIZE(NUM_TINY_BLOCKS - msize - offset_msize);
2752
2753 #if ASLR_INTERNAL
2754 // Put a header on the previous block for same reason
2755 tiny_mag_ptr->mag_bytes_free_at_start = TINY_BYTES_FOR_MSIZE(offset_msize);
2756 if (offset_msize) {
2757 set_tiny_meta_header_in_use_1((void *)((uintptr_t)ptr - TINY_QUANTUM));
2758 }
2759 #else
2760 tiny_mag_ptr->mag_bytes_free_at_start = 0;
2761 #endif
2762
2763 // connect to magazine as last node
2764 recirc_list_splice_last(szone, tiny_mag_ptr, REGION_TRAILER_FOR_TINY_REGION(aligned_address));
2765
2766 #if DEBUG_MALLOC
2767 if (LOG(szone,ptr)) {
2768 malloc_printf("in tiny_malloc_from_region_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
2769 }
2770 #endif
2771 return ptr;
2772 }
2773
2774 static INLINE void *
2775 tiny_try_shrink_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_good_size)
2776 {
2777 msize_t new_msize = TINY_MSIZE_FOR_BYTES(new_good_size);
2778 msize_t mshrinkage = TINY_MSIZE_FOR_BYTES(old_size) - new_msize;
2779
2780 if (mshrinkage) {
2781 void *q = (void *)((uintptr_t)ptr + TINY_BYTES_FOR_MSIZE(new_msize));
2782 magazine_t *tiny_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->tiny_magazines,
2783 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr)),
2784 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr)));
2785
2786 // Mark q as block header and in-use, thus creating two blocks.
2787 set_tiny_meta_header_in_use(q, mshrinkage);
2788 tiny_mag_ptr->mag_num_objects++;
2789
2790 SZONE_MAGAZINE_PTR_UNLOCK(szone,tiny_mag_ptr);
2791 szone_free(szone, q); // avoid inlining free_tiny(szone, q, ...);
2792 }
2793 return ptr;
2794 }
2795
2796 static INLINE boolean_t
2797 tiny_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size)
2798 {
2799 // returns 1 on success
2800 msize_t index;
2801 msize_t old_msize;
2802 unsigned next_index;
2803 void *next_block;
2804 boolean_t is_free;
2805 msize_t next_msize, coalesced_msize, leftover_msize;
2806 void *leftover;
2807
2808 index = TINY_INDEX_FOR_PTR(ptr);
2809 old_msize = TINY_MSIZE_FOR_BYTES(old_size);
2810 next_index = index + old_msize;
2811
2812 if (next_index >= NUM_TINY_BLOCKS) {
2813 return 0;
2814 }
2815 next_block = (char *)ptr + old_size;
2816
2817 magazine_t *tiny_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->tiny_magazines,
2818 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr)),
2819 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr)));
2820
2821 /*
2822 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
2823 * it.
2824 */
2825 is_free = tiny_meta_header_is_free(next_block);
2826 if (!is_free) {
2827 SZONE_MAGAZINE_PTR_UNLOCK(szone,tiny_mag_ptr);
2828 return 0; // next_block is in use;
2829 }
2830 next_msize = get_tiny_free_size(next_block);
2831 if (old_size + TINY_BYTES_FOR_MSIZE(next_msize) < new_size) {
2832 SZONE_MAGAZINE_PTR_UNLOCK(szone,tiny_mag_ptr);
2833 return 0; // even with next block, not enough
2834 }
2835 /*
2836 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
2837 * our needs.
2838 */
2839 tiny_free_list_remove_ptr(szone, tiny_mag_ptr, next_block, next_msize);
2840 set_tiny_meta_header_middle(next_block); // clear the meta_header to enable coalescing backwards
2841 coalesced_msize = TINY_MSIZE_FOR_BYTES(new_size - old_size + TINY_QUANTUM - 1);
2842 leftover_msize = next_msize - coalesced_msize;
2843 if (leftover_msize) {
2844 /* there's some left, so put the remainder back */
2845 leftover = (void *)((uintptr_t)next_block + TINY_BYTES_FOR_MSIZE(coalesced_msize));
2846
2847 tiny_free_list_add_ptr(szone, tiny_mag_ptr, leftover, leftover_msize);
2848 }
2849 set_tiny_meta_header_in_use(ptr, old_msize + coalesced_msize);
2850 #if DEBUG_MALLOC
2851 if (LOG(szone,ptr)) {
2852 malloc_printf("in tiny_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr, old_msize + coalesced_msize);
2853 }
2854 #endif
2855 tiny_mag_ptr->mag_num_bytes_in_objects += TINY_BYTES_FOR_MSIZE(coalesced_msize);
2856
2857 // Update this region's bytes in use count
2858 region_trailer_t *node = REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr));
2859 size_t bytes_used = node->bytes_used + TINY_BYTES_FOR_MSIZE(coalesced_msize);
2860 node->bytes_used = bytes_used;
2861
2862 // Emptiness discriminant
2863 if (bytes_used < DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES)) {
2864 /* After this reallocation the region is still sparse, so it must have been even more so before
2865 the reallocation. That implies the region is already correctly marked. Do nothing. */
2866 } else {
2867 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
2868 recirculation candidates list. */
2869 node->recirc_suitable = FALSE;
2870 }
2871
2872 SZONE_MAGAZINE_PTR_UNLOCK(szone,tiny_mag_ptr);
2873 CHECK(szone, __PRETTY_FUNCTION__);
2874 return 1;
2875 }
2876
2877 static boolean_t
2878 tiny_check_region(szone_t *szone, region_t region)
2879 {
2880 uintptr_t start, ptr, region_end;
2881 boolean_t prev_free = 0;
2882 boolean_t is_free;
2883 msize_t msize;
2884 free_list_t *free_head;
2885 void *follower, *previous, *next;
2886 mag_index_t mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(region);
2887 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
2888
2889 // Assumes locked
2890 CHECK_MAGAZINE_PTR_LOCKED(szone, tiny_mag_ptr, __PRETTY_FUNCTION__);
2891
2892 /* establish region limits */
2893 start = (uintptr_t)TINY_REGION_ADDRESS(region);
2894 ptr = start;
2895 if (region == tiny_mag_ptr->mag_last_region) {
2896 ptr += tiny_mag_ptr->mag_bytes_free_at_start;
2897
2898 /*
2899 * Check the leading block's integrity here also.
2900 */
2901 if (tiny_mag_ptr->mag_bytes_free_at_start) {
2902 msize = get_tiny_meta_header((void *)(ptr - TINY_QUANTUM), &is_free);
2903 if (is_free || (msize != 1)) {
2904 malloc_printf("*** invariant broken for leader block %p - %d %d\n", ptr - TINY_QUANTUM, msize, is_free);
2905 }
2906 }
2907 }
2908 region_end = (uintptr_t)TINY_REGION_END(region);
2909
2910 /*
2911 * The last region may have a trailing chunk which has not been converted into inuse/freelist
2912 * blocks yet.
2913 */
2914 if (region == tiny_mag_ptr->mag_last_region)
2915 region_end -= tiny_mag_ptr->mag_bytes_free_at_end;
2916
2917 /*
2918 * Scan blocks within the region.
2919 */
2920 while (ptr < region_end) {
2921 /*
2922 * If the first block is free, and its size is 65536 (msize = 0) then the entire region is
2923 * free.
2924 */
2925 msize = get_tiny_meta_header((void *)ptr, &is_free);
2926 if (is_free && !msize && (ptr == start)) {
2927 return 1;
2928 }
2929
2930 /*
2931 * If the block's size is 65536 (msize = 0) then since we're not the first entry the size is
2932 * corrupt.
2933 */
2934 if (!msize) {
2935 malloc_printf("*** invariant broken for tiny block %p this msize=%d - size is too small\n",
2936 ptr, msize);
2937 return 0;
2938 }
2939
2940 if (!is_free) {
2941 /*
2942 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
2943 */
2944 prev_free = 0;
2945 if (msize > (NUM_TINY_SLOTS - 1)) {
2946 malloc_printf("*** invariant broken for %p this tiny msize=%d - size is too large\n",
2947 ptr, msize);
2948 return 0;
2949 }
2950 /* move to next block */
2951 ptr += TINY_BYTES_FOR_MSIZE(msize);
2952 } else {
2953 #if !RELAXED_INVARIANT_CHECKS
2954 /*
2955 * Free blocks must have been coalesced, we cannot have a free block following another
2956 * free block.
2957 */
2958 if (prev_free) {
2959 malloc_printf("*** invariant broken for free block %p this tiny msize=%d: two free blocks in a row\n",
2960 ptr, msize);
2961 return 0;
2962 }
2963 #endif // RELAXED_INVARIANT_CHECKS
2964 prev_free = 1;
2965 /*
2966 * Check the integrity of this block's entry in its freelist.
2967 */
2968 free_head = (free_list_t *)ptr;
2969 previous = free_list_unchecksum_ptr(szone, &free_head->previous);
2970 next = free_list_unchecksum_ptr(szone, &free_head->next);
2971 if (previous && !tiny_meta_header_is_free(previous)) {
2972 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
2973 ptr, previous);
2974 return 0;
2975 }
2976 if (next && !tiny_meta_header_is_free(next)) {
2977 malloc_printf("*** invariant broken for %p (next in free list %p is not a free pointer)\n",
2978 ptr, next);
2979 return 0;
2980 }
2981 /*
2982 * Check the free block's trailing size value.
2983 */
2984 follower = FOLLOWING_TINY_PTR(ptr, msize);
2985 if (((uintptr_t)follower != region_end) && (get_tiny_previous_free_msize(follower) != msize)) {
2986 malloc_printf("*** invariant broken for tiny free %p followed by %p in region [%p-%p] "
2987 "(end marker incorrect) should be %d; in fact %d\n",
2988 ptr, follower, TINY_REGION_ADDRESS(region), region_end, msize, get_tiny_previous_free_msize(follower));
2989 return 0;
2990 }
2991 /* move to next block */
2992 ptr = (uintptr_t)follower;
2993 }
2994 }
2995 /*
2996 * Ensure that we scanned the entire region
2997 */
2998 if (ptr != region_end) {
2999 malloc_printf("*** invariant broken for region end %p - %p\n", ptr, region_end);
3000 return 0;
3001 }
3002 /*
3003 * Check the trailing block's integrity.
3004 */
3005 if (region == tiny_mag_ptr->mag_last_region) {
3006 if (tiny_mag_ptr->mag_bytes_free_at_end) {
3007 msize = get_tiny_meta_header((void *)ptr, &is_free);
3008 if (is_free || (msize != 1)) {
3009 malloc_printf("*** invariant broken for blocker block %p - %d %d\n", ptr, msize, is_free);
3010 }
3011 }
3012 }
3013 return 1;
3014 }
3015
3016 static kern_return_t
3017 tiny_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone,
3018 memory_reader_t reader, vm_range_recorder_t recorder)
3019 {
3020 size_t num_regions;
3021 size_t index;
3022 region_t *regions;
3023 vm_range_t buffer[MAX_RECORDER_BUFFER];
3024 unsigned count = 0;
3025 kern_return_t err;
3026 region_t region;
3027 vm_range_t range;
3028 vm_range_t admin_range;
3029 vm_range_t ptr_range;
3030 unsigned char *mapped_region;
3031 uint32_t *block_header;
3032 uint32_t *in_use;
3033 unsigned block_index;
3034 unsigned block_limit;
3035 boolean_t is_free;
3036 msize_t msize;
3037 void *mapped_ptr;
3038 unsigned bit;
3039 magazine_t *tiny_mag_base = NULL;
3040
3041 region_hash_generation_t *trg_ptr;
3042 err = reader(task, (vm_address_t)szone->tiny_region_generation, sizeof(region_hash_generation_t), (void **)&trg_ptr);
3043 if (err) return err;
3044
3045 num_regions = trg_ptr->num_regions_allocated;
3046 err = reader(task, (vm_address_t)trg_ptr->hashed_regions, sizeof(region_t) * num_regions, (void **)&regions);
3047 if (err) return err;
3048
3049 if (type_mask & MALLOC_PTR_IN_USE_RANGE_TYPE) {
3050 // Map in all active magazines. Do this outside the iteration over regions.
3051 err = reader(task, (vm_address_t)(szone->tiny_magazines),
3052 szone->num_tiny_magazines*sizeof(magazine_t),(void **)&tiny_mag_base);
3053 if (err) return err;
3054 }
3055
3056 for (index = 0; index < num_regions; ++index) {
3057 region = regions[index];
3058 if (HASHRING_OPEN_ENTRY != region && HASHRING_REGION_DEALLOCATED != region) {
3059 range.address = (vm_address_t)TINY_REGION_ADDRESS(region);
3060 range.size = (vm_size_t)TINY_REGION_SIZE;
3061 if (type_mask & MALLOC_ADMIN_REGION_RANGE_TYPE) {
3062 admin_range.address = range.address + TINY_METADATA_START;
3063 admin_range.size = TINY_METADATA_SIZE;
3064 recorder(task, context, MALLOC_ADMIN_REGION_RANGE_TYPE, &admin_range, 1);
3065 }
3066 if (type_mask & (MALLOC_PTR_REGION_RANGE_TYPE | MALLOC_ADMIN_REGION_RANGE_TYPE)) {
3067 ptr_range.address = range.address;
3068 ptr_range.size = NUM_TINY_BLOCKS * TINY_QUANTUM;
3069 recorder(task, context, MALLOC_PTR_REGION_RANGE_TYPE, &ptr_range, 1);
3070 }
3071 if (type_mask & MALLOC_PTR_IN_USE_RANGE_TYPE) {
3072 void *mag_last_free;
3073 vm_address_t mag_last_free_ptr = 0;
3074 msize_t mag_last_free_msize = 0;
3075
3076 err = reader(task, range.address, range.size, (void **)&mapped_region);
3077 if (err)
3078 return err;
3079
3080 mag_index_t mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(mapped_region);
3081 magazine_t *tiny_mag_ptr = tiny_mag_base + mag_index;
3082
3083 if (DEPOT_MAGAZINE_INDEX != mag_index) {
3084 mag_last_free = tiny_mag_ptr->mag_last_free;
3085 if (mag_last_free) {
3086 mag_last_free_ptr = (uintptr_t) mag_last_free & ~(TINY_QUANTUM - 1);
3087 mag_last_free_msize = (uintptr_t) mag_last_free & (TINY_QUANTUM - 1);
3088 }
3089 } else {
3090 for (mag_index = 0; mag_index < szone->num_tiny_magazines; mag_index++) {
3091 if ((void *)range.address == (tiny_mag_base + mag_index)->mag_last_free_rgn) {
3092 mag_last_free = (tiny_mag_base + mag_index)->mag_last_free;
3093 if (mag_last_free) {
3094 mag_last_free_ptr = (uintptr_t) mag_last_free & ~(TINY_QUANTUM - 1);
3095 mag_last_free_msize = (uintptr_t) mag_last_free & (TINY_QUANTUM - 1);
3096 }
3097 }
3098 }
3099 }
3100
3101 block_header = (uint32_t *)(mapped_region + TINY_METADATA_START + sizeof(region_trailer_t));
3102 in_use = TINY_INUSE_FOR_HEADER(block_header);
3103 block_index = 0;
3104 block_limit = NUM_TINY_BLOCKS;
3105 if (region == tiny_mag_ptr->mag_last_region) {
3106 block_index += TINY_MSIZE_FOR_BYTES(tiny_mag_ptr->mag_bytes_free_at_start);
3107 block_limit -= TINY_MSIZE_FOR_BYTES(tiny_mag_ptr->mag_bytes_free_at_end);
3108 }
3109
3110 while (block_index < block_limit) {
3111 vm_size_t block_offset = TINY_BYTES_FOR_MSIZE(block_index);
3112 is_free = !BITARRAY_BIT(in_use, block_index);
3113 if (is_free) {
3114 mapped_ptr = mapped_region + block_offset;
3115
3116 // mapped_region, the address at which 'range' in 'task' has been
3117 // mapped into our process, is not necessarily aligned to
3118 // TINY_BLOCKS_ALIGN.
3119 //
3120 // Since the code in get_tiny_free_size() assumes the pointer came
3121 // from a properly aligned tiny region, and mapped_region is not
3122 // necessarily aligned, then do the size calculation directly.
3123 // If the next bit is set in the header bitmap, then the size is one
3124 // quantum. Otherwise, read the size field.
3125 if (!BITARRAY_BIT(block_header, (block_index+1)))
3126 msize = TINY_FREE_SIZE(mapped_ptr);
3127 else
3128 msize = 1;
3129
3130 } else if (range.address + block_offset != mag_last_free_ptr) {
3131 msize = 1;
3132 bit = block_index + 1;
3133 while (! BITARRAY_BIT(block_header, bit)) {
3134 bit++;
3135 msize ++;
3136 }
3137 buffer[count].address = range.address + block_offset;
3138 buffer[count].size = TINY_BYTES_FOR_MSIZE(msize);
3139 count++;
3140 if (count >= MAX_RECORDER_BUFFER) {
3141 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
3142 count = 0;
3143 }
3144 } else {
3145 // Block is not free but it matches mag_last_free_ptr so even
3146 // though it is not marked free in the bitmap, we treat it as if
3147 // it is and move on
3148 msize = mag_last_free_msize;
3149 }
3150
3151 if (!msize)
3152 return KERN_FAILURE; // Somethings amiss. Avoid looping at this block_index.
3153
3154 block_index += msize;
3155 }
3156 if (count) {
3157 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
3158 count = 0;
3159 }
3160 }
3161 }
3162 }
3163 return 0;
3164 }
3165
3166 static void *
3167 tiny_malloc_from_free_list(szone_t *szone, magazine_t *tiny_mag_ptr, mag_index_t mag_index, msize_t msize)
3168 {
3169 free_list_t *ptr;
3170 msize_t this_msize;
3171 grain_t slot = msize - 1;
3172 free_list_t **free_list = tiny_mag_ptr->mag_free_list;
3173 free_list_t **the_slot = free_list + slot;
3174 free_list_t *next;
3175 free_list_t **limit;
3176 #if defined(__LP64__)
3177 uint64_t bitmap;
3178 #else
3179 uint32_t bitmap;
3180 #endif
3181 msize_t leftover_msize;
3182 free_list_t *leftover_ptr;
3183
3184 // Assumes we've locked the region
3185 CHECK_MAGAZINE_PTR_LOCKED(szone, tiny_mag_ptr, __PRETTY_FUNCTION__);
3186
3187 // Look for an exact match by checking the freelist for this msize.
3188 //
3189 ptr = *the_slot;
3190 if (ptr) {
3191 next = free_list_unchecksum_ptr(szone, &ptr->next);
3192 if (next) {
3193 next->previous = ptr->previous;
3194 } else {
3195 BITMAPV_CLR(tiny_mag_ptr->mag_bitmap, slot);
3196 }
3197 *the_slot = next;
3198 this_msize = msize;
3199 #if DEBUG_MALLOC
3200 if (LOG(szone, ptr)) {
3201 malloc_printf("in tiny_malloc_from_free_list(), exact match ptr=%p, this_msize=%d\n", ptr, this_msize);
3202 }
3203 #endif
3204 goto return_tiny_alloc;
3205 }
3206
3207 // Mask off the bits representing slots holding free blocks smaller than the
3208 // size we need. If there are no larger free blocks, try allocating from
3209 // the free space at the end of the tiny region.
3210 #if defined(__LP64__)
3211 bitmap = ((uint64_t *)(tiny_mag_ptr->mag_bitmap))[0] & ~ ((1ULL << slot) - 1);
3212 #else
3213 bitmap = tiny_mag_ptr->mag_bitmap[0] & ~ ((1 << slot) - 1);
3214 #endif
3215 if (!bitmap)
3216 goto try_tiny_malloc_from_end;
3217
3218 slot = BITMAPV_CTZ(bitmap);
3219 limit = free_list + NUM_TINY_SLOTS - 1;
3220 free_list += slot;
3221
3222 if (free_list < limit) {
3223 ptr = *free_list;
3224 if (ptr) {
3225 next = free_list_unchecksum_ptr(szone, &ptr->next);
3226 *free_list = next;
3227 if (next) {
3228 next->previous = ptr->previous;
3229 } else {
3230 BITMAPV_CLR(tiny_mag_ptr->mag_bitmap, slot);
3231 }
3232 this_msize = get_tiny_free_size(ptr);
3233 goto add_leftover_and_proceed;
3234 }
3235 #if DEBUG_MALLOC
3236 malloc_printf("in tiny_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot);
3237 #endif
3238 }
3239
3240 // We are now looking at the last slot, which contains blocks equal to, or
3241 // due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
3242 // If the last freelist is not empty, and the head contains a block that is
3243 // larger than our request, then the remainder is put back on the free list.
3244 ptr = *limit;
3245 if (ptr) {
3246 this_msize = get_tiny_free_size(ptr);
3247 next = free_list_unchecksum_ptr(szone, &ptr->next);
3248 if (this_msize - msize >= NUM_TINY_SLOTS) {
3249 // the leftover will go back to the free list, so we optimize by
3250 // modifying the free list rather than a pop and push of the head
3251 leftover_msize = this_msize - msize;
3252 leftover_ptr = (free_list_t *)((unsigned char *)ptr + TINY_BYTES_FOR_MSIZE(msize));
3253 *limit = leftover_ptr;
3254 if (next) {
3255 next->previous.u = free_list_checksum_ptr(szone, leftover_ptr);
3256 }
3257 leftover_ptr->previous = ptr->previous;
3258 leftover_ptr->next = ptr->next;
3259 set_tiny_meta_header_free(leftover_ptr, leftover_msize);
3260 #if DEBUG_MALLOC
3261 if (LOG(szone,ptr)) {
3262 malloc_printf("in tiny_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n",
3263 ptr, msize, this_msize);
3264 }
3265 #endif
3266 this_msize = msize;
3267 goto return_tiny_alloc;
3268 }
3269 if (next) {
3270 next->previous = ptr->previous;
3271 }
3272 *limit = next;
3273 goto add_leftover_and_proceed;
3274 /* NOTREACHED */
3275 }
3276
3277 try_tiny_malloc_from_end:
3278 // Let's see if we can use tiny_mag_ptr->mag_bytes_free_at_end
3279 if (tiny_mag_ptr->mag_bytes_free_at_end >= TINY_BYTES_FOR_MSIZE(msize)) {
3280 ptr = (free_list_t *)((uintptr_t)TINY_REGION_END(tiny_mag_ptr->mag_last_region) -
3281 tiny_mag_ptr->mag_bytes_free_at_end);
3282 tiny_mag_ptr->mag_bytes_free_at_end -= TINY_BYTES_FOR_MSIZE(msize);
3283 if (tiny_mag_ptr->mag_bytes_free_at_end) {
3284 // let's add an in use block after ptr to serve as boundary
3285 set_tiny_meta_header_in_use_1((unsigned char *)ptr + TINY_BYTES_FOR_MSIZE(msize));
3286 }
3287 this_msize = msize;
3288 #if DEBUG_MALLOC
3289 if (LOG(szone, ptr)) {
3290 malloc_printf("in tiny_malloc_from_free_list(), from end ptr=%p, msize=%d\n", ptr, msize);
3291 }
3292 #endif
3293 goto return_tiny_alloc;
3294 }
3295 #if ASLR_INTERNAL
3296 // Try from start if nothing left at end
3297 if (tiny_mag_ptr->mag_bytes_free_at_start >= TINY_BYTES_FOR_MSIZE(msize)) {
3298 ptr = (free_list_t *)(TINY_REGION_ADDRESS(tiny_mag_ptr->mag_last_region) +
3299 tiny_mag_ptr->mag_bytes_free_at_start - TINY_BYTES_FOR_MSIZE(msize));
3300 tiny_mag_ptr->mag_bytes_free_at_start -= TINY_BYTES_FOR_MSIZE(msize);
3301 if (tiny_mag_ptr->mag_bytes_free_at_start) {
3302 // let's add an in use block before ptr to serve as boundary
3303 set_tiny_meta_header_in_use_1((unsigned char *)ptr - TINY_QUANTUM);
3304 }
3305 this_msize = msize;
3306 #if DEBUG_MALLOC
3307 if (LOG(szone, ptr)) {
3308 malloc_printf("in tiny_malloc_from_free_list(), from start ptr=%p, msize=%d\n", ptr, msize);
3309 }
3310 #endif
3311 goto return_tiny_alloc;
3312 }
3313 #endif
3314 return NULL;
3315
3316 add_leftover_and_proceed:
3317 if (!this_msize || (this_msize > msize)) {
3318 leftover_msize = this_msize - msize;
3319 leftover_ptr = (free_list_t *)((unsigned char *)ptr + TINY_BYTES_FOR_MSIZE(msize));
3320 #if DEBUG_MALLOC
3321 if (LOG(szone,ptr)) {
3322 malloc_printf("in tiny_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr, this_msize);
3323 }
3324 #endif
3325 tiny_free_list_add_ptr(szone, tiny_mag_ptr, leftover_ptr, leftover_msize);
3326 this_msize = msize;
3327 }
3328
3329 return_tiny_alloc:
3330 tiny_mag_ptr->mag_num_objects++;
3331 tiny_mag_ptr->mag_num_bytes_in_objects += TINY_BYTES_FOR_MSIZE(this_msize);
3332
3333 // Update this region's bytes in use count
3334 region_trailer_t *node = REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr));
3335 size_t bytes_used = node->bytes_used + TINY_BYTES_FOR_MSIZE(this_msize);
3336 node->bytes_used = bytes_used;
3337
3338 // Emptiness discriminant
3339 if (bytes_used < DENSITY_THRESHOLD(TINY_REGION_PAYLOAD_BYTES)) {
3340 /* After this allocation the region is still sparse, so it must have been even more so before
3341 the allocation. That implies the region is already correctly marked. Do nothing. */
3342 } else {
3343 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
3344 recirculation candidates list. */
3345 node->recirc_suitable = FALSE;
3346 }
3347 #if DEBUG_MALLOC
3348 if (LOG(szone,ptr)) {
3349 malloc_printf("in tiny_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr, this_msize, msize);
3350 }
3351 #endif
3352 if (this_msize > 1)
3353 set_tiny_meta_header_in_use(ptr, this_msize);
3354 else
3355 set_tiny_meta_header_in_use_1(ptr);
3356 return ptr;
3357 }
3358 #undef DENSITY_THRESHOLD
3359 #undef K
3360
3361 static INLINE void *
3362 tiny_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested)
3363 {
3364 void *ptr;
3365 mag_index_t mag_index = mag_get_thread_index(szone);
3366 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
3367
3368 #if DEBUG_MALLOC
3369 if (DEPOT_MAGAZINE_INDEX == mag_index) {
3370 szone_error(szone, 1, "malloc called for magazine index -1", NULL, NULL);
3371 return(NULL);
3372 }
3373
3374 if (!msize) {
3375 szone_error(szone, 1, "invariant broken (!msize) in allocation (region)", NULL, NULL);
3376 return(NULL);
3377 }
3378 #endif
3379
3380 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
3381
3382 #if TINY_CACHE
3383 ptr = tiny_mag_ptr->mag_last_free;
3384
3385 if ((((uintptr_t)ptr) & (TINY_QUANTUM - 1)) == msize) {
3386 // we have a winner
3387 tiny_mag_ptr->mag_last_free = NULL;
3388 tiny_mag_ptr->mag_last_free_rgn = NULL;
3389 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3390 CHECK(szone, __PRETTY_FUNCTION__);
3391 ptr = (void *)((uintptr_t)ptr & ~ (TINY_QUANTUM - 1));
3392 if (cleared_requested) {
3393 memset(ptr, 0, TINY_BYTES_FOR_MSIZE(msize));
3394 }
3395 #if DEBUG_MALLOC
3396 if (LOG(szone,ptr)) {
3397 malloc_printf("in tiny_malloc_should_clear(), tiny cache ptr=%p, msize=%d\n", ptr, msize);
3398 }
3399 #endif
3400 return ptr;
3401 }
3402 #endif /* TINY_CACHE */
3403
3404 while (1) {
3405 ptr = tiny_malloc_from_free_list(szone, tiny_mag_ptr, mag_index, msize);
3406 if (ptr) {
3407 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3408 CHECK(szone, __PRETTY_FUNCTION__);
3409 if (cleared_requested) {
3410 memset(ptr, 0, TINY_BYTES_FOR_MSIZE(msize));
3411 }
3412 return ptr;
3413 }
3414
3415 if (tiny_get_region_from_depot(szone, tiny_mag_ptr, mag_index, msize)) {
3416 ptr = tiny_malloc_from_free_list(szone, tiny_mag_ptr, mag_index, msize);
3417 if (ptr) {
3418 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3419 CHECK(szone, __PRETTY_FUNCTION__);
3420 if (cleared_requested) {
3421 memset(ptr, 0, TINY_BYTES_FOR_MSIZE(msize));
3422 }
3423 return ptr;
3424 }
3425 }
3426
3427 // The magazine is exhausted. A new region (heap) must be allocated to satisfy this call to malloc().
3428 // The allocation, an mmap() system call, will be performed outside the magazine spin locks by the first
3429 // thread that suffers the exhaustion. That thread sets "alloc_underway" and enters a critical section.
3430 // Threads arriving here later are excluded from the critical section, yield the CPU, and then retry the
3431 // allocation. After some time the magazine is resupplied, the original thread leaves with its allocation,
3432 // and retry-ing threads succeed in the code just above.
3433 if (!tiny_mag_ptr->alloc_underway) {
3434 void *fresh_region;
3435
3436 // time to create a new region (do this outside the magazine lock)
3437 tiny_mag_ptr->alloc_underway = TRUE;
3438 OSMemoryBarrier();
3439 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3440 fresh_region = allocate_pages_securely(szone, TINY_REGION_SIZE, TINY_BLOCKS_ALIGN, VM_MEMORY_MALLOC_TINY);
3441 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
3442
3443 MAGMALLOC_ALLOCREGION((void *)szone, (int)mag_index, fresh_region, TINY_REGION_SIZE); // DTrace USDT Probe
3444
3445 if (!fresh_region) { // out of memory!
3446 tiny_mag_ptr->alloc_underway = FALSE;
3447 OSMemoryBarrier();
3448 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3449 return NULL;
3450 }
3451
3452 ptr = tiny_malloc_from_region_no_lock(szone, tiny_mag_ptr, mag_index, msize, fresh_region);
3453
3454 // we don't clear because this freshly allocated space is pristine
3455 tiny_mag_ptr->alloc_underway = FALSE;
3456 OSMemoryBarrier();
3457 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3458 CHECK(szone, __PRETTY_FUNCTION__);
3459 return ptr;
3460 } else {
3461 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3462 pthread_yield_np();
3463 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
3464 }
3465 }
3466 /* NOTREACHED */
3467 }
3468
3469 static NOINLINE void
3470 free_tiny_botch(szone_t *szone, free_list_t *ptr)
3471 {
3472 mag_index_t mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr));
3473 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
3474 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3475 szone_error(szone, 1, "double free", ptr, NULL);
3476 }
3477
3478 static INLINE void
3479 free_tiny(szone_t *szone, void *ptr, region_t tiny_region, size_t known_size)
3480 {
3481 msize_t msize;
3482 boolean_t is_free;
3483 mag_index_t mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region);
3484 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
3485
3486 // ptr is known to be in tiny_region
3487 if (known_size) {
3488 msize = TINY_MSIZE_FOR_BYTES(known_size + TINY_QUANTUM - 1);
3489 } else {
3490 msize = get_tiny_meta_header(ptr, &is_free);
3491 if (is_free) {
3492 free_tiny_botch(szone, ptr);
3493 return;
3494 }
3495 }
3496 #if DEBUG_MALLOC
3497 if (!msize) {
3498 malloc_printf("*** free_tiny() block in use is too large: %p\n", ptr);
3499 return;
3500 }
3501 #endif
3502
3503 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
3504
3505 #if TINY_CACHE
3506 // Depot does not participate in TINY_CACHE since it can't be directly malloc()'d
3507 if (DEPOT_MAGAZINE_INDEX != mag_index) {
3508 if (msize < TINY_QUANTUM) { // to see if the bits fit in the last 4 bits
3509 void *ptr2 = tiny_mag_ptr->mag_last_free; // Might be NULL
3510 region_t rgn2 = tiny_mag_ptr->mag_last_free_rgn;
3511
3512 /* check that we don't already have this pointer in the cache */
3513 if (ptr == (void *)((uintptr_t)ptr2 & ~ (TINY_QUANTUM - 1))) {
3514 free_tiny_botch(szone, ptr);
3515 return;
3516 }
3517
3518 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && msize)
3519 memset(ptr, 0x55, TINY_BYTES_FOR_MSIZE(msize));
3520
3521 tiny_mag_ptr->mag_last_free = (void *)(((uintptr_t)ptr) | msize);
3522 tiny_mag_ptr->mag_last_free_rgn = tiny_region;
3523
3524 if (!ptr2) {
3525 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3526 CHECK(szone, __PRETTY_FUNCTION__);
3527 return;
3528 }
3529
3530 msize = (uintptr_t)ptr2 & (TINY_QUANTUM - 1);
3531 ptr = (void *)(((uintptr_t)ptr2) & ~(TINY_QUANTUM - 1));
3532 tiny_region = rgn2;
3533 }
3534 }
3535 #endif /* TINY_CACHE */
3536
3537 // Now in the time it took to acquire the lock, the region may have migrated
3538 // from one magazine to another. I.e. trailer->mag_index is volatile.
3539 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
3540 // is stale. If so, keep on tryin' ...
3541 region_trailer_t *trailer = REGION_TRAILER_FOR_TINY_REGION(tiny_region);
3542 mag_index_t refreshed_index;
3543
3544 while (mag_index != (refreshed_index = trailer->mag_index)) { // Note assignment
3545
3546 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3547
3548 mag_index = refreshed_index;
3549 tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
3550 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
3551 }
3552
3553 if (tiny_free_no_lock(szone, tiny_mag_ptr, mag_index, tiny_region, ptr, msize))
3554 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3555
3556 CHECK(szone, __PRETTY_FUNCTION__);
3557 }
3558
3559 static void
3560 print_tiny_free_list(szone_t *szone)
3561 {
3562 free_list_t *ptr;
3563 _SIMPLE_STRING b = _simple_salloc();
3564 mag_index_t mag_index;
3565
3566 if (b) {
3567 _simple_sappend(b, "tiny free sizes:\n");
3568 for (mag_index = -1; mag_index < szone->num_tiny_magazines; mag_index++) {
3569 grain_t slot = 0;
3570 _simple_sprintf(b,"\tMagazine %d: ", mag_index);
3571 while (slot < NUM_TINY_SLOTS) {
3572 ptr = szone->tiny_magazines[mag_index].mag_free_list[slot];
3573 if (ptr) {
3574 _simple_sprintf(b, "%s%y[%d]; ", (slot == NUM_TINY_SLOTS-1) ? ">=" : "",
3575 (slot+1)*TINY_QUANTUM, free_list_count(szone, ptr));
3576 }
3577 slot++;
3578 }
3579 _simple_sappend(b,"\n");
3580 }
3581 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
3582 _simple_sfree(b);
3583 }
3584 }
3585
3586 static void
3587 print_tiny_region(boolean_t verbose, region_t region, size_t bytes_at_start, size_t bytes_at_end)
3588 {
3589 unsigned counts[1024];
3590 unsigned in_use = 0;
3591 uintptr_t start = (uintptr_t)TINY_REGION_ADDRESS(region);
3592 uintptr_t current = start + bytes_at_end;
3593 uintptr_t limit = (uintptr_t)TINY_REGION_END(region) - bytes_at_end;
3594 boolean_t is_free;
3595 msize_t msize;
3596 unsigned ci;
3597 _SIMPLE_STRING b;
3598 uintptr_t pgTot = 0;
3599
3600 if (region == HASHRING_REGION_DEALLOCATED) {
3601 if ((b = _simple_salloc()) != NULL) {
3602 _simple_sprintf(b, "Tiny region [unknown address] was returned to the OS\n");
3603 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
3604 _simple_sfree(b);
3605 }
3606 return;
3607 }
3608
3609 memset(counts, 0, sizeof(counts));
3610 while (current < limit) {
3611 msize = get_tiny_meta_header((void *)current, &is_free);
3612 if (is_free & !msize && (current == start)) {
3613 // first block is all free
3614 uintptr_t pgLo = round_page(start + sizeof(free_list_t) + sizeof(msize_t));
3615 uintptr_t pgHi = trunc_page(start + TINY_REGION_SIZE - sizeof(msize_t));
3616
3617 if (pgLo < pgHi) {
3618 pgTot += (pgHi - pgLo);
3619 }
3620 break;
3621 }
3622 if (!msize) {
3623 malloc_printf("*** error with %p: msize=%d\n", (void *)current, (unsigned)msize);
3624 break;
3625 }
3626 if (!is_free) {
3627 // block in use
3628 if (msize > NUM_TINY_SLOTS)
3629 malloc_printf("*** error at %p msize for in_use is %d\n", (void *)current, msize);
3630 if (msize < 1024)
3631 counts[msize]++;
3632 in_use++;
3633 } else {
3634 uintptr_t pgLo = round_page(current + sizeof(free_list_t) + sizeof(msize_t));
3635 uintptr_t pgHi = trunc_page(current + TINY_BYTES_FOR_MSIZE(msize) - sizeof(msize_t));
3636
3637 if (pgLo < pgHi) {
3638 pgTot += (pgHi - pgLo);
3639 }
3640 }
3641 current += TINY_BYTES_FOR_MSIZE(msize);
3642 }
3643 if ((b = _simple_salloc()) != NULL) {
3644 _simple_sprintf(b, "Tiny region [%p-%p, %y] \t", (void *)start, TINY_REGION_END(region), (int)TINY_REGION_SIZE);
3645 _simple_sprintf(b, "Magazine=%d \t", MAGAZINE_INDEX_FOR_TINY_REGION(region));
3646 _simple_sprintf(b, "Allocations in use=%d \t Bytes in use=%ly \t", in_use, BYTES_USED_FOR_TINY_REGION(region));
3647 if (bytes_at_end || bytes_at_start)
3648 _simple_sprintf(b, "Untouched=%ly ", bytes_at_end + bytes_at_start);
3649 if (DEPOT_MAGAZINE_INDEX == MAGAZINE_INDEX_FOR_TINY_REGION(region)) {
3650 _simple_sprintf(b, "Advised MADV_FREE=%ly", pgTot);
3651 } else {
3652 _simple_sprintf(b, "Fragments subject to reclamation=%ly", pgTot);
3653 }
3654 if (verbose && in_use) {
3655 _simple_sappend(b, "\n\tSizes in use: ");
3656 for (ci = 0; ci < 1024; ci++)
3657 if (counts[ci])
3658 _simple_sprintf(b, "%d[%d] ", TINY_BYTES_FOR_MSIZE(ci), counts[ci]);
3659 }
3660 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
3661 _simple_sfree(b);
3662 }
3663 }
3664
3665 static boolean_t
3666 tiny_free_list_check(szone_t *szone, grain_t slot)
3667 {
3668 mag_index_t mag_index;
3669
3670 for (mag_index = -1; mag_index < szone->num_tiny_magazines; mag_index++) {
3671 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
3672 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
3673
3674 unsigned count = 0;
3675 free_list_t *ptr = szone->tiny_magazines[mag_index].mag_free_list[slot];
3676 boolean_t is_free;
3677 free_list_t *previous = NULL;
3678
3679 while (ptr) {
3680 is_free = tiny_meta_header_is_free(ptr);
3681 if (! is_free) {
3682 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot, count, ptr);
3683 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3684 return 0;
3685 }
3686 if (((uintptr_t)ptr) & (TINY_QUANTUM - 1)) {
3687 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot, count, ptr);
3688 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3689 return 0;
3690 }
3691 if (!tiny_region_for_ptr_no_lock(szone, ptr)) {
3692 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot, count, ptr);
3693 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3694 return 0;
3695 }
3696 if (free_list_unchecksum_ptr(szone, &ptr->previous) != previous) {
3697 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot, count, ptr);
3698 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3699 return 0;
3700 }
3701 previous = ptr;
3702 ptr = free_list_unchecksum_ptr(szone, &ptr->next);
3703 count++;
3704 }
3705
3706 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
3707 }
3708 return 1;
3709 }
3710
3711 /********************* SMALL FREE LIST UTILITIES ************************/
3712
3713 /*
3714 * Mark a block as free. Only the first quantum of a block is marked thusly,
3715 * the remainder are marked "middle".
3716 */
3717 static INLINE void
3718 small_meta_header_set_is_free(msize_t *meta_headers, unsigned index, msize_t msize)
3719 {
3720 meta_headers[index] = msize | SMALL_IS_FREE;
3721 }
3722
3723 /*
3724 * Mark a block as in use. Only the first quantum of a block is marked thusly,
3725 * the remainder are marked "middle".
3726 */
3727 static INLINE void
3728 small_meta_header_set_in_use(msize_t *meta_headers, msize_t index, msize_t msize)
3729 {
3730 meta_headers[index] = msize;
3731 }
3732
3733 /*
3734 * Mark a quantum as being the second or later in a block.
3735 */
3736 static INLINE void
3737 small_meta_header_set_middle(msize_t *meta_headers, msize_t index)
3738 {
3739 meta_headers[index] = 0;
3740 }
3741
3742 /*
3743 * Adds an item to the proper free list, and also marks the meta-header of the
3744 * block properly.
3745 * Assumes szone has been locked
3746 */
3747 static void
3748 small_free_list_add_ptr(szone_t *szone, magazine_t *small_mag_ptr, void *ptr, msize_t msize)
3749 {
3750 grain_t slot = (msize <= szone->num_small_slots) ? msize - 1 : szone->num_small_slots - 1;
3751 free_list_t *free_ptr = ptr;
3752 free_list_t *free_head = small_mag_ptr->mag_free_list[slot];
3753 void *follower;
3754
3755 #if DEBUG_MALLOC
3756 if (LOG(szone,ptr)) {
3757 malloc_printf("in %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
3758 }
3759 if (((uintptr_t)ptr) & (SMALL_QUANTUM - 1)) {
3760 szone_error(szone, 1, "small_free_list_add_ptr: Unaligned ptr", ptr, NULL);
3761 }
3762 #endif
3763 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(ptr), SMALL_META_INDEX_FOR_PTR(ptr), msize);
3764
3765 if (free_head) {
3766 #if DEBUG_MALLOC
3767 if (free_list_unchecksum_ptr(szone, &free_head->previous)) {
3768 szone_error(szone, 1, "small_free_list_add_ptr: Internal invariant broken (free_head->previous)", ptr,
3769 "ptr=%p slot=%d free_head=%p previous=%p\n", ptr, slot, (void *)free_head, free_head->previous.p);
3770 }
3771 if (!SMALL_PTR_IS_FREE(free_head)) {
3772 szone_error(szone, 1, "small_free_list_add_ptr: Internal invariant broken (free_head is not a free pointer)", ptr,
3773 "ptr=%p slot=%d free_head=%p\n", ptr, slot, (void *)free_head);
3774 }
3775 #endif
3776 free_head->previous.u = free_list_checksum_ptr(szone, free_ptr);
3777 } else {
3778 BITMAPN_SET(small_mag_ptr->mag_bitmap, slot);
3779 }
3780 free_ptr->previous.u = free_list_checksum_ptr(szone, NULL);
3781 free_ptr->next.u = free_list_checksum_ptr(szone, free_head);
3782
3783 small_mag_ptr->mag_free_list[slot] = free_ptr;
3784
3785 // Store msize at the end of the block denoted by "ptr" (i.e. at a negative offset from "follower")
3786 follower = (void *)((uintptr_t)ptr + SMALL_BYTES_FOR_MSIZE(msize));
3787 SMALL_PREVIOUS_MSIZE(follower) = msize;
3788 }
3789
3790 /*
3791 * Removes the item pointed to by ptr in the proper free list.
3792 * Assumes szone has been locked
3793 */
3794 static void
3795 small_free_list_remove_ptr(szone_t *szone, magazine_t *small_mag_ptr, void *ptr, msize_t msize)
3796 {
3797 grain_t slot = (msize <= szone->num_small_slots) ? msize - 1 : szone->num_small_slots - 1;
3798 free_list_t *free_ptr = ptr, *next, *previous;
3799
3800 next = free_list_unchecksum_ptr(szone, &free_ptr->next);
3801 previous = free_list_unchecksum_ptr(szone, &free_ptr->previous);
3802
3803 #if DEBUG_MALLOC
3804 if (LOG(szone,ptr)) {
3805 malloc_printf("In %s, ptr=%p, msize=%d\n", __FUNCTION__, ptr, msize);
3806 }
3807 #endif
3808
3809 if (!previous) {
3810 // The block to remove is the head of the free list
3811 #if DEBUG_MALLOC
3812 if (small_mag_ptr->mag_free_list[slot] != ptr) {
3813 szone_error(szone, 1, "small_free_list_remove_ptr: Internal invariant broken (small_mag_ptr->mag_free_list[slot])", ptr,
3814 "ptr=%p slot=%d msize=%d small_mag_ptr->mag_free_list[slot]=%p\n",
3815 ptr, slot, msize, (void *)small_mag_ptr->mag_free_list[slot]);
3816 return;
3817 }
3818 #endif
3819 small_mag_ptr->mag_free_list[slot] = next;
3820 if (!next) BITMAPN_CLR(small_mag_ptr->mag_bitmap, slot);
3821 } else {
3822 // We know free_ptr is already checksummed, so we don't need to do it
3823 // again.
3824 previous->next = free_ptr->next;
3825 }
3826 if (next) {
3827 // We know free_ptr is already checksummed, so we don't need to do it
3828 // again.
3829 next->previous = free_ptr->previous;
3830 }
3831 }
3832
3833 /*
3834 * small_region_for_ptr_no_lock - Returns the small region containing the pointer,
3835 * or NULL if not found.
3836 */
3837 static INLINE region_t
3838 small_region_for_ptr_no_lock(szone_t *szone, const void *ptr)
3839 {
3840 rgnhdl_t r = hash_lookup_region_no_lock(szone->small_region_generation->hashed_regions,
3841 szone->small_region_generation->num_regions_allocated,
3842 szone->small_region_generation->num_regions_allocated_shift,
3843 SMALL_REGION_FOR_PTR(ptr));
3844 return r ? *r : r;
3845 }
3846
3847 static void
3848 small_finalize_region(szone_t *szone, magazine_t *small_mag_ptr) {
3849 void *last_block, *previous_block;
3850 msize_t last_msize, previous_msize, last_index;
3851
3852 // It is possible that the block prior to the last block in the region has
3853 // been free'd, but was not coalesced with the free bytes at the end of the
3854 // block, since we treat the bytes at the end of the region as "in use" in
3855 // the meta headers. Attempt to coalesce the last block with the previous
3856 // block, so we don't violate the "no consecutive free blocks" invariant.
3857 //
3858 // FIXME: If we could calculate the previous small free size in the same
3859 // manner as tiny_previous_preceding_free, it would eliminate the
3860 // index & previous msize checks, which are a guard against reading
3861 // bogus data out of in-use or written-on-freed memory.
3862 //
3863 // FIXME: Need to investigate how much work would be required to increase
3864 // 'mag_bytes_free_at_end' when freeing the preceding block, rather
3865 // than performing this workaround.
3866 //
3867 if (small_mag_ptr->mag_bytes_free_at_end) {
3868 last_block = SMALL_REGION_END(small_mag_ptr->mag_last_region) - small_mag_ptr->mag_bytes_free_at_end;
3869 last_msize = SMALL_MSIZE_FOR_BYTES(small_mag_ptr->mag_bytes_free_at_end);
3870
3871 last_index = SMALL_META_INDEX_FOR_PTR(last_block);
3872 previous_msize = SMALL_PREVIOUS_MSIZE(last_block);
3873
3874 if (last_index && (previous_msize <= last_index)) {
3875 previous_block = (void *)((uintptr_t)last_block - SMALL_BYTES_FOR_MSIZE(previous_msize));
3876 if (*SMALL_METADATA_FOR_PTR(previous_block) == (previous_msize | SMALL_IS_FREE)) {
3877 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(last_block);
3878
3879 small_meta_header_set_middle(meta_headers, last_index);
3880 small_free_list_remove_ptr(szone, small_mag_ptr, previous_block, previous_msize);
3881 last_block = (void *)((uintptr_t)last_block - SMALL_BYTES_FOR_MSIZE(previous_msize));
3882 last_msize += previous_msize;
3883 }
3884 }
3885
3886 // splice last_block into the free list
3887 small_free_list_add_ptr(szone, small_mag_ptr, last_block, last_msize);
3888 small_mag_ptr->mag_bytes_free_at_end = 0;
3889 }
3890
3891 #if ASLR_INTERNAL
3892 if (small_mag_ptr->mag_bytes_free_at_start) {
3893 last_block = SMALL_REGION_ADDRESS(small_mag_ptr->mag_last_region);
3894 last_msize = SMALL_MSIZE_FOR_BYTES(small_mag_ptr->mag_bytes_free_at_start);
3895
3896 void *next_block = (void *) ((uintptr_t)last_block + small_mag_ptr->mag_bytes_free_at_start);
3897 if (SMALL_PTR_IS_FREE(next_block)) {
3898 msize_t next_msize = SMALL_PTR_SIZE(next_block);
3899
3900 small_meta_header_set_middle(SMALL_META_HEADER_FOR_PTR(next_block), SMALL_META_INDEX_FOR_PTR(next_block));
3901 small_free_list_remove_ptr(szone, small_mag_ptr, next_block, next_msize);
3902 last_msize += next_msize;
3903 }
3904
3905 // splice last_block into the free list
3906 small_free_list_add_ptr(szone, small_mag_ptr, last_block, last_msize);
3907 small_mag_ptr->mag_bytes_free_at_start = 0;
3908 }
3909 #endif
3910
3911 // TODO: Will we ever need to coalesce the blocks at the beginning and end when we finalize?
3912
3913 small_mag_ptr->mag_last_region = NULL;
3914 }
3915
3916 static int
3917 small_free_detach_region(szone_t *szone, magazine_t *small_mag_ptr, region_t r) {
3918 unsigned char *ptr = SMALL_REGION_ADDRESS(r);
3919 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
3920 uintptr_t start = (uintptr_t)SMALL_REGION_ADDRESS(r);
3921 uintptr_t current = start;
3922 uintptr_t limit = (uintptr_t)SMALL_REGION_END(r);
3923 int total_alloc = 0;
3924
3925 while (current < limit) {
3926 unsigned index = SMALL_META_INDEX_FOR_PTR(current);
3927 msize_t msize_and_free = meta_headers[index];
3928 boolean_t is_free = msize_and_free & SMALL_IS_FREE;
3929 msize_t msize = msize_and_free & ~ SMALL_IS_FREE;
3930
3931 if (!msize) {
3932 #if DEBUG_MALLOC
3933 malloc_printf("*** small_free_detach_region error with %p: msize=%d is_free =%d\n",
3934 (void *)current, msize, is_free);
3935 #endif
3936 break;
3937 }
3938 if (is_free) {
3939 small_free_list_remove_ptr(szone, small_mag_ptr, (void *)current, msize);
3940 } else {
3941 total_alloc++;
3942 }
3943 current += SMALL_BYTES_FOR_MSIZE(msize);
3944 }
3945 return total_alloc;
3946 }
3947
3948 static size_t
3949 small_free_reattach_region(szone_t *szone, magazine_t *small_mag_ptr, region_t r) {
3950 unsigned char *ptr = SMALL_REGION_ADDRESS(r);
3951 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
3952 uintptr_t start = (uintptr_t)SMALL_REGION_ADDRESS(r);
3953 uintptr_t current = start;
3954 uintptr_t limit = (uintptr_t)SMALL_REGION_END(r);
3955 size_t total_alloc = 0;
3956
3957 while (current < limit) {
3958 unsigned index = SMALL_META_INDEX_FOR_PTR(current);
3959 msize_t msize_and_free = meta_headers[index];
3960 boolean_t is_free = msize_and_free & SMALL_IS_FREE;
3961 msize_t msize = msize_and_free & ~ SMALL_IS_FREE;
3962
3963 if (!msize) {
3964 #if DEBUG_MALLOC
3965 malloc_printf("*** small_free_reattach_region error with %p: msize=%d is_free =%d\n",
3966 (void *)current, msize, is_free);
3967 #endif
3968 break;
3969 }
3970 if (is_free) {
3971 small_free_list_add_ptr(szone, small_mag_ptr, (void *)current, msize);
3972 } else {
3973 total_alloc += SMALL_BYTES_FOR_MSIZE(msize);
3974 }
3975 current += SMALL_BYTES_FOR_MSIZE(msize);
3976 }
3977 return total_alloc;
3978 }
3979
3980 typedef struct {
3981 uint16_t pnum, size;
3982 } small_pg_pair_t;
3983
3984 static void NOINLINE /* want private stack frame for automatic array */
3985 small_free_scan_madvise_free(szone_t *szone, magazine_t *depot_ptr, region_t r) {
3986 uintptr_t start = (uintptr_t)SMALL_REGION_ADDRESS(r);
3987 uintptr_t current = start;
3988 uintptr_t limit = (uintptr_t)SMALL_REGION_END(r);
3989 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(start);
3990 small_pg_pair_t advisory[((SMALL_REGION_PAYLOAD_BYTES + vm_page_size - 1) >> vm_page_shift) >> 1]; // 4096bytes stack allocated
3991 int advisories = 0;
3992
3993 // Scan the metadata identifying blocks which span one or more pages. Mark the pages MADV_FREE taking care to preserve free list
3994 // management data.
3995 while (current < limit) {
3996 unsigned index = SMALL_META_INDEX_FOR_PTR(current);
3997 msize_t msize_and_free = meta_headers[index];
3998 boolean_t is_free = msize_and_free & SMALL_IS_FREE;
3999 msize_t msize = msize_and_free & ~ SMALL_IS_FREE;
4000
4001 if (is_free && !msize && (current == start)) {
4002 #if DEBUG_MALLOC
4003 // first block is all free
4004 malloc_printf("*** small_free_scan_madvise_free first block is all free! %p: msize=%d is_free =%d\n",
4005 (void *)current, msize, is_free);
4006 #endif
4007 uintptr_t pgLo = round_page(start + sizeof(free_list_t) + sizeof(msize_t));
4008 uintptr_t pgHi = trunc_page(start + SMALL_REGION_SIZE - sizeof(msize_t));
4009
4010 if (pgLo < pgHi) {
4011 advisory[advisories].pnum = (pgLo - start) >> vm_page_shift;
4012 advisory[advisories].size = (pgHi - pgLo) >> vm_page_shift;
4013 advisories++;
4014 }
4015 break;
4016 }
4017 if (!msize) {
4018 #if DEBUG_MALLOC
4019 malloc_printf("*** small_free_scan_madvise_free error with %p: msize=%d is_free =%d\n",
4020 (void *)current, msize, is_free);
4021 #endif
4022 break;
4023 }
4024 if (is_free) {
4025 uintptr_t pgLo = round_page(current + sizeof(free_list_t) + sizeof(msize_t));
4026 uintptr_t pgHi = trunc_page(current + SMALL_BYTES_FOR_MSIZE(msize) - sizeof(msize_t));
4027
4028 if (pgLo < pgHi) {
4029 advisory[advisories].pnum = (pgLo - start) >> vm_page_shift;
4030 advisory[advisories].size = (pgHi - pgLo) >> vm_page_shift;
4031 advisories++;
4032 }
4033 }
4034 current += SMALL_BYTES_FOR_MSIZE(msize);
4035 }
4036
4037 if (advisories > 0) {
4038 int i;
4039
4040 OSAtomicIncrement32Barrier(&(REGION_TRAILER_FOR_SMALL_REGION(r)->pinned_to_depot));
4041 SZONE_MAGAZINE_PTR_UNLOCK(szone, depot_ptr);
4042 for (i = 0; i < advisories; ++i) {
4043 uintptr_t addr = (advisory[i].pnum << vm_page_shift) + start;
4044 size_t size = advisory[i].size << vm_page_shift;
4045
4046 #if TARGET_OS_EMBEDDED
4047 madvise_free_range(szone, r, addr, addr + size, NULL);
4048 #else
4049 madvise_free_range(szone, r, addr, addr + size);
4050 #endif
4051 }
4052 SZONE_MAGAZINE_PTR_LOCK(szone, depot_ptr);
4053 OSAtomicDecrement32Barrier(&(REGION_TRAILER_FOR_SMALL_REGION(r)->pinned_to_depot));
4054 }
4055 }
4056
4057 static region_t
4058 small_free_try_depot_unmap_no_lock(szone_t *szone, magazine_t *depot_ptr, region_trailer_t *node)
4059 {
4060 if (0 < node->bytes_used ||
4061 0 < node->pinned_to_depot ||
4062 depot_ptr->recirculation_entries < (szone->num_small_magazines * 2)) {
4063 return NULL;
4064 }
4065
4066 // disconnect first node from Depot
4067 recirc_list_extract(szone, depot_ptr, node);
4068
4069 // Iterate the region pulling its free entries off the (locked) Depot's free list
4070 region_t sparse_region = SMALL_REGION_FOR_PTR(node);
4071 int objects_in_use = small_free_detach_region(szone, depot_ptr, sparse_region);
4072
4073 if (0 == objects_in_use) {
4074 // Invalidate the hash table entry for this region with HASHRING_REGION_DEALLOCATED.
4075 // Using HASHRING_REGION_DEALLOCATED preserves the collision chain, using HASHRING_OPEN_ENTRY (0) would not.
4076 rgnhdl_t pSlot = hash_lookup_region_no_lock(szone->small_region_generation->hashed_regions,
4077 szone->small_region_generation->num_regions_allocated,
4078 szone->small_region_generation->num_regions_allocated_shift, sparse_region);
4079 if (NULL == pSlot) {
4080 szone_error(szone, 1, "small_free_try_depot_unmap_no_lock hash lookup failed:", NULL, "%p\n", sparse_region);
4081 return NULL;
4082 }
4083 *pSlot = HASHRING_REGION_DEALLOCATED;
4084 depot_ptr->num_bytes_in_magazine -= SMALL_REGION_PAYLOAD_BYTES;
4085 __sync_fetch_and_add( &(szone->num_small_regions_dealloc), 1); // Atomically increment num_small_regions_dealloc
4086
4087 // Caller will transfer ownership of the region back to the OS with no locks held
4088 MAGMALLOC_DEALLOCREGION((void *)szone, (void *)sparse_region, SMALL_REGION_SIZE); // DTrace USDT Probe
4089 return sparse_region;
4090
4091 } else {
4092 szone_error(szone, 1, "small_free_try_depot_unmap_no_lock objects_in_use not zero:", NULL, "%d\n", objects_in_use);
4093 return NULL;
4094 }
4095 }
4096
4097 static boolean_t
4098 small_free_do_recirc_to_depot(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index)
4099 {
4100 // The entire magazine crossed the "emptiness threshold". Transfer a region
4101 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
4102 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
4103 region_trailer_t *node = small_mag_ptr->firstNode;
4104
4105 while (node && !node->recirc_suitable) {
4106 node = node->next;
4107 }
4108
4109 if (NULL == node) {
4110 #if DEBUG_MALLOC
4111 malloc_printf("*** small_free_do_recirc_to_depot end of list\n");
4112 #endif
4113 return TRUE; // Caller must SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4114 }
4115
4116 region_t sparse_region = SMALL_REGION_FOR_PTR(node);
4117
4118 // Deal with unclaimed memory -- mag_bytes_free_at_end or mag_bytes_free_at start
4119 if (sparse_region == small_mag_ptr->mag_last_region && (small_mag_ptr->mag_bytes_free_at_end || small_mag_ptr->mag_bytes_free_at_start)) {
4120 small_finalize_region(szone, small_mag_ptr);
4121 }
4122
4123 // disconnect "suitable" node from magazine
4124 recirc_list_extract(szone, small_mag_ptr, node);
4125
4126 // Iterate the region pulling its free entries off its (locked) magazine's free list
4127 int objects_in_use = small_free_detach_region(szone, small_mag_ptr, sparse_region);
4128 magazine_t *depot_ptr = &(szone->small_magazines[DEPOT_MAGAZINE_INDEX]);
4129
4130 // hand over the region to the (locked) Depot
4131 SZONE_MAGAZINE_PTR_LOCK(szone,depot_ptr);
4132 // this will cause small_free_list_add_ptr called by small_free_reattach_region to use
4133 // the depot as its target magazine, rather than magazine formerly associated with sparse_region
4134 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region) = DEPOT_MAGAZINE_INDEX;
4135 node->pinned_to_depot = 0;
4136
4137 // Iterate the region putting its free entries on Depot's free list
4138 size_t bytes_inplay = small_free_reattach_region(szone, depot_ptr, sparse_region);
4139
4140 small_mag_ptr->mag_num_bytes_in_objects -= bytes_inplay;
4141 small_mag_ptr->num_bytes_in_magazine -= SMALL_REGION_PAYLOAD_BYTES;
4142 small_mag_ptr->mag_num_objects -= objects_in_use;
4143
4144 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr); // Unlock the originating magazine
4145
4146 depot_ptr->mag_num_bytes_in_objects += bytes_inplay;
4147 depot_ptr->num_bytes_in_magazine += SMALL_REGION_PAYLOAD_BYTES;
4148 depot_ptr->mag_num_objects += objects_in_use;
4149
4150 // connect to Depot as last node
4151 recirc_list_splice_last(szone, depot_ptr, node);
4152
4153 MAGMALLOC_RECIRCREGION((void *)szone, (int)mag_index, (void *)sparse_region, SMALL_REGION_SIZE,
4154 (int)BYTES_USED_FOR_SMALL_REGION(sparse_region)); // DTrace USDT Probe
4155
4156 // Mark free'd dirty pages with MADV_FREE to reduce memory pressure
4157 small_free_scan_madvise_free(szone, depot_ptr, sparse_region);
4158
4159 // If the region is entirely empty vm_deallocate() it outside the depot lock
4160 region_t r_dealloc = small_free_try_depot_unmap_no_lock(szone, depot_ptr, node);
4161 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
4162 if (r_dealloc)
4163 deallocate_pages(szone, r_dealloc, SMALL_REGION_SIZE, 0);
4164 return FALSE; // Caller need not unlock the originating magazine
4165 }
4166
4167 static region_t
4168 small_find_msize_region(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, msize_t msize)
4169 {
4170 free_list_t *ptr;
4171 grain_t slot = (msize <= szone->num_small_slots) ? msize - 1 : szone->num_small_slots - 1;
4172 free_list_t **free_list = small_mag_ptr->mag_free_list;
4173 free_list_t **the_slot = free_list + slot;
4174 free_list_t **limit;
4175 unsigned bitmap;
4176
4177 // Assumes we've locked the magazine
4178 CHECK_MAGAZINE_PTR_LOCKED(szone, small_mag_ptr, __PRETTY_FUNCTION__);
4179
4180 // Look for an exact match by checking the freelist for this msize.
4181 ptr = *the_slot;
4182 if (ptr)
4183 return SMALL_REGION_FOR_PTR(ptr);
4184
4185 // Mask off the bits representing slots holding free blocks smaller than
4186 // the size we need.
4187 if (szone->is_largemem) {
4188 // BITMAPN_CTZ implementation
4189 unsigned idx = slot >> 5;
4190 bitmap = 0;
4191 unsigned mask = ~ ((1 << (slot & 31)) - 1);
4192 for ( ; idx < SMALL_BITMAP_WORDS; ++idx ) {
4193 bitmap = small_mag_ptr->mag_bitmap[idx] & mask;
4194 if (bitmap != 0)
4195 break;
4196 mask = ~0U;
4197 }
4198 // Check for fallthrough: No bits set in bitmap
4199 if ((bitmap == 0) && (idx == SMALL_BITMAP_WORDS))
4200 return NULL;
4201
4202 // Start looking at the first set bit, plus 32 bits for every word of
4203 // zeroes or entries that were too small.
4204 slot = BITMAP32_CTZ((&bitmap)) + (idx * 32);
4205 } else {
4206 bitmap = small_mag_ptr->mag_bitmap[0] & ~ ((1 << slot) - 1);
4207 if (!bitmap)
4208 return NULL;
4209
4210 slot = BITMAP32_CTZ((&bitmap));
4211 }
4212 limit = free_list + szone->num_small_slots - 1;
4213 free_list += slot;
4214
4215 if (free_list < limit) {
4216 ptr = *free_list;
4217 if (ptr)
4218 return SMALL_REGION_FOR_PTR(ptr);
4219 else {
4220 /* Shouldn't happen. Fall through to look at last slot. */
4221 #if DEBUG_MALLOC
4222 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot);
4223 #endif
4224 }
4225 }
4226
4227 // We are now looking at the last slot, which contains blocks equal to, or
4228 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4229 ptr = *limit;
4230 if (ptr)
4231 return SMALL_REGION_FOR_PTR(ptr);
4232
4233 return NULL;
4234 }
4235
4236 static boolean_t
4237 small_get_region_from_depot(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, msize_t msize)
4238 {
4239 magazine_t *depot_ptr = &(szone->small_magazines[DEPOT_MAGAZINE_INDEX]);
4240
4241 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
4242 if (szone->num_small_magazines == 1) // Uniprocessor, single magazine, so no recirculation necessary
4243 return 0;
4244
4245 #if DEBUG_MALLOC
4246 if (DEPOT_MAGAZINE_INDEX == mag_index) {
4247 szone_error(szone, 1, "small_get_region_from_depot called for magazine index -1", NULL, NULL);
4248 return 0;
4249 }
4250 #endif
4251
4252 SZONE_MAGAZINE_PTR_LOCK(szone,depot_ptr);
4253
4254 // Appropriate a Depot'd region that can satisfy requested msize.
4255 region_trailer_t *node;
4256 region_t sparse_region;
4257
4258 while (1) {
4259 sparse_region = small_find_msize_region(szone, depot_ptr, DEPOT_MAGAZINE_INDEX, msize);
4260 if (NULL == sparse_region) { // Depot empty?
4261 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
4262 return 0;
4263 }
4264
4265 node = REGION_TRAILER_FOR_SMALL_REGION(sparse_region);
4266 if (0 >= node->pinned_to_depot)
4267 break;
4268
4269 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
4270 pthread_yield_np();
4271 SZONE_MAGAZINE_PTR_LOCK(szone,depot_ptr);
4272 }
4273
4274 // disconnect node from Depot
4275 recirc_list_extract(szone, depot_ptr, node);
4276
4277 // Iterate the region pulling its free entries off the (locked) Depot's free list
4278 int objects_in_use = small_free_detach_region(szone, depot_ptr, sparse_region);
4279
4280 // Transfer ownership of the region
4281 MAGAZINE_INDEX_FOR_SMALL_REGION(sparse_region) = mag_index;
4282 node->pinned_to_depot = 0;
4283
4284 // Iterate the region putting its free entries on its new (locked) magazine's free list
4285 size_t bytes_inplay = small_free_reattach_region(szone, small_mag_ptr, sparse_region);
4286
4287 depot_ptr->mag_num_bytes_in_objects -= bytes_inplay;
4288 depot_ptr->num_bytes_in_magazine -= SMALL_REGION_PAYLOAD_BYTES;
4289 depot_ptr->mag_num_objects -= objects_in_use;
4290
4291 small_mag_ptr->mag_num_bytes_in_objects += bytes_inplay;
4292 small_mag_ptr->num_bytes_in_magazine += SMALL_REGION_PAYLOAD_BYTES;
4293 small_mag_ptr->mag_num_objects += objects_in_use;
4294
4295 // connect to magazine as first node
4296 recirc_list_splice_first(szone, small_mag_ptr, node);
4297
4298 SZONE_MAGAZINE_PTR_UNLOCK(szone,depot_ptr);
4299
4300 // madvise() outside the Depot lock
4301 #if TARGET_OS_EMBEDDED
4302 if (node->failedREUSE) {
4303 #else
4304 if (node->failedREUSE ||
4305 -1 == madvise((void *)sparse_region, SMALL_REGION_PAYLOAD_BYTES, MADV_FREE_REUSE)) {
4306 #endif
4307 /* -1 return: VM map entry change makes this unfit for reuse. Something evil lurks. */
4308 #if DEBUG_MADVISE
4309 szone_error(szone, 0, "small_get_region_from_depot madvise(..., MADV_FREE_REUSE) failed",
4310 sparse_region, "length=%d\n", SMALL_REGION_PAYLOAD_BYTES);
4311 #endif
4312 node->failedREUSE = TRUE;
4313 }
4314
4315 MAGMALLOC_DEPOTREGION((void *)szone, (int)mag_index, (void *)sparse_region, SMALL_REGION_SIZE,
4316 (int)BYTES_USED_FOR_SMALL_REGION(sparse_region)); // DTrace USDT Probe
4317
4318 return 1;
4319 }
4320
4321 #define K 1.5 // headroom measured in number of 8Mb regions
4322 #define DENSITY_THRESHOLD(a) \
4323 ((a) - ((a) >> 2)) // "Emptiness" f = 0.25, so "Density" is (1 - f)*a. Generally: ((a) - ((a) >> -log2(f)))
4324
4325 static INLINE boolean_t
4326 small_free_no_lock(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, region_t region, void *ptr, msize_t msize)
4327 {
4328 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
4329 unsigned index = SMALL_META_INDEX_FOR_PTR(ptr);
4330 void *original_ptr = ptr;
4331 size_t original_size = SMALL_BYTES_FOR_MSIZE(msize);
4332 unsigned char *next_block = ((unsigned char *)ptr + original_size);
4333 msize_t next_index = index + msize;
4334 msize_t previous_msize, next_msize;
4335 void *previous;
4336
4337 #if DEBUG_MALLOC
4338 if (LOG(szone,ptr)) {
4339 malloc_printf("in small_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
4340 }
4341 if (!msize) {
4342 szone_error(szone, 1, "trying to free small block that is too small", ptr,
4343 "in small_free_no_lock(), ptr=%p, msize=%d\n", ptr, msize);
4344 }
4345 #endif
4346
4347 // We try to coalesce this block with the preceeding one
4348 if (index && (SMALL_PREVIOUS_MSIZE(ptr) <= index)) {
4349 previous_msize = SMALL_PREVIOUS_MSIZE(ptr);
4350 if (meta_headers[index - previous_msize] == (previous_msize | SMALL_IS_FREE)) {
4351 previous = (void *)((uintptr_t)ptr - SMALL_BYTES_FOR_MSIZE(previous_msize));
4352 // previous is really to be coalesced
4353 #if DEBUG_MALLOC
4354 if (LOG(szone, ptr) || LOG(szone,previous)) {
4355 malloc_printf("in small_free_no_lock(), coalesced backwards for %p previous=%p\n", ptr, previous);
4356 }
4357 #endif
4358 small_free_list_remove_ptr(szone, small_mag_ptr, previous, previous_msize);
4359 small_meta_header_set_middle(meta_headers, index);
4360 ptr = previous;
4361 msize += previous_msize;
4362 index -= previous_msize;
4363 }
4364 }
4365 // We try to coalesce with the next block
4366 if ((next_block < SMALL_REGION_END(region)) && (meta_headers[next_index] & SMALL_IS_FREE)) {
4367 // next block is free, we coalesce
4368 next_msize = meta_headers[next_index] & ~ SMALL_IS_FREE;
4369 #if DEBUG_MALLOC
4370 if (LOG(szone,ptr))
4371 malloc_printf("In small_free_no_lock(), for ptr=%p, msize=%d coalesced next block=%p next_msize=%d\n",
4372 ptr, msize, next_block, next_msize);
4373 #endif
4374 small_free_list_remove_ptr(szone, small_mag_ptr, next_block, next_msize);
4375 small_meta_header_set_middle(meta_headers, next_index);
4376 msize += next_msize;
4377 }
4378 if (szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) {
4379 if (!msize) {
4380 szone_error(szone, 1, "incorrect size information - block header was damaged", ptr, NULL);
4381 } else {
4382 memset(ptr, 0x55, SMALL_BYTES_FOR_MSIZE(msize));
4383 }
4384 }
4385 small_free_list_add_ptr(szone, small_mag_ptr, ptr, msize);
4386 small_mag_ptr->mag_num_objects--;
4387 // we use original_size and not msize to avoid double counting the coalesced blocks
4388 small_mag_ptr->mag_num_bytes_in_objects -= original_size;
4389
4390 // Update this region's bytes in use count
4391 region_trailer_t *node = REGION_TRAILER_FOR_SMALL_REGION(region);
4392 size_t bytes_used = node->bytes_used - original_size;
4393 node->bytes_used = bytes_used;
4394
4395 #if !TARGET_OS_EMBEDDED // Always madvise for embedded platforms
4396 /* FIXME: Would Uniprocessor benefit from recirc and MADV_FREE? */
4397 if (szone->num_small_magazines == 1) { // Uniprocessor, single magazine, so no recirculation necessary
4398 /* NOTHING */
4399 } else if (DEPOT_MAGAZINE_INDEX != mag_index) {
4400 // Emptiness discriminant
4401 if (bytes_used < DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES)) {
4402 /* Region has crossed threshold from density to sparsity. Mark it "suitable" on the
4403 recirculation candidates list. */
4404 node->recirc_suitable = TRUE;
4405 } else {
4406 /* After this free, we've found the region is still dense, so it must have been even more so before
4407 the free. That implies the region is already correctly marked. Do nothing. */
4408 }
4409
4410 // Has the entire magazine crossed the "emptiness threshold"? If so, transfer a region
4411 // from this magazine to the Depot. Choose a region that itself has crossed the emptiness threshold (i.e
4412 // is at least fraction "f" empty.) Such a region will be marked "suitable" on the recirculation list.
4413
4414 size_t a = small_mag_ptr->num_bytes_in_magazine; // Total bytes allocated to this magazine
4415 size_t u = small_mag_ptr->mag_num_bytes_in_objects; // In use (malloc'd) from this magaqzine
4416
4417 if (a - u > ((3 * SMALL_REGION_PAYLOAD_BYTES) / 2) && u < DENSITY_THRESHOLD(a)) {
4418 return small_free_do_recirc_to_depot(szone, small_mag_ptr, mag_index);
4419 }
4420
4421 } else {
4422 #endif
4423 // Freed to Depot. N.B. Lock on small_magazines[DEPOT_MAGAZINE_INDEX] is already held
4424 // Calcuate the first page in the coalesced block that would be safe to mark MADV_FREE
4425 uintptr_t safe_ptr = (uintptr_t)ptr + sizeof(free_list_t) + sizeof(msize_t);
4426 uintptr_t round_safe = round_page(safe_ptr);
4427
4428 // Calcuate the last page in the coalesced block that would be safe to mark MADV_FREE
4429 uintptr_t safe_extent = (uintptr_t)ptr + SMALL_BYTES_FOR_MSIZE(msize) - sizeof(msize_t);
4430 uintptr_t trunc_extent = trunc_page(safe_extent);
4431
4432 // The newly freed block may complete a span of bytes that cover one or more pages. Mark the span with MADV_FREE.
4433 if (round_safe < trunc_extent) { // Safe area covers a page (perhaps many)
4434 uintptr_t lo = trunc_page((uintptr_t)original_ptr);
4435 uintptr_t hi = round_page((uintptr_t)original_ptr + original_size);
4436
4437 small_free_list_remove_ptr(szone, small_mag_ptr, ptr, msize);
4438 small_meta_header_set_in_use(meta_headers, index, msize);
4439
4440 OSAtomicIncrement32Barrier(&(node->pinned_to_depot));
4441 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4442 #if TARGET_OS_EMBEDDED
4443 madvise_free_range(szone, region, MAX(round_safe, lo), MIN(trunc_extent, hi), &szone->last_small_advise);
4444 #else
4445 madvise_free_range(szone, region, MAX(round_safe, lo), MIN(trunc_extent, hi));
4446 #endif
4447 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
4448 OSAtomicDecrement32Barrier(&(node->pinned_to_depot));
4449
4450 small_meta_header_set_is_free(meta_headers, index, msize);
4451 small_free_list_add_ptr(szone, small_mag_ptr, ptr, msize);
4452 }
4453
4454 #if !TARGET_OS_EMBEDDED
4455 if (0 < bytes_used || 0 < node->pinned_to_depot) {
4456 /* Depot'd region is still live. Leave it in place on the Depot's recirculation list
4457 so as to avoid thrashing between the Depot's free list and a magazines's free list
4458 with detach_region/reattach_region */
4459 } else {
4460 /* Depot'd region is just now empty. Consider return to OS. */
4461 region_t r_dealloc = small_free_try_depot_unmap_no_lock(szone, small_mag_ptr, node);
4462 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4463 if (r_dealloc)
4464 deallocate_pages(szone, r_dealloc, SMALL_REGION_SIZE, 0);
4465 return FALSE; // Caller need not unlock
4466 }
4467 }
4468 #endif
4469
4470 return TRUE; // Caller must do SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr)
4471 }
4472
4473 // Allocates from the last region or a freshly allocated region
4474 static void *
4475 small_malloc_from_region_no_lock(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index,
4476 msize_t msize, void *aligned_address)
4477 {
4478 void *ptr;
4479
4480 // Before anything we transform the mag_bytes_free_at_end or mag_bytes_free_at_start - if any - to a regular free block
4481 /* FIXME: last_block needs to be coalesced with previous entry if free, <rdar://5462322> */
4482 if (small_mag_ptr->mag_bytes_free_at_end || small_mag_ptr->mag_bytes_free_at_start)
4483 small_finalize_region(szone, small_mag_ptr);
4484
4485 // Here find the only place in smallville that (infrequently) takes the small_regions_lock.
4486 // Only one thread at a time should be permitted to assess the density of the hash
4487 // ring and adjust if needed.
4488 // Only one thread at a time should be permitted to insert its new region on
4489 // the hash ring.
4490 // It is safe for all other threads to read the hash ring (hashed_regions) and
4491 // the associated sizes (num_regions_allocated and num_small_regions).
4492
4493 LOCK(szone->small_regions_lock);
4494 // Check to see if the hash ring of small regions needs to grow. Try to
4495 // avoid the hash ring becoming too dense.
4496 if (szone->small_region_generation->num_regions_allocated < (2 * szone->num_small_regions)) {
4497 region_t *new_regions;
4498 size_t new_size;
4499 size_t new_shift = szone->small_region_generation->num_regions_allocated_shift; // In/Out parameter
4500 new_regions = hash_regions_grow_no_lock(szone, szone->small_region_generation->hashed_regions,
4501 szone->small_region_generation->num_regions_allocated,
4502 &new_shift,
4503 &new_size);
4504 // Do not deallocate the current hashed_regions allocation since someone
4505 // may be iterating it. Instead, just leak it.
4506
4507 // Prepare to advance to the "next generation" of the hash ring.
4508 szone->small_region_generation->nextgen->hashed_regions = new_regions;
4509 szone->small_region_generation->nextgen->num_regions_allocated = new_size;
4510 szone->small_region_generation->nextgen->num_regions_allocated_shift = new_shift;
4511
4512 // Throw the switch to atomically advance to the next generation.
4513 szone->small_region_generation = szone->small_region_generation->nextgen;
4514 // Ensure everyone sees the advance.
4515 OSMemoryBarrier();
4516 }
4517 // Tag the region at "aligned_address" as belonging to us,
4518 // and so put it under the protection of the magazine lock we are holding.
4519 // Do this before advertising "aligned_address" on the hash ring(!)
4520 MAGAZINE_INDEX_FOR_SMALL_REGION(aligned_address) = mag_index;
4521
4522 // Insert the new region into the hash ring, and update malloc statistics
4523 hash_region_insert_no_lock(szone->small_region_generation->hashed_regions,
4524 szone->small_region_generation->num_regions_allocated,
4525 szone->small_region_generation->num_regions_allocated_shift,
4526 aligned_address);
4527
4528 szone->num_small_regions++;
4529
4530 UNLOCK(szone->small_regions_lock);
4531
4532 small_mag_ptr->mag_last_region = aligned_address;
4533 BYTES_USED_FOR_SMALL_REGION(aligned_address) = SMALL_BYTES_FOR_MSIZE(msize);
4534 #if ASLR_INTERNAL
4535 int offset_msize = malloc_entropy[1] & SMALL_ENTROPY_MASK;
4536 #if DEBUG_MALLOC
4537 if (getenv("MallocASLRForce")) offset_msize = strtol(getenv("MallocASLRForce"), NULL, 0) & SMALL_ENTROPY_MASK;
4538 if (getenv("MallocASLRPrint")) malloc_printf("Region: %p offset: %d\n", aligned_address, offset_msize);
4539 #endif
4540 #else
4541 int offset_msize = 0;
4542 #endif
4543 ptr = (void *)((uintptr_t) aligned_address + SMALL_BYTES_FOR_MSIZE(offset_msize));
4544 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr), offset_msize, msize);
4545 small_mag_ptr->mag_num_objects++;
4546 small_mag_ptr->mag_num_bytes_in_objects += SMALL_BYTES_FOR_MSIZE(msize);
4547 small_mag_ptr->num_bytes_in_magazine += SMALL_REGION_PAYLOAD_BYTES;
4548
4549 // add a big free block at the end
4550 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr), offset_msize + msize, NUM_SMALL_BLOCKS - msize - offset_msize);
4551 small_mag_ptr->mag_bytes_free_at_end = SMALL_BYTES_FOR_MSIZE(NUM_SMALL_BLOCKS - msize - offset_msize);
4552
4553 #if ASLR_INTERNAL
4554 // add a big free block at the start
4555 small_mag_ptr->mag_bytes_free_at_start = SMALL_BYTES_FOR_MSIZE(offset_msize);
4556 if (offset_msize) {
4557 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr), 0, offset_msize);
4558 }
4559 #else
4560 small_mag_ptr->mag_bytes_free_at_start = 0;
4561 #endif
4562
4563 // connect to magazine as last node
4564 recirc_list_splice_last(szone, small_mag_ptr, REGION_TRAILER_FOR_SMALL_REGION(aligned_address));
4565
4566 return ptr;
4567 }
4568
4569 static INLINE void *
4570 small_try_shrink_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_good_size)
4571 {
4572 msize_t new_msize = SMALL_MSIZE_FOR_BYTES(new_good_size);
4573 msize_t mshrinkage = SMALL_MSIZE_FOR_BYTES(old_size) - new_msize;
4574
4575 if (mshrinkage) {
4576 void *q = (void *)((uintptr_t)ptr + SMALL_BYTES_FOR_MSIZE(new_msize));
4577 magazine_t *small_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->small_magazines,
4578 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr)),
4579 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr)));
4580
4581 // Mark q as block header and in-use, thus creating two blocks.
4582 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr), SMALL_META_INDEX_FOR_PTR(ptr), new_msize);
4583 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q), SMALL_META_INDEX_FOR_PTR(q), mshrinkage);
4584 small_mag_ptr->mag_num_objects++;
4585
4586 SZONE_MAGAZINE_PTR_UNLOCK(szone,small_mag_ptr);
4587 szone_free(szone, q); // avoid inlining free_small(szone, q, ...);
4588 }
4589
4590 return ptr;
4591 }
4592
4593 static INLINE boolean_t
4594 small_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size)
4595 {
4596 // returns 1 on success
4597 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
4598 unsigned index;
4599 msize_t old_msize, new_msize;
4600 unsigned next_index;
4601 void *next_block;
4602 msize_t next_msize_and_free;
4603 boolean_t is_free;
4604 msize_t next_msize, leftover_msize;
4605 void *leftover;
4606
4607 index = SMALL_META_INDEX_FOR_PTR(ptr);
4608 old_msize = SMALL_MSIZE_FOR_BYTES(old_size);
4609 new_msize = SMALL_MSIZE_FOR_BYTES(new_size + SMALL_QUANTUM - 1);
4610 next_index = index + old_msize;
4611
4612 if (next_index >= NUM_SMALL_BLOCKS) {
4613 return 0;
4614 }
4615 next_block = (char *)ptr + old_size;
4616
4617 #if DEBUG_MALLOC
4618 if ((uintptr_t)next_block & (SMALL_QUANTUM - 1)) {
4619 szone_error(szone, 1, "internal invariant broken in realloc(next_block)", next_block, NULL);
4620 }
4621 if (meta_headers[index] != old_msize)
4622 malloc_printf("*** small_try_realloc_in_place incorrect old %d %d\n",
4623 meta_headers[index], old_msize);
4624 #endif
4625
4626 magazine_t *small_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->small_magazines,
4627 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr)),
4628 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr)));
4629
4630 /*
4631 * Look for a free block immediately afterwards. If it's large enough, we can consume (part of)
4632 * it.
4633 */
4634 next_msize_and_free = meta_headers[next_index];
4635 is_free = next_msize_and_free & SMALL_IS_FREE;
4636 if (!is_free) {
4637 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4638 return 0; // next_block is in use;
4639 }
4640 next_msize = next_msize_and_free & ~ SMALL_IS_FREE;
4641 if (old_msize + next_msize < new_msize) {
4642 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4643 return 0; // even with next block, not enough
4644 }
4645 /*
4646 * The following block is big enough; pull it from its freelist and chop off enough to satisfy
4647 * our needs.
4648 */
4649 small_free_list_remove_ptr(szone, small_mag_ptr, next_block, next_msize);
4650 small_meta_header_set_middle(meta_headers, next_index);
4651 leftover_msize = old_msize + next_msize - new_msize;
4652 if (leftover_msize) {
4653 /* there's some left, so put the remainder back */
4654 leftover = (unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(new_msize);
4655
4656 small_free_list_add_ptr(szone, small_mag_ptr, leftover, leftover_msize);
4657 }
4658 #if DEBUG_MALLOC
4659 if (SMALL_BYTES_FOR_MSIZE(new_msize) > szone->large_threshold) {
4660 malloc_printf("*** realloc in place for %p exceeded msize=%d\n", new_msize);
4661 }
4662 #endif
4663 small_meta_header_set_in_use(meta_headers, index, new_msize);
4664 #if DEBUG_MALLOC
4665 if (LOG(szone,ptr)) {
4666 malloc_printf("in small_try_realloc_in_place(), ptr=%p, msize=%d\n", ptr, *SMALL_METADATA_FOR_PTR(ptr));
4667 }
4668 #endif
4669 small_mag_ptr->mag_num_bytes_in_objects += SMALL_BYTES_FOR_MSIZE(new_msize - old_msize);
4670
4671 // Update this region's bytes in use count
4672 region_trailer_t *node = REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr));
4673 size_t bytes_used = node->bytes_used + SMALL_BYTES_FOR_MSIZE(new_msize - old_msize);
4674 node->bytes_used = bytes_used;
4675
4676 // Emptiness discriminant
4677 if (bytes_used < DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES)) {
4678 /* After this reallocation the region is still sparse, so it must have been even more so before
4679 the reallocation. That implies the region is already correctly marked. Do nothing. */
4680 } else {
4681 /* Region has crossed threshold from sparsity to density. Mark it not "suitable" on the
4682 recirculation candidates list. */
4683 node->recirc_suitable = FALSE;
4684 }
4685
4686 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
4687 CHECK(szone, __PRETTY_FUNCTION__);
4688 return 1;
4689 }
4690
4691 static boolean_t
4692 small_check_region(szone_t *szone, region_t region)
4693 {
4694 unsigned char *ptr = SMALL_REGION_ADDRESS(region);
4695 msize_t *meta_headers = SMALL_META_HEADER_FOR_PTR(ptr);
4696 unsigned char *region_end = SMALL_REGION_END(region);
4697 msize_t prev_free = 0;
4698 unsigned index;
4699 msize_t msize_and_free;
4700 msize_t msize;
4701 free_list_t *free_head;
4702 void *previous, *next;
4703 msize_t *follower;
4704 mag_index_t mag_index = MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr));
4705 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
4706
4707 // Assumes locked
4708 CHECK_MAGAZINE_PTR_LOCKED(szone, small_mag_ptr, __PRETTY_FUNCTION__);
4709
4710 if (region == small_mag_ptr->mag_last_region) {
4711 ptr += small_mag_ptr->mag_bytes_free_at_start;
4712 region_end -= small_mag_ptr->mag_bytes_free_at_end;
4713 }
4714
4715 while (ptr < region_end) {
4716 index = SMALL_META_INDEX_FOR_PTR(ptr);
4717 msize_and_free = meta_headers[index];
4718 if (!(msize_and_free & SMALL_IS_FREE)) {
4719 // block is in use
4720 msize = msize_and_free;
4721 if (!msize) {
4722 malloc_printf("*** invariant broken: null msize ptr=%p num_small_regions=%d end=%p\n",
4723 ptr, szone->num_small_regions, region_end);
4724 return 0;
4725 }
4726 #if !RELAXED_INVARIANT_CHECKS
4727 if (SMALL_BYTES_FOR_MSIZE(msize) > szone->large_threshold) {
4728 malloc_printf("*** invariant broken for %p this small msize=%d - size is too large\n",
4729 ptr, msize_and_free);
4730 return 0;
4731 }
4732 #endif // RELAXED_INVARIANT_CHECKS
4733 ptr += SMALL_BYTES_FOR_MSIZE(msize);
4734 prev_free = 0;
4735 } else {
4736 // free pointer
4737 msize = msize_and_free & ~ SMALL_IS_FREE;
4738 free_head = (free_list_t *)ptr;
4739 follower = (msize_t *)FOLLOWING_SMALL_PTR(ptr, msize);
4740 if (!msize) {
4741 malloc_printf("*** invariant broken for free block %p this msize=%d\n", ptr, msize);
4742 return 0;
4743 }
4744 #if !RELAXED_INVARIANT_CHECKS
4745 if (prev_free) {
4746 malloc_printf("*** invariant broken for %p (2 free in a row)\n", ptr);
4747 return 0;
4748 }
4749 #endif
4750 previous = free_list_unchecksum_ptr(szone, &free_head->previous);
4751 next = free_list_unchecksum_ptr(szone, &free_head->next);
4752 if (previous && !SMALL_PTR_IS_FREE(previous)) {
4753 malloc_printf("*** invariant broken for %p (previous %p is not a free pointer)\n",
4754 ptr, free_head->previous);
4755 return 0;
4756 }
4757 if (next && !SMALL_PTR_IS_FREE(next)) {
4758 malloc_printf("*** invariant broken for %p (next is not a free pointer)\n", ptr);
4759 return 0;
4760 }
4761 if (SMALL_PREVIOUS_MSIZE(follower) != msize) {
4762 malloc_printf("*** invariant broken for small free %p followed by %p in region [%p-%p] "
4763 "(end marker incorrect) should be %d; in fact %d\n",
4764 ptr, follower, SMALL_REGION_ADDRESS(region), region_end, msize, SMALL_PREVIOUS_MSIZE(follower));
4765 return 0;
4766 }
4767 ptr = (unsigned char *)follower;
4768 prev_free = SMALL_IS_FREE;
4769 }
4770 }
4771 return 1;
4772 }
4773
4774 static kern_return_t
4775 small_in_use_enumerator(task_t task, void *context, unsigned type_mask, szone_t *szone,
4776 memory_reader_t reader, vm_range_recorder_t recorder)
4777 {
4778 size_t num_regions;
4779 size_t index;
4780 region_t *regions;
4781 vm_range_t buffer[MAX_RECORDER_BUFFER];
4782 unsigned count = 0;
4783 kern_return_t err;
4784 region_t region;
4785 vm_range_t range;
4786 vm_range_t admin_range;
4787 vm_range_t ptr_range;
4788 unsigned char *mapped_region;
4789 msize_t *block_header;
4790 unsigned block_index;
4791 unsigned block_limit;
4792 msize_t msize_and_free;
4793 msize_t msize;
4794 magazine_t *small_mag_base = NULL;
4795
4796 region_hash_generation_t *srg_ptr;
4797 err = reader(task, (vm_address_t)szone->small_region_generation, sizeof(region_hash_generation_t), (void **)&srg_ptr);
4798 if (err) return err;
4799
4800 num_regions = srg_ptr->num_regions_allocated;
4801 err = reader(task, (vm_address_t)srg_ptr->hashed_regions, sizeof(region_t) * num_regions, (void **)&regions);
4802 if (err) return err;
4803
4804 if (type_mask & MALLOC_PTR_IN_USE_RANGE_TYPE) {
4805 // Map in all active magazines. Do this outside the iteration over regions.
4806 err = reader(task, (vm_address_t)(szone->small_magazines),
4807 szone->num_small_magazines*sizeof(magazine_t),(void **)&small_mag_base);
4808 if (err) return err;
4809 }
4810
4811 for (index = 0; index < num_regions; ++index) {
4812 region = regions[index];
4813 if (HASHRING_OPEN_ENTRY != region && HASHRING_REGION_DEALLOCATED != region) {
4814 range.address = (vm_address_t)SMALL_REGION_ADDRESS(region);
4815 range.size = SMALL_REGION_SIZE;
4816 if (type_mask & MALLOC_ADMIN_REGION_RANGE_TYPE) {
4817 admin_range.address = range.address + SMALL_METADATA_START;
4818 admin_range.size = SMALL_METADATA_SIZE;
4819 recorder(task, context, MALLOC_ADMIN_REGION_RANGE_TYPE, &admin_range, 1);
4820 }
4821 if (type_mask & (MALLOC_PTR_REGION_RANGE_TYPE | MALLOC_ADMIN_REGION_RANGE_TYPE)) {
4822 ptr_range.address = range.address;
4823 ptr_range.size = NUM_SMALL_BLOCKS * SMALL_QUANTUM;
4824 recorder(task, context, MALLOC_PTR_REGION_RANGE_TYPE, &ptr_range, 1);
4825 }
4826 if (type_mask & MALLOC_PTR_IN_USE_RANGE_TYPE) {
4827 void *mag_last_free;
4828 vm_address_t mag_last_free_ptr = 0;
4829 msize_t mag_last_free_msize = 0;
4830
4831 err = reader(task, range.address, range.size, (void **)&mapped_region);
4832 if (err)
4833 return err;
4834
4835 mag_index_t mag_index = MAGAZINE_INDEX_FOR_SMALL_REGION(mapped_region);
4836 magazine_t *small_mag_ptr = small_mag_base + mag_index;
4837
4838 if (DEPOT_MAGAZINE_INDEX != mag_index) {
4839 mag_last_free = small_mag_ptr->mag_last_free;
4840 if (mag_last_free) {
4841 mag_last_free_ptr = (uintptr_t) mag_last_free & ~(SMALL_QUANTUM - 1);
4842 mag_last_free_msize = (uintptr_t) mag_last_free & (SMALL_QUANTUM - 1);
4843 }
4844 } else {
4845 for (mag_index = 0; mag_index < szone->num_small_magazines; mag_index++) {
4846 if ((void *)range.address == (small_mag_base + mag_index)->mag_last_free_rgn) {
4847 mag_last_free = (small_mag_base + mag_index)->mag_last_free;
4848 if (mag_last_free) {
4849 mag_last_free_ptr = (uintptr_t) mag_last_free & ~(SMALL_QUANTUM - 1);
4850 mag_last_free_msize = (uintptr_t) mag_last_free & (SMALL_QUANTUM - 1);
4851 }
4852 }
4853 }
4854 }
4855
4856 block_header = (msize_t *)(mapped_region + SMALL_METADATA_START + sizeof(region_trailer_t));
4857 block_index = 0;
4858 block_limit = NUM_SMALL_BLOCKS;
4859 if (region == small_mag_ptr->mag_last_region) {
4860 block_index += SMALL_MSIZE_FOR_BYTES(small_mag_ptr->mag_bytes_free_at_start);
4861 block_limit -= SMALL_MSIZE_FOR_BYTES(small_mag_ptr->mag_bytes_free_at_end);
4862 }
4863 while (block_index < block_limit) {
4864 msize_and_free = block_header[block_index];
4865 msize = msize_and_free & ~ SMALL_IS_FREE;
4866 if (! (msize_and_free & SMALL_IS_FREE) &&
4867 range.address + SMALL_BYTES_FOR_MSIZE(block_index) != mag_last_free_ptr) {
4868 // Block in use
4869 buffer[count].address = range.address + SMALL_BYTES_FOR_MSIZE(block_index);
4870 buffer[count].size = SMALL_BYTES_FOR_MSIZE(msize);
4871 count++;
4872 if (count >= MAX_RECORDER_BUFFER) {
4873 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
4874 count = 0;
4875 }
4876 }
4877
4878 if (!msize)
4879 return KERN_FAILURE; // Somethings amiss. Avoid looping at this block_index.
4880
4881 block_index += msize;
4882 }
4883 if (count) {
4884 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE, buffer, count);
4885 count = 0;
4886 }
4887 }
4888 }
4889 }
4890 return 0;
4891 }
4892
4893 static void *
4894 small_malloc_from_free_list(szone_t *szone, magazine_t *small_mag_ptr, mag_index_t mag_index, msize_t msize)
4895 {
4896 free_list_t *ptr;
4897 msize_t this_msize;
4898 grain_t slot = (msize <= szone->num_small_slots) ? msize - 1 : szone->num_small_slots - 1;
4899 free_list_t **free_list = small_mag_ptr->mag_free_list;
4900 free_list_t **the_slot = free_list + slot;
4901 free_list_t *next;
4902 free_list_t **limit;
4903 unsigned bitmap;
4904 msize_t leftover_msize;
4905 free_list_t *leftover_ptr;
4906
4907 // Assumes we've locked the region
4908 CHECK_MAGAZINE_PTR_LOCKED(szone, small_mag_ptr, __PRETTY_FUNCTION__);
4909
4910 // Look for an exact match by checking the freelist for this msize.
4911 //
4912 ptr = *the_slot;
4913 if (ptr) {
4914 next = free_list_unchecksum_ptr(szone, &ptr->next);
4915 if (next) {
4916 next->previous = ptr->previous;
4917 } else {
4918 BITMAPN_CLR(small_mag_ptr->mag_bitmap, slot);
4919 }
4920 *the_slot = next;
4921 this_msize = msize;
4922 goto return_small_alloc;
4923 }
4924
4925 // Mask off the bits representing slots holding free blocks smaller than
4926 // the size we need. If there are no larger free blocks, try allocating
4927 // from the free space at the end of the small region.
4928 if (szone->is_largemem) {
4929 // BITMAPN_CTZ implementation
4930 unsigned idx = slot >> 5;
4931 bitmap = 0;
4932 unsigned mask = ~ ((1 << (slot & 31)) - 1);
4933 for ( ; idx < SMALL_BITMAP_WORDS; ++idx ) {
4934 bitmap = small_mag_ptr->mag_bitmap[idx] & mask;
4935 if (bitmap != 0)
4936 break;
4937 mask = ~0U;
4938 }
4939 // Check for fallthrough: No bits set in bitmap
4940 if ((bitmap == 0) && (idx == SMALL_BITMAP_WORDS))
4941 goto try_small_from_end;
4942
4943 // Start looking at the first set bit, plus 32 bits for every word of
4944 // zeroes or entries that were too small.
4945 slot = BITMAP32_CTZ((&bitmap)) + (idx * 32);
4946 } else {
4947 bitmap = small_mag_ptr->mag_bitmap[0] & ~ ((1 << slot) - 1);
4948 if (!bitmap)
4949 goto try_small_from_end;
4950
4951 slot = BITMAP32_CTZ((&bitmap));
4952 }
4953 // FIXME: Explain use of - 1 here, last slot has special meaning
4954 limit = free_list + szone->num_small_slots - 1;
4955 free_list += slot;
4956
4957 if (free_list < limit) {
4958 ptr = *free_list;
4959 if (ptr) {
4960
4961 next = free_list_unchecksum_ptr(szone, &ptr->next);
4962 *free_list = next;
4963 if (next) {
4964 next->previous = ptr->previous;
4965 } else {
4966 BITMAPN_CLR(small_mag_ptr->mag_bitmap, slot);
4967 }
4968 this_msize = SMALL_PTR_SIZE(ptr);
4969 goto add_leftover_and_proceed;
4970 }
4971 #if DEBUG_MALLOC
4972 malloc_printf("in small_malloc_from_free_list(), mag_bitmap out of sync, slot=%d\n",slot);
4973 #endif
4974 }
4975
4976 // We are now looking at the last slot, which contains blocks equal to, or
4977 // due to coalescing of free blocks, larger than (num_small_slots - 1) * (small quantum size).
4978 // If the last freelist is not empty, and the head contains a block that is
4979 // larger than our request, then the remainder is put back on the free list.
4980 //
4981 ptr = *limit;
4982 if (ptr) {
4983 this_msize = SMALL_PTR_SIZE(ptr);
4984 next = free_list_unchecksum_ptr(szone, &ptr->next);
4985 if (this_msize - msize >= szone->num_small_slots) {
4986 // the leftover will go back to the free list, so we optimize by
4987 // modifying the free list rather than a pop and push of the head
4988 leftover_msize = this_msize - msize;
4989 leftover_ptr = (free_list_t *)((unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(msize));
4990 *limit = leftover_ptr;
4991 if (next) {
4992 next->previous.u = free_list_checksum_ptr(szone, leftover_ptr);
4993 }
4994 leftover_ptr->previous = ptr->previous;
4995 leftover_ptr->next = ptr->next;
4996 small_meta_header_set_is_free(SMALL_META_HEADER_FOR_PTR(leftover_ptr),
4997 SMALL_META_INDEX_FOR_PTR(leftover_ptr), leftover_msize);
4998 // Store msize at the end of the block denoted by "leftover_ptr" (i.e. at a negative offset from follower)
4999 SMALL_PREVIOUS_MSIZE(FOLLOWING_SMALL_PTR(leftover_ptr, leftover_msize)) = leftover_msize; // Access is safe
5000 #if DEBUG_MALLOC
5001 if (LOG(szone,ptr)) {
5002 malloc_printf("in small_malloc_from_free_list(), last slot ptr=%p, msize=%d this_msize=%d\n", ptr, msize, this_msize);
5003 }
5004 #endif
5005 this_msize = msize;
5006 goto return_small_alloc;
5007 }
5008 if (next) {
5009 next->previous = ptr->previous;
5010 }
5011 *limit = next;
5012 goto add_leftover_and_proceed;
5013 }
5014
5015 try_small_from_end:
5016 // Let's see if we can use small_mag_ptr->mag_bytes_free_at_end
5017 if (small_mag_ptr->mag_bytes_free_at_end >= SMALL_BYTES_FOR_MSIZE(msize)) {
5018 ptr = (free_list_t *)(SMALL_REGION_END(small_mag_ptr->mag_last_region) -
5019 small_mag_ptr->mag_bytes_free_at_end);
5020 small_mag_ptr->mag_bytes_free_at_end -= SMALL_BYTES_FOR_MSIZE(msize);
5021 if (small_mag_ptr->mag_bytes_free_at_end) {
5022 // let's mark this block as in use to serve as boundary
5023 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr),
5024 SMALL_META_INDEX_FOR_PTR((unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(msize)),
5025 SMALL_MSIZE_FOR_BYTES(small_mag_ptr->mag_bytes_free_at_end));
5026 }
5027 this_msize = msize;
5028 goto return_small_alloc;
5029 }
5030 #if ASLR_INTERNAL
5031 // Try from start if nothing left at end
5032 if (small_mag_ptr->mag_bytes_free_at_start >= SMALL_BYTES_FOR_MSIZE(msize)) {
5033 ptr = (free_list_t *)(SMALL_REGION_ADDRESS(small_mag_ptr->mag_last_region) +
5034 small_mag_ptr->mag_bytes_free_at_start - SMALL_BYTES_FOR_MSIZE(msize));
5035 small_mag_ptr->mag_bytes_free_at_start -= SMALL_BYTES_FOR_MSIZE(msize);
5036 if (small_mag_ptr->mag_bytes_free_at_start) {
5037 // let's mark this block as in use to serve as boundary
5038 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr), 0, SMALL_MSIZE_FOR_BYTES(small_mag_ptr->mag_bytes_free_at_start));
5039 }
5040 this_msize = msize;
5041 goto return_small_alloc;
5042 }
5043 #endif
5044 return NULL;
5045
5046 add_leftover_and_proceed:
5047 if (this_msize > msize) {
5048 leftover_msize = this_msize - msize;
5049 leftover_ptr = (free_list_t *)((unsigned char *)ptr + SMALL_BYTES_FOR_MSIZE(msize));
5050 #if DEBUG_MALLOC
5051 if (LOG(szone,ptr)) {
5052 malloc_printf("in small_malloc_from_free_list(), adding leftover ptr=%p, this_msize=%d\n", ptr, this_msize);
5053 }
5054 #endif
5055 small_free_list_add_ptr(szone, small_mag_ptr, leftover_ptr, leftover_msize);
5056 this_msize = msize;
5057 }
5058
5059 return_small_alloc:
5060 small_mag_ptr->mag_num_objects++;
5061 small_mag_ptr->mag_num_bytes_in_objects += SMALL_BYTES_FOR_MSIZE(this_msize);
5062
5063 // Update this region's bytes in use count
5064 region_trailer_t *node = REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr));
5065 size_t bytes_used = node->bytes_used + SMALL_BYTES_FOR_MSIZE(this_msize);
5066 node->bytes_used = bytes_used;
5067
5068 // Emptiness discriminant
5069 if (bytes_used < DENSITY_THRESHOLD(SMALL_REGION_PAYLOAD_BYTES)) {
5070 /* After this allocation the region is still sparse, so it must have been even more so before
5071 the allocation. That implies the region is already correctly marked. Do nothing. */
5072 } else {
5073 /* Region has crossed threshold from sparsity to density. Mark in not "suitable" on the
5074 recirculation candidates list. */
5075 node->recirc_suitable = FALSE;
5076 }
5077 #if DEBUG_MALLOC
5078 if (LOG(szone,ptr)) {
5079 malloc_printf("in small_malloc_from_free_list(), ptr=%p, this_msize=%d, msize=%d\n", ptr, this_msize, msize);
5080 }
5081 #endif
5082 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(ptr), SMALL_META_INDEX_FOR_PTR(ptr), this_msize);
5083 return ptr;
5084 }
5085 #undef DENSITY_THRESHOLD
5086 #undef K
5087
5088 static INLINE void *
5089 small_malloc_should_clear(szone_t *szone, msize_t msize, boolean_t cleared_requested)
5090 {
5091 void *ptr;
5092 mag_index_t mag_index = mag_get_thread_index(szone);
5093 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
5094
5095 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
5096
5097 #if SMALL_CACHE
5098 ptr = (void *)small_mag_ptr->mag_last_free;
5099
5100 if ((((uintptr_t)ptr) & (SMALL_QUANTUM - 1)) == msize) {
5101 // we have a winner
5102 small_mag_ptr->mag_last_free = NULL;
5103 small_mag_ptr->mag_last_free_rgn = NULL;
5104 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5105 CHECK(szone, __PRETTY_FUNCTION__);
5106 ptr = (void *)((uintptr_t)ptr & ~ (SMALL_QUANTUM - 1));
5107 if (cleared_requested) {
5108 memset(ptr, 0, SMALL_BYTES_FOR_MSIZE(msize));
5109 }
5110 return ptr;
5111 }
5112 #endif /* SMALL_CACHE */
5113
5114 while(1) {
5115 ptr = small_malloc_from_free_list(szone, small_mag_ptr, mag_index, msize);
5116 if (ptr) {
5117 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5118 CHECK(szone, __PRETTY_FUNCTION__);
5119 if (cleared_requested) {
5120 memset(ptr, 0, SMALL_BYTES_FOR_MSIZE(msize));
5121 }
5122 return ptr;
5123 }
5124
5125 if (small_get_region_from_depot(szone, small_mag_ptr, mag_index, msize)) {
5126 ptr = small_malloc_from_free_list(szone, small_mag_ptr, mag_index, msize);
5127 if (ptr) {
5128 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5129 CHECK(szone, __PRETTY_FUNCTION__);
5130 if (cleared_requested) {
5131 memset(ptr, 0, SMALL_BYTES_FOR_MSIZE(msize));
5132 }
5133 return ptr;
5134 }
5135 }
5136
5137 // The magazine is exhausted. A new region (heap) must be allocated to satisfy this call to malloc().
5138 // The allocation, an mmap() system call, will be performed outside the magazine spin locks by the first
5139 // thread that suffers the exhaustion. That thread sets "alloc_underway" and enters a critical section.
5140 // Threads arriving here later are excluded from the critical section, yield the CPU, and then retry the
5141 // allocation. After some time the magazine is resupplied, the original thread leaves with its allocation,
5142 // and retry-ing threads succeed in the code just above.
5143 if (!small_mag_ptr->alloc_underway) {
5144 void *fresh_region;
5145
5146 // time to create a new region (do this outside the magazine lock)
5147 small_mag_ptr->alloc_underway = TRUE;
5148 OSMemoryBarrier();
5149 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5150 fresh_region = allocate_pages_securely(szone, SMALL_REGION_SIZE, SMALL_BLOCKS_ALIGN, VM_MEMORY_MALLOC_SMALL);
5151 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
5152
5153 MAGMALLOC_ALLOCREGION((void *)szone, (int)mag_index, fresh_region, SMALL_REGION_SIZE); // DTrace USDT Probe
5154
5155 if (!fresh_region) { // out of memory!
5156 small_mag_ptr->alloc_underway = FALSE;
5157 OSMemoryBarrier();
5158 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5159 return NULL;
5160 }
5161
5162 ptr = small_malloc_from_region_no_lock(szone, small_mag_ptr, mag_index, msize, fresh_region);
5163
5164 // we don't clear because this freshly allocated space is pristine
5165 small_mag_ptr->alloc_underway = FALSE;
5166 OSMemoryBarrier();
5167 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5168 CHECK(szone, __PRETTY_FUNCTION__);
5169 return ptr;
5170 } else {
5171 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5172 pthread_yield_np();
5173 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
5174 }
5175 }
5176 /* NOTREACHED */
5177 }
5178
5179 static NOINLINE void
5180 free_small_botch(szone_t *szone, free_list_t *ptr)
5181 {
5182 mag_index_t mag_index = MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr));
5183 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
5184 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5185 szone_error(szone, 1, "double free", ptr, NULL);
5186 }
5187
5188 static INLINE void
5189 free_small(szone_t *szone, void *ptr, region_t small_region, size_t known_size)
5190 {
5191 msize_t msize;
5192 mag_index_t mag_index = MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr));
5193 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
5194
5195 // ptr is known to be in small_region
5196 if (known_size) {
5197 msize = SMALL_MSIZE_FOR_BYTES(known_size + SMALL_QUANTUM - 1);
5198 } else {
5199 msize = SMALL_PTR_SIZE(ptr);
5200 if (SMALL_PTR_IS_FREE(ptr)) {
5201 free_small_botch(szone, ptr);
5202 return;
5203 }
5204 }
5205
5206 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
5207
5208 #if SMALL_CACHE
5209 // Depot does not participate in SMALL_CACHE since it can't be directly malloc()'d
5210 if (DEPOT_MAGAZINE_INDEX != mag_index) {
5211
5212 void *ptr2 = small_mag_ptr->mag_last_free; // Might be NULL
5213 region_t rgn2 = small_mag_ptr->mag_last_free_rgn;
5214
5215 /* check that we don't already have this pointer in the cache */
5216 if (ptr == (void *)((uintptr_t)ptr2 & ~ (SMALL_QUANTUM - 1))) {
5217 free_small_botch(szone, ptr);
5218 return;
5219 }
5220
5221 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && msize)
5222 memset(ptr, 0x55, SMALL_BYTES_FOR_MSIZE(msize));
5223
5224 small_mag_ptr->mag_last_free = (void *)(((uintptr_t)ptr) | msize);
5225 small_mag_ptr->mag_last_free_rgn = small_region;
5226
5227 if (!ptr2) {
5228 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5229 CHECK(szone, __PRETTY_FUNCTION__);
5230 return;
5231 }
5232
5233 msize = (uintptr_t)ptr2 & (SMALL_QUANTUM - 1);
5234 ptr = (void *)(((uintptr_t)ptr2) & ~(SMALL_QUANTUM - 1));
5235 small_region = rgn2;
5236 }
5237 #endif /* SMALL_CACHE */
5238
5239 // Now in the time it took to acquire the lock, the region may have migrated
5240 // from one magazine to another. I.e. trailer->mag_index is volatile.
5241 // In which case the magazine lock we obtained (namely magazines[mag_index].mag_lock)
5242 // is stale. If so, keep on tryin' ...
5243 region_trailer_t *trailer = REGION_TRAILER_FOR_SMALL_REGION(small_region);
5244 mag_index_t refreshed_index;
5245
5246 while (mag_index != (refreshed_index = trailer->mag_index)) { // Note assignment
5247
5248 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5249
5250 mag_index = refreshed_index;
5251 small_mag_ptr = &(szone->small_magazines[mag_index]);
5252 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
5253 }
5254
5255 if (small_free_no_lock(szone, small_mag_ptr, mag_index, small_region, ptr, msize))
5256 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5257
5258 CHECK(szone, __PRETTY_FUNCTION__);
5259 }
5260
5261 static void
5262 print_small_free_list(szone_t *szone)
5263 {
5264 free_list_t *ptr;
5265 _SIMPLE_STRING b = _simple_salloc();
5266 mag_index_t mag_index;
5267
5268 if (b) {
5269 _simple_sappend(b, "small free sizes:\n");
5270 for (mag_index = -1; mag_index < szone->num_small_magazines; mag_index++) {
5271 grain_t slot = 0;
5272 _simple_sprintf(b,"\tMagazine %d: ", mag_index);
5273 while (slot < szone->num_small_slots) {
5274 ptr = szone->small_magazines[mag_index].mag_free_list[slot];
5275 if (ptr) {
5276 _simple_sprintf(b, "%s%y[%d]; ", (slot == szone->num_small_slots-1) ? ">=" : "",
5277 (slot + 1) * SMALL_QUANTUM, free_list_count(szone, ptr));
5278 }
5279 slot++;
5280 }
5281 _simple_sappend(b,"\n");
5282 }
5283 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
5284 _simple_sfree(b);
5285 }
5286 }
5287
5288 static void
5289 print_small_region(szone_t *szone, boolean_t verbose, region_t region, size_t bytes_at_start, size_t bytes_at_end)
5290 {
5291 unsigned counts[1024];
5292 unsigned in_use = 0;
5293 uintptr_t start = (uintptr_t)SMALL_REGION_ADDRESS(region);
5294 uintptr_t current = start + bytes_at_start;
5295 uintptr_t limit = (uintptr_t)SMALL_REGION_END(region) - bytes_at_end;
5296 msize_t msize_and_free;
5297 msize_t msize;
5298 unsigned ci;
5299 _SIMPLE_STRING b;
5300 uintptr_t pgTot = 0;
5301
5302 if (region == HASHRING_REGION_DEALLOCATED) {
5303 if ((b = _simple_salloc()) != NULL) {
5304 _simple_sprintf(b, "Small region [unknown address] was returned to the OS\n");
5305 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
5306 _simple_sfree(b);
5307 }
5308 return;
5309 }
5310
5311 memset(counts, 0, sizeof(counts));
5312 while (current < limit) {
5313 msize_and_free = *SMALL_METADATA_FOR_PTR(current);
5314 msize = msize_and_free & ~ SMALL_IS_FREE;
5315 if (!msize) {
5316 malloc_printf("*** error with %p: msize=%d\n", (void *)current, (unsigned)msize);
5317 break;
5318 }
5319 if (!(msize_and_free & SMALL_IS_FREE)) {
5320 // block in use
5321 if (msize < 1024)
5322 counts[msize]++;
5323 in_use++;
5324 } else {
5325 uintptr_t pgLo = round_page(current + sizeof(free_list_t) + sizeof(msize_t));
5326 uintptr_t pgHi = trunc_page(current + SMALL_BYTES_FOR_MSIZE(msize) - sizeof(msize_t));
5327
5328 if (pgLo < pgHi) {
5329 pgTot += (pgHi - pgLo);
5330 }
5331 }
5332 current += SMALL_BYTES_FOR_MSIZE(msize);
5333 }
5334 if ((b = _simple_salloc()) != NULL) {
5335 _simple_sprintf(b, "Small region [%p-%p, %y] \t", (void *)start, SMALL_REGION_END(region), (int)SMALL_REGION_SIZE);
5336 _simple_sprintf(b, "Magazine=%d \t", MAGAZINE_INDEX_FOR_SMALL_REGION(region));
5337 _simple_sprintf(b, "Allocations in use=%d \t Bytes in use=%ly \t", in_use, BYTES_USED_FOR_SMALL_REGION(region));
5338 if (bytes_at_end || bytes_at_start)
5339 _simple_sprintf(b, "Untouched=%ly ", bytes_at_end + bytes_at_start);
5340 if (DEPOT_MAGAZINE_INDEX == MAGAZINE_INDEX_FOR_SMALL_REGION(region)) {
5341 _simple_sprintf(b, "Advised MADV_FREE=%ly", pgTot);
5342 } else {
5343 _simple_sprintf(b, "Fragments subject to reclamation=%ly", pgTot);
5344 }
5345 if (verbose && in_use) {
5346 _simple_sappend(b, "\n\tSizes in use: ");
5347 for (ci = 0; ci < 1024; ci++)
5348 if (counts[ci])
5349 _simple_sprintf(b, "%d[%d] ", SMALL_BYTES_FOR_MSIZE(ci), counts[ci]);
5350 }
5351 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
5352 _simple_sfree(b);
5353 }
5354 }
5355
5356 static boolean_t
5357 small_free_list_check(szone_t *szone, grain_t slot)
5358 {
5359 mag_index_t mag_index;
5360
5361 for (mag_index = -1; mag_index < szone->num_small_magazines; mag_index++) {
5362 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
5363 SZONE_MAGAZINE_PTR_LOCK(szone, small_mag_ptr);
5364
5365 unsigned count = 0;
5366 free_list_t *ptr = szone->small_magazines[mag_index].mag_free_list[slot];
5367 msize_t msize_and_free;
5368 free_list_t *previous = NULL;
5369
5370 while (ptr) {
5371 msize_and_free = *SMALL_METADATA_FOR_PTR(ptr);
5372 if (!(msize_and_free & SMALL_IS_FREE)) {
5373 malloc_printf("*** in-use ptr in free list slot=%d count=%d ptr=%p\n", slot, count, ptr);
5374 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5375 return 0;
5376 }
5377 if (((uintptr_t)ptr) & (SMALL_QUANTUM - 1)) {
5378 malloc_printf("*** unaligned ptr in free list slot=%d count=%d ptr=%p\n", slot, count, ptr);
5379 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5380 return 0;
5381 }
5382 if (!small_region_for_ptr_no_lock(szone, ptr)) {
5383 malloc_printf("*** ptr not in szone slot=%d count=%d ptr=%p\n", slot, count, ptr);
5384 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5385 return 0;
5386 }
5387 if (free_list_unchecksum_ptr(szone, &ptr->previous) != previous) {
5388 malloc_printf("*** previous incorrectly set slot=%d count=%d ptr=%p\n", slot, count, ptr);
5389 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5390 return 0;
5391 }
5392 previous = ptr;
5393 ptr = free_list_unchecksum_ptr(szone, &ptr->next);
5394 count++;
5395 }
5396
5397 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
5398 }
5399 return 1;
5400 }
5401
5402 /*******************************************************************************
5403 * Large allocator implementation
5404 ******************************************************************************/
5405 #pragma mark large allocator
5406
5407 #if DEBUG_MALLOC
5408
5409 static void
5410 large_debug_print(szone_t *szone)
5411 {
5412 unsigned index;
5413 large_entry_t *range;
5414 _SIMPLE_STRING b = _simple_salloc();
5415
5416 if (b) {
5417 for (index = 0, range = szone->large_entries; index < szone->num_large_entries; index++, range++)
5418 if (range->address)
5419 _simple_sprintf(b, "%d: %p(%y); ", index, range->address, range->size);
5420
5421 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX, "%s\n", _simple_string(b));
5422 _simple_sfree(b);
5423 }
5424 }
5425 #endif
5426
5427 /*
5428 * Scan the hash ring looking for an entry for the given pointer.
5429 */
5430 static large_entry_t *
5431 large_entry_for_pointer_no_lock(szone_t *szone, const void *ptr)
5432 {
5433 // result only valid with lock held
5434 unsigned num_large_entries = szone->num_large_entries;
5435 unsigned hash_index;
5436 unsigned index;
5437 large_entry_t *range;
5438
5439 if (!num_large_entries)
5440 return NULL;
5441
5442 hash_index = ((uintptr_t)ptr >> vm_page_shift) % num_large_entries;
5443 index = hash_index;
5444
5445 do {
5446 range = szone->large_entries + index;
5447 if (range->address == (vm_address_t)ptr)
5448 return range;
5449 if (0 == range->address)
5450 return NULL; // end of chain
5451 index++;
5452 if (index == num_large_entries)
5453 index = 0;
5454 } while (index != hash_index);
5455
5456 return NULL;
5457 }
5458
5459 static void
5460 large_entry_insert_no_lock(szone_t *szone, large_entry_t range)
5461 {
5462 unsigned num_large_entries = szone->num_large_entries;
5463 unsigned hash_index = (((uintptr_t)(range.address)) >> vm_page_shift) % num_large_entries;
5464 unsigned index = hash_index;
5465 large_entry_t *entry;
5466
5467 // assert(szone->num_large_objects_in_use < szone->num_large_entries); /* must be called with room to spare */
5468
5469 do {
5470 entry = szone->large_entries + index;
5471 if (0 == entry->address) {
5472 *entry = range;
5473 return; // end of chain
5474 }
5475 index++;
5476 if (index == num_large_entries)
5477 index = 0;
5478 } while (index != hash_index);
5479
5480 // assert(0); /* must not fallthrough! */
5481 }
5482
5483 // FIXME: can't we simply swap the (now empty) entry with the last entry on the collision chain for this hash slot?
5484 static INLINE void
5485 large_entries_rehash_after_entry_no_lock(szone_t *szone, large_entry_t *entry)
5486 {
5487 unsigned num_large_entries = szone->num_large_entries;
5488 unsigned hash_index = entry - szone->large_entries;
5489 unsigned index = hash_index;
5490 large_entry_t range;
5491
5492 // assert(entry->address == 0) /* caller must have cleared *entry */
5493
5494 do {
5495 index++;
5496 if (index == num_large_entries)
5497 index = 0;
5498 range = szone->large_entries[index];
5499 if (0 == range.address)
5500 return;
5501 szone->large_entries[index].address = (vm_address_t)0;
5502 szone->large_entries[index].size = 0;
5503 szone->large_entries[index].did_madvise_reusable = FALSE;
5504 large_entry_insert_no_lock(szone, range); // this will reinsert in the
5505 // proper place
5506 } while (index != hash_index);
5507
5508 // assert(0); /* since entry->address == 0, must not fallthrough! */
5509 }
5510
5511 // FIXME: num should probably be a size_t, since you can theoretically allocate
5512 // more than 2^32-1 large_threshold objects in 64 bit.
5513 static INLINE large_entry_t *
5514 large_entries_alloc_no_lock(szone_t *szone, unsigned num)
5515 {
5516 size_t size = num * sizeof(large_entry_t);
5517
5518 // Note that we allocate memory (via a system call) under a spin lock
5519 // That is certainly evil, however it's very rare in the lifetime of a process
5520 // The alternative would slow down the normal case
5521 return allocate_pages(szone, round_page(size), 0, 0, VM_MEMORY_MALLOC_LARGE);
5522 }
5523
5524 static void
5525 large_entries_free_no_lock(szone_t *szone, large_entry_t *entries, unsigned num, vm_range_t *range_to_deallocate)
5526 {
5527 size_t size = num * sizeof(large_entry_t);
5528
5529 range_to_deallocate->address = (vm_address_t)entries;
5530 range_to_deallocate->size = round_page(size);
5531 }
5532
5533 static large_entry_t *
5534 large_entries_grow_no_lock(szone_t *szone, vm_range_t *range_to_deallocate)
5535 {
5536 // sets range_to_deallocate
5537 unsigned old_num_entries = szone->num_large_entries;
5538 large_entry_t *old_entries = szone->large_entries;
5539 // always an odd number for good hashing
5540 unsigned new_num_entries = (old_num_entries) ? old_num_entries * 2 + 1 :
5541 ((vm_page_size / sizeof(large_entry_t)) - 1);
5542 large_entry_t *new_entries = large_entries_alloc_no_lock(szone, new_num_entries);
5543 unsigned index = old_num_entries;
5544 large_entry_t oldRange;
5545
5546 // if the allocation of new entries failed, bail
5547 if (new_entries == NULL)
5548 return NULL;
5549
5550 szone->num_large_entries = new_num_entries;
5551 szone->large_entries = new_entries;
5552
5553 /* rehash entries into the new list */
5554 while (index--) {
5555 oldRange = old_entries[index];
5556 if (oldRange.address) {
5557 large_entry_insert_no_lock(szone, oldRange);
5558 }
5559 }
5560
5561 if (old_entries) {
5562 large_entries_free_no_lock(szone, old_entries, old_num_entries, range_to_deallocate);
5563 } else {
5564 range_to_deallocate->address = (vm_address_t)0;
5565 range_to_deallocate->size = 0;
5566 }
5567
5568 return new_entries;
5569 }
5570
5571 // frees the specific entry in the size table
5572 // returns a range to truly deallocate
5573 static vm_range_t
5574 large_entry_free_no_lock(szone_t *szone, large_entry_t *entry)
5575 {
5576 vm_range_t range;
5577
5578 range.address = entry->address;
5579 range.size = entry->size;
5580
5581 if (szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) {
5582 protect((void *)range.address, range.size, PROT_READ | PROT_WRITE, szone->debug_flags);
5583 range.address -= vm_page_size;
5584 range.size += 2 * vm_page_size;
5585 }
5586
5587 entry->address = 0;
5588 entry->size = 0;
5589 entry->did_madvise_reusable = FALSE;
5590 large_entries_rehash_after_entry_no_lock(szone, entry);
5591
5592 #if DEBUG_MALLOC
5593 if (large_entry_for_pointer_no_lock(szone, (void *)range.address)) {
5594 malloc_printf("*** freed entry %p still in use; num_large_entries=%d\n",
5595 range.address, szone->num_large_entries);
5596 large_debug_print(szone);
5597 szone_sleep();
5598 }
5599 #endif
5600 return range;
5601 }
5602
5603 static NOINLINE kern_return_t
5604 large_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t large_entries_address,
5605 unsigned num_entries, memory_reader_t reader, vm_range_recorder_t recorder)
5606 {
5607 unsigned index = 0;
5608 vm_range_t buffer[MAX_RECORDER_BUFFER];
5609 unsigned count = 0;
5610 large_entry_t *entries;
5611 kern_return_t err;
5612 vm_range_t range;
5613 large_entry_t entry;
5614
5615 err = reader(task, large_entries_address, sizeof(large_entry_t) * num_entries, (void **)&entries);
5616 if (err)
5617 return err;
5618
5619 index = num_entries;
5620 if (type_mask & MALLOC_ADMIN_REGION_RANGE_TYPE) {
5621 range.address = large_entries_address;
5622 range.size = round_page(num_entries * sizeof(large_entry_t));
5623 recorder(task, context, MALLOC_ADMIN_REGION_RANGE_TYPE, &range, 1);
5624 }
5625 if (type_mask & (MALLOC_PTR_IN_USE_RANGE_TYPE | MALLOC_PTR_REGION_RANGE_TYPE)) {
5626 while (index--) {
5627 entry = entries[index];
5628 if (entry.address) {
5629 range.address = entry.address;
5630 range.size = entry.size;
5631 buffer[count++] = range;
5632 if (count >= MAX_RECORDER_BUFFER) {
5633 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE | MALLOC_PTR_REGION_RANGE_TYPE,
5634 buffer, count);
5635 count = 0;
5636 }
5637 }
5638 }
5639 }
5640 if (count) {
5641 recorder(task, context, MALLOC_PTR_IN_USE_RANGE_TYPE | MALLOC_PTR_REGION_RANGE_TYPE,
5642 buffer, count);
5643 }
5644 return 0;
5645 }
5646
5647 static void *
5648 large_malloc(szone_t *szone, size_t num_pages, unsigned char alignment,
5649 boolean_t cleared_requested)
5650 {
5651 void *addr;
5652 vm_range_t range_to_deallocate;
5653 size_t size;
5654 large_entry_t large_entry;
5655
5656 if (!num_pages)
5657 num_pages = 1; // minimal allocation size for this szone
5658 size = (size_t)num_pages << vm_page_shift;
5659 range_to_deallocate.size = 0;
5660 range_to_deallocate.address = 0;
5661
5662 #if LARGE_CACHE
5663 if (size < LARGE_CACHE_SIZE_ENTRY_LIMIT) { // Look for a large_entry_t on the death-row cache?
5664 SZONE_LOCK(szone);
5665
5666 int i, best = -1, idx = szone->large_entry_cache_newest, stop_idx = szone->large_entry_cache_oldest;
5667 size_t best_size = SIZE_T_MAX;
5668
5669 while (1) { // Scan large_entry_cache for best fit, starting with most recent entry
5670 size_t this_size = szone->large_entry_cache[idx].size;
5671 addr = (void *)szone->large_entry_cache[idx].address;
5672
5673 if (0 == alignment || 0 == (((uintptr_t) addr) & (((uintptr_t) 1 << alignment) - 1))) {
5674 if (size == this_size) { // size match!
5675 best = idx;
5676 best_size = this_size;
5677 break;
5678 }
5679
5680 if (size <= this_size && this_size < best_size) { // improved fit?
5681 best = idx;
5682 best_size = this_size;
5683 }
5684 }
5685
5686 if (idx == stop_idx) // exhausted live ring?
5687 break;
5688
5689 if (idx)
5690 idx--; // bump idx down
5691 else
5692 idx = LARGE_ENTRY_CACHE_SIZE - 1; // wrap idx
5693 }
5694
5695 if (best > -1 && (best_size - size) < size) { //limit fragmentation to 50%
5696 addr = (void *)szone->large_entry_cache[best].address;
5697 boolean_t was_madvised_reusable = szone->large_entry_cache[best].did_madvise_reusable;
5698
5699 // Compact live ring to fill entry now vacated at large_entry_cache[best]
5700 // while preserving time-order
5701 if (szone->large_entry_cache_oldest < szone->large_entry_cache_newest) {
5702
5703 // Ring hasn't wrapped. Fill in from right.
5704 for (i = best; i < szone->large_entry_cache_newest; ++i)
5705 szone->large_entry_cache[i] = szone->large_entry_cache[i + 1];
5706
5707 szone->large_entry_cache_newest--; // Pull in right endpoint.
5708
5709 } else if (szone->large_entry_cache_newest < szone->large_entry_cache_oldest) {
5710
5711 // Ring has wrapped. Arrange to fill in from the contiguous side.
5712 if (best <= szone->large_entry_cache_newest) {
5713 // Fill from right.
5714 for (i = best; i < szone->large_entry_cache_newest; ++i)
5715 szone->large_entry_cache[i] = szone->large_entry_cache[i + 1];
5716
5717 if (0 < szone->large_entry_cache_newest)
5718 szone->large_entry_cache_newest--;
5719 else
5720 szone->large_entry_cache_newest = LARGE_ENTRY_CACHE_SIZE - 1;
5721 } else {
5722 // Fill from left.
5723 for ( i = best; i > szone->large_entry_cache_oldest; --i)
5724 szone->large_entry_cache[i] = szone->large_entry_cache[i - 1];
5725
5726 if (szone->large_entry_cache_oldest < LARGE_ENTRY_CACHE_SIZE - 1)
5727 szone->large_entry_cache_oldest++;
5728 else
5729 szone->large_entry_cache_oldest = 0;
5730 }
5731
5732 } else {
5733 // By trichotomy, large_entry_cache_newest == large_entry_cache_oldest.
5734 // That implies best == large_entry_cache_newest == large_entry_cache_oldest
5735 // and the ring is now empty.
5736 szone->large_entry_cache[best].address = 0;
5737 szone->large_entry_cache[best].size = 0;
5738 szone->large_entry_cache[best].did_madvise_reusable = FALSE;
5739 }
5740
5741 if ((szone->num_large_objects_in_use + 1) * 4 > szone->num_large_entries) {
5742 // density of hash table too high; grow table
5743 // we do that under lock to avoid a race
5744 large_entry_t *entries = large_entries_grow_no_lock(szone, &range_to_deallocate);
5745 if (entries == NULL) {
5746 SZONE_UNLOCK(szone);
5747 return NULL;
5748 }
5749 }
5750
5751 large_entry.address = (vm_address_t)addr;
5752 large_entry.size = best_size;
5753 large_entry.did_madvise_reusable = FALSE;
5754 large_entry_insert_no_lock(szone, large_entry);
5755
5756 szone->num_large_objects_in_use ++;
5757 szone->num_bytes_in_large_objects += best_size;
5758 if (!was_madvised_reusable)
5759 szone->large_entry_cache_reserve_bytes -= best_size;
5760
5761 szone->large_entry_cache_bytes -= best_size;
5762
5763 if (szone->flotsam_enabled && szone->large_entry_cache_bytes < SZONE_FLOTSAM_THRESHOLD_LOW) {
5764 szone->flotsam_enabled = FALSE;
5765 }
5766
5767 SZONE_UNLOCK(szone);
5768
5769 if (range_to_deallocate.size) {
5770 // we deallocate outside the lock
5771 deallocate_pages(szone, (void *)range_to_deallocate.address, range_to_deallocate.size, 0);
5772 }
5773
5774 // Perform the madvise() outside the lock.
5775 // Typically the madvise() is successful and we'll quickly return from this routine.
5776 // In the unusual case of failure, reacquire the lock to unwind.
5777 #if TARGET_OS_EMBEDDED
5778 // Ok to do this madvise on embedded because we won't call MADV_FREE_REUSABLE on a large
5779 // cache block twice without MADV_FREE_REUSE in between.
5780 #endif
5781 if (was_madvised_reusable && -1 == madvise(addr, size, MADV_FREE_REUSE)) {
5782 /* -1 return: VM map entry change makes this unfit for reuse. */
5783 #if DEBUG_MADVISE
5784 szone_error(szone, 0, "large_malloc madvise(..., MADV_FREE_REUSE) failed",
5785 addr, "length=%d\n", size);
5786 #endif
5787
5788 SZONE_LOCK(szone);
5789 szone->num_large_objects_in_use--;
5790 szone->num_bytes_in_large_objects -= large_entry.size;
5791
5792 // Re-acquire "entry" after interval just above where we let go the lock.
5793 large_entry_t *entry = large_entry_for_pointer_no_lock(szone, addr);
5794 if (NULL == entry) {
5795 szone_error(szone, 1, "entry for pointer being discarded from death-row vanished", addr, NULL);
5796 SZONE_UNLOCK(szone);
5797 } else {
5798
5799 range_to_deallocate = large_entry_free_no_lock(szone, entry);
5800 SZONE_UNLOCK(szone);
5801
5802 if (range_to_deallocate.size) {
5803 // we deallocate outside the lock
5804 deallocate_pages(szone, (void *)range_to_deallocate.address, range_to_deallocate.size, 0);
5805 }
5806 }
5807 /* Fall through to allocate_pages() afresh. */
5808 } else {
5809 if (cleared_requested) {
5810 memset(addr, 0, size);
5811 }
5812
5813 return addr;
5814 }
5815 } else {
5816 SZONE_UNLOCK(szone);
5817 }
5818 }
5819
5820 range_to_deallocate.size = 0;
5821 range_to_deallocate.address = 0;
5822 #endif /* LARGE_CACHE */
5823
5824 addr = allocate_pages(szone, size, alignment, szone->debug_flags, VM_MEMORY_MALLOC_LARGE);
5825 if (addr == NULL) {
5826 return NULL;
5827 }
5828
5829 SZONE_LOCK(szone);
5830 if ((szone->num_large_objects_in_use + 1) * 4 > szone->num_large_entries) {
5831 // density of hash table too high; grow table
5832 // we do that under lock to avoid a race
5833 large_entry_t *entries = large_entries_grow_no_lock(szone, &range_to_deallocate);
5834 if (entries == NULL) {
5835 SZONE_UNLOCK(szone);
5836 return NULL;
5837 }
5838 }
5839
5840 large_entry.address = (vm_address_t)addr;
5841 large_entry.size = size;
5842 large_entry.did_madvise_reusable = FALSE;
5843 large_entry_insert_no_lock(szone, large_entry);
5844
5845 szone->num_large_objects_in_use ++;
5846 szone->num_bytes_in_large_objects += size;
5847 SZONE_UNLOCK(szone);
5848
5849 if (range_to_deallocate.size) {
5850 // we deallocate outside the lock
5851 deallocate_pages(szone, (void *)range_to_deallocate.address, range_to_deallocate.size, 0);
5852 }
5853 return addr;
5854 }
5855
5856 static NOINLINE void
5857 free_large(szone_t *szone, void *ptr)
5858 {
5859 // We have established ptr is page-aligned and neither tiny nor small
5860 large_entry_t *entry;
5861 vm_range_t vm_range_to_deallocate;
5862
5863 SZONE_LOCK(szone);
5864 entry = large_entry_for_pointer_no_lock(szone, ptr);
5865 if (entry) {
5866 #if LARGE_CACHE
5867 #ifndef MADV_CAN_REUSE
5868 #define MADV_CAN_REUSE 9 /* per Francois, for testing until xnu is resubmitted to B&I */
5869 #endif
5870 if (entry->size < LARGE_CACHE_SIZE_ENTRY_LIMIT &&
5871 -1 != madvise((void *)(entry->address), entry->size, MADV_CAN_REUSE)) { // Put the large_entry_t on the death-row cache?
5872 int idx = szone->large_entry_cache_newest, stop_idx = szone->large_entry_cache_oldest;
5873 large_entry_t this_entry = *entry; // Make a local copy, "entry" is volatile when lock is let go.
5874 boolean_t reusable = TRUE;
5875 boolean_t should_madvise = szone->large_entry_cache_reserve_bytes + this_entry.size > szone->large_entry_cache_reserve_limit;
5876
5877 // Already freed?
5878 // [Note that repeated entries in death-row risk vending the same entry subsequently
5879 // to two different malloc() calls. By checking here the (illegal) double free
5880 // is accommodated, matching the behavior of the previous implementation.]
5881 while (1) { // Scan large_entry_cache starting with most recent entry
5882 if (szone->large_entry_cache[idx].address == entry->address) {
5883 szone_error(szone, 1, "pointer being freed already on death-row", ptr, NULL);
5884 SZONE_UNLOCK(szone);
5885 return;
5886 }
5887
5888 if (idx == stop_idx) // exhausted live ring?
5889 break;
5890
5891 if (idx)
5892 idx--; // bump idx down
5893 else
5894 idx = LARGE_ENTRY_CACHE_SIZE - 1; // wrap idx
5895 }
5896
5897 SZONE_UNLOCK(szone);
5898
5899 if (szone->debug_flags & SCALABLE_MALLOC_PURGEABLE) { // Are we a purgable zone?
5900 int state = VM_PURGABLE_NONVOLATILE; // restore to default condition
5901
5902 if (KERN_SUCCESS != vm_purgable_control(mach_task_self(), this_entry.address, VM_PURGABLE_SET_STATE, &state)) {
5903 malloc_printf("*** can't vm_purgable_control(..., VM_PURGABLE_SET_STATE) for large freed block at %p\n",
5904 this_entry.address);
5905 reusable = FALSE;
5906 }
5907 }
5908
5909 if (szone->large_legacy_reset_mprotect) { // Linked for Leopard?
5910 // Accomodate Leopard apps that (illegally) mprotect() their own guard pages on large malloc'd allocations
5911 int err = mprotect((void *)(this_entry.address), this_entry.size, PROT_READ | PROT_WRITE);
5912 if (err) {
5913 malloc_printf("*** can't reset protection for large freed block at %p\n", this_entry.address);
5914 reusable = FALSE;
5915 }
5916 }
5917
5918 // madvise(..., MADV_REUSABLE) death-row arrivals if hoarding would exceed large_entry_cache_reserve_limit
5919 if (should_madvise) {
5920 // Issue madvise to avoid paging out the dirtied free()'d pages in "entry"
5921 MAGMALLOC_MADVFREEREGION((void *)szone, (void *)0, (void *)(this_entry.address), this_entry.size); // DTrace USDT Probe
5922
5923 #if TARGET_OS_EMBEDDED
5924 // Ok to do this madvise on embedded because we won't call MADV_FREE_REUSABLE on a large
5925 // cache block twice without MADV_FREE_REUSE in between.
5926 #endif
5927 if (-1 == madvise((void *)(this_entry.address), this_entry.size, MADV_FREE_REUSABLE)) {
5928 /* -1 return: VM map entry change makes this unfit for reuse. */
5929 #if DEBUG_MADVISE
5930 szone_error(szone, 0, "free_large madvise(..., MADV_FREE_REUSABLE) failed",
5931 (void *)this_entry.address, "length=%d\n", this_entry.size);
5932 #endif
5933 reusable = FALSE;
5934 }
5935 }
5936
5937 SZONE_LOCK(szone);
5938
5939 // Re-acquire "entry" after interval just above where we let go the lock.
5940 entry = large_entry_for_pointer_no_lock(szone, ptr);
5941 if (NULL == entry) {
5942 szone_error(szone, 1, "entry for pointer being freed from death-row vanished", ptr, NULL);
5943 SZONE_UNLOCK(szone);
5944 return;
5945 }
5946
5947 // Add "entry" to death-row ring
5948 if (reusable) {
5949 int idx = szone->large_entry_cache_newest; // Most recently occupied
5950 vm_address_t addr;
5951 size_t adjsize;
5952
5953 if (szone->large_entry_cache_newest == szone->large_entry_cache_oldest &&
5954 0 == szone->large_entry_cache[idx].address) {
5955 // Ring is empty, idx is good as it stands
5956 addr = 0;
5957 adjsize = 0;
5958 } else {
5959 // Extend the queue to the "right" by bumping up large_entry_cache_newest
5960 if (idx == LARGE_ENTRY_CACHE_SIZE - 1)
5961 idx = 0; // Wrap index
5962 else
5963 idx++; // Bump index
5964
5965 if (idx == szone->large_entry_cache_oldest) { // Fully occupied
5966 // Drop this entry from the cache and deallocate the VM
5967 addr = szone->large_entry_cache[idx].address;
5968 adjsize = szone->large_entry_cache[idx].size;
5969 szone->large_entry_cache_bytes -= adjsize;
5970 if (!szone->large_entry_cache[idx].did_madvise_reusable)
5971 szone->large_entry_cache_reserve_bytes -= adjsize;
5972 } else {
5973 // Using an unoccupied cache slot
5974 addr = 0;
5975 adjsize = 0;
5976 }
5977 }
5978
5979 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE))
5980 memset((void *)(entry->address), 0x55, entry->size);
5981
5982 entry->did_madvise_reusable = should_madvise; // Was madvise()'d above?
5983 if (!should_madvise) // Entered on death-row without madvise() => up the hoard total
5984 szone->large_entry_cache_reserve_bytes += entry->size;
5985
5986 szone->large_entry_cache_bytes += entry->size;
5987
5988 if (!szone->flotsam_enabled && szone->large_entry_cache_bytes > SZONE_FLOTSAM_THRESHOLD_HIGH) {
5989 szone->flotsam_enabled = TRUE;
5990 }
5991
5992 szone->large_entry_cache[idx] = *entry;
5993 szone->large_entry_cache_newest = idx;
5994
5995 szone->num_large_objects_in_use--;
5996 szone->num_bytes_in_large_objects -= entry->size;
5997
5998 (void)large_entry_free_no_lock(szone, entry);
5999
6000 if (0 == addr) {
6001 SZONE_UNLOCK(szone);
6002 return;
6003 }
6004
6005 // Fall through to drop large_entry_cache_oldest from the cache,
6006 // and then deallocate its pages.
6007
6008 // Trim the queue on the "left" by bumping up large_entry_cache_oldest
6009 if (szone->large_entry_cache_oldest == LARGE_ENTRY_CACHE_SIZE - 1)
6010 szone->large_entry_cache_oldest = 0;
6011 else
6012 szone->large_entry_cache_oldest++;
6013
6014 // we deallocate_pages, including guard pages, outside the lock
6015 SZONE_UNLOCK(szone);
6016 deallocate_pages(szone, (void *)addr, (size_t)adjsize, 0);
6017 return;
6018 } else {
6019 /* fall through to discard an allocation that is not reusable */
6020 }
6021 }
6022 #endif /* LARGE_CACHE */
6023
6024 szone->num_large_objects_in_use--;
6025 szone->num_bytes_in_large_objects -= entry->size;
6026
6027 vm_range_to_deallocate = large_entry_free_no_lock(szone, entry);
6028 } else {
6029 #if DEBUG_MALLOC
6030 large_debug_print(szone);
6031 #endif
6032 szone_error(szone, 1, "pointer being freed was not allocated", ptr, NULL);
6033 SZONE_UNLOCK(szone);
6034 return;
6035 }
6036 SZONE_UNLOCK(szone); // we release the lock asap
6037 CHECK(szone, __PRETTY_FUNCTION__);
6038
6039 // we deallocate_pages, including guard pages, outside the lock
6040 if (vm_range_to_deallocate.address) {
6041 #if DEBUG_MALLOC
6042 // FIXME: large_entry_for_pointer_no_lock() needs the lock held ...
6043 if (large_entry_for_pointer_no_lock(szone, (void *)vm_range_to_deallocate.address)) {
6044 malloc_printf("*** invariant broken: %p still in use num_large_entries=%d\n",
6045 vm_range_to_deallocate.address, szone->num_large_entries);
6046 large_debug_print(szone);
6047 szone_sleep();
6048 }
6049 #endif
6050 deallocate_pages(szone, (void *)vm_range_to_deallocate.address, (size_t)vm_range_to_deallocate.size, 0);
6051 }
6052 }
6053
6054 static INLINE void *
6055 large_try_shrink_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_good_size)
6056 {
6057 size_t shrinkage = old_size - new_good_size;
6058
6059 if (shrinkage) {
6060 SZONE_LOCK(szone);
6061 /* contract existing large entry */
6062 large_entry_t *large_entry = large_entry_for_pointer_no_lock(szone, ptr);
6063 if (!large_entry) {
6064 szone_error(szone, 1, "large entry reallocated is not properly in table", ptr, NULL);
6065 SZONE_UNLOCK(szone);
6066 return ptr;
6067 }
6068
6069 large_entry->address = (vm_address_t)ptr;
6070 large_entry->size = new_good_size;
6071 szone->num_bytes_in_large_objects -= shrinkage;
6072 SZONE_UNLOCK(szone); // we release the lock asap
6073
6074 deallocate_pages(szone, (void *)((uintptr_t)ptr + new_good_size), shrinkage, 0);
6075 }
6076 return ptr;
6077 }
6078
6079 static INLINE int
6080 large_try_realloc_in_place(szone_t *szone, void *ptr, size_t old_size, size_t new_size)
6081 {
6082 vm_address_t addr = (vm_address_t)ptr + old_size;
6083 large_entry_t *large_entry;
6084 kern_return_t err;
6085
6086 SZONE_LOCK(szone);
6087 large_entry = large_entry_for_pointer_no_lock(szone, (void *)addr);
6088 SZONE_UNLOCK(szone);
6089
6090 if (large_entry) { // check if "addr = ptr + old_size" is already spoken for
6091 return 0; // large pointer already exists in table - extension is not going to work
6092 }
6093
6094 new_size = round_page(new_size);
6095 /*
6096 * Ask for allocation at a specific address, and mark as realloc
6097 * to request coalescing with previous realloc'ed extensions.
6098 */
6099 err = vm_allocate(mach_task_self(), &addr, new_size - old_size, VM_MAKE_TAG(VM_MEMORY_REALLOC));
6100 if (err != KERN_SUCCESS) {
6101 return 0;
6102 }
6103
6104 SZONE_LOCK(szone);
6105 /* extend existing large entry */
6106 large_entry = large_entry_for_pointer_no_lock(szone, ptr);
6107 if (!large_entry) {
6108 szone_error(szone, 1, "large entry reallocated is not properly in table", ptr, NULL);
6109 SZONE_UNLOCK(szone);
6110 return 0; // Bail, leaking "addr"
6111 }
6112
6113 large_entry->address = (vm_address_t)ptr;
6114 large_entry->size = new_size;
6115 szone->num_bytes_in_large_objects += new_size - old_size;
6116 SZONE_UNLOCK(szone); // we release the lock asap
6117
6118 return 1;
6119 }
6120
6121 /********************* Zone call backs ************************/
6122 /*
6123 * Mark these NOINLINE to avoid bloating the purgeable zone call backs
6124 */
6125 static NOINLINE void
6126 szone_free(szone_t *szone, void *ptr)
6127 {
6128 region_t tiny_region;
6129 region_t small_region;
6130
6131 #if DEBUG_MALLOC
6132 if (LOG(szone, ptr))
6133 malloc_printf("in szone_free with %p\n", ptr);
6134 #endif
6135 if (!ptr)
6136 return;
6137 /*
6138 * Try to free to a tiny region.
6139 */
6140 if ((uintptr_t)ptr & (TINY_QUANTUM - 1)) {
6141 szone_error(szone, 1, "Non-aligned pointer being freed", ptr, NULL);
6142 return;
6143 }
6144 if ((tiny_region = tiny_region_for_ptr_no_lock(szone, ptr)) != NULL) {
6145 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS) {
6146 szone_error(szone, 1, "Pointer to metadata being freed", ptr, NULL);
6147 return;
6148 }
6149 free_tiny(szone, ptr, tiny_region, 0);
6150 return;
6151 }
6152
6153 /*
6154 * Try to free to a small region.
6155 */
6156 if ((uintptr_t)ptr & (SMALL_QUANTUM - 1)) {
6157 szone_error(szone, 1, "Non-aligned pointer being freed (2)", ptr, NULL);
6158 return;
6159 }
6160 if ((small_region = small_region_for_ptr_no_lock(szone, ptr)) != NULL) {
6161 if (SMALL_META_INDEX_FOR_PTR(ptr) >= NUM_SMALL_BLOCKS) {
6162 szone_error(szone, 1, "Pointer to metadata being freed (2)", ptr, NULL);
6163 return;
6164 }
6165 free_small(szone, ptr, small_region, 0);
6166 return;
6167 }
6168
6169 /* check that it's a legal large allocation */
6170 if ((uintptr_t)ptr & (vm_page_size - 1)) {
6171 szone_error(szone, 1, "non-page-aligned, non-allocated pointer being freed", ptr, NULL);
6172 return;
6173 }
6174 free_large(szone, ptr);
6175 }
6176
6177 static NOINLINE void
6178 szone_free_definite_size(szone_t *szone, void *ptr, size_t size)
6179 {
6180 #if DEBUG_MALLOC
6181 if (LOG(szone, ptr))
6182 malloc_printf("in szone_free_definite_size with %p\n", ptr);
6183
6184 if (0 == size) {
6185 szone_error(szone, 1, "pointer of size zero being freed", ptr, NULL);
6186 return;
6187 }
6188
6189 #endif
6190 if (!ptr)
6191 return;
6192
6193 /*
6194 * Try to free to a tiny region.
6195 */
6196 if ((uintptr_t)ptr & (TINY_QUANTUM - 1)) {
6197 szone_error(szone, 1, "Non-aligned pointer being freed", ptr, NULL);
6198 return;
6199 }
6200 if (size <= (NUM_TINY_SLOTS - 1)*TINY_QUANTUM) {
6201 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS) {
6202 szone_error(szone, 1, "Pointer to metadata being freed", ptr, NULL);
6203 return;
6204 }
6205 free_tiny(szone, ptr, TINY_REGION_FOR_PTR(ptr), size);
6206 return;
6207 }
6208
6209 /*
6210 * Try to free to a small region.
6211 */
6212 if ((uintptr_t)ptr & (SMALL_QUANTUM - 1)) {
6213 szone_error(szone, 1, "Non-aligned pointer being freed (2)", ptr, NULL);
6214 return;
6215 }
6216 if (size <= szone->large_threshold) {
6217 if (SMALL_META_INDEX_FOR_PTR(ptr) >= NUM_SMALL_BLOCKS) {
6218 szone_error(szone, 1, "Pointer to metadata being freed (2)", ptr, NULL);
6219 return;
6220 }
6221 free_small(szone, ptr, SMALL_REGION_FOR_PTR(ptr), size);
6222 return;
6223 }
6224
6225 /* check that it's a legal large allocation */
6226 if ((uintptr_t)ptr & (vm_page_size - 1)) {
6227 szone_error(szone, 1, "non-page-aligned, non-allocated pointer being freed", ptr, NULL);
6228 return;
6229 }
6230 free_large(szone, ptr);
6231 }
6232
6233 static NOINLINE void *
6234 szone_malloc_should_clear(szone_t *szone, size_t size, boolean_t cleared_requested)
6235 {
6236 void *ptr;
6237 msize_t msize;
6238
6239 if (size <= (NUM_TINY_SLOTS - 1)*TINY_QUANTUM) {
6240 // think tiny
6241 msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
6242 if (!msize)
6243 msize = 1;
6244 ptr = tiny_malloc_should_clear(szone, msize, cleared_requested);
6245 } else if (size <= szone->large_threshold) {
6246 // think small
6247 msize = SMALL_MSIZE_FOR_BYTES(size + SMALL_QUANTUM - 1);
6248 if (!msize)
6249 msize = 1;
6250 ptr = small_malloc_should_clear(szone, msize, cleared_requested);
6251 } else {
6252 // large
6253 size_t num_pages = round_page(size) >> vm_page_shift;
6254 if (num_pages == 0) /* Overflowed */
6255 ptr = 0;
6256 else
6257 ptr = large_malloc(szone, num_pages, 0, cleared_requested);
6258 }
6259 #if DEBUG_MALLOC
6260 if (LOG(szone, ptr))
6261 malloc_printf("szone_malloc returned %p\n", ptr);
6262 #endif
6263 /*
6264 * If requested, scribble on allocated memory.
6265 */
6266 if ((szone->debug_flags & SCALABLE_MALLOC_DO_SCRIBBLE) && ptr && !cleared_requested && size)
6267 memset(ptr, 0xaa, size);
6268
6269 return ptr;
6270 }
6271
6272 static NOINLINE void *
6273 szone_malloc(szone_t *szone, size_t size) {
6274 return szone_malloc_should_clear(szone, size, 0);
6275 }
6276
6277 static NOINLINE void *
6278 szone_calloc(szone_t *szone, size_t num_items, size_t size)
6279 {
6280 size_t total_bytes = num_items * size;
6281
6282 // Check for overflow of integer multiplication
6283 if (num_items > 1) {
6284 #if __LP64__ /* size_t is uint64_t */
6285 if ((num_items | size) & 0xffffffff00000000ul) {
6286 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
6287 __uint128_t product = ((__uint128_t)num_items) * ((__uint128_t)size);
6288 if ((uint64_t)(product >> 64)) // compiles to test on upper register of register pair
6289 return NULL;
6290 }
6291 #else /* size_t is uint32_t */
6292 if ((num_items | size) & 0xffff0000ul) {
6293 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
6294 uint64_t product = ((uint64_t)num_items) * ((uint64_t)size);
6295 if ((uint32_t)(product >> 32)) // compiles to test on upper register of register pair
6296 return NULL;
6297 }
6298 #endif
6299 }
6300
6301 return szone_malloc_should_clear(szone, total_bytes, 1);
6302 }
6303
6304 static NOINLINE void *
6305 szone_valloc(szone_t *szone, size_t size)
6306 {
6307 void *ptr;
6308
6309 if (size <= szone->large_threshold) {
6310 ptr = szone_memalign(szone, vm_page_size, size);
6311 } else {
6312 size_t num_pages;
6313
6314 num_pages = round_page(size) >> vm_page_shift;
6315 ptr = large_malloc(szone, num_pages, 0, 0);
6316 }
6317
6318 #if DEBUG_MALLOC
6319 if (LOG(szone, ptr))
6320 malloc_printf("szone_valloc returned %p\n", ptr);
6321 #endif
6322 return ptr;
6323 }
6324
6325 /* Isolate PIC-base load (for __is_threaded) here. */
6326 static NOINLINE size_t
6327 szone_size_try_large(szone_t *szone, const void *ptr)
6328 {
6329 size_t size = 0;
6330 large_entry_t *entry;
6331
6332 SZONE_LOCK(szone);
6333 entry = large_entry_for_pointer_no_lock(szone, ptr);
6334 if (entry) {
6335 size = entry->size;
6336 }
6337 SZONE_UNLOCK(szone);
6338 #if DEBUG_MALLOC
6339 if (LOG(szone, ptr)) {
6340 malloc_printf("szone_size for %p returned %d\n", ptr, (unsigned)size);
6341 }
6342 #endif
6343 return size;
6344 }
6345
6346 static NOINLINE size_t
6347 szone_size(szone_t *szone, const void *ptr)
6348 {
6349 boolean_t is_free;
6350 msize_t msize, msize_and_free;
6351
6352 if (!ptr)
6353 return 0;
6354 #if DEBUG_MALLOC
6355 if (LOG(szone, ptr)) {
6356 malloc_printf("in szone_size for %p (szone=%p)\n", ptr, szone);
6357 }
6358 #endif
6359
6360 /*
6361 * Look for it in a tiny region.
6362 */
6363 if ((uintptr_t)ptr & (TINY_QUANTUM - 1))
6364 return 0;
6365 if (tiny_region_for_ptr_no_lock(szone, ptr)) {
6366 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS)
6367 return 0;
6368 msize = get_tiny_meta_header(ptr, &is_free);
6369 if (is_free)
6370 return 0;
6371 #if TINY_CACHE
6372 {
6373 mag_index_t mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(ptr));
6374 if (DEPOT_MAGAZINE_INDEX != mag_index) {
6375 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
6376
6377 if (msize < TINY_QUANTUM && ptr == (void *)((uintptr_t)(tiny_mag_ptr->mag_last_free) & ~ (TINY_QUANTUM - 1)))
6378 return 0;
6379 } else {
6380 for (mag_index = 0; mag_index < szone->num_tiny_magazines; mag_index++) {
6381 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
6382
6383 if (msize < TINY_QUANTUM && ptr == (void *)((uintptr_t)(tiny_mag_ptr->mag_last_free) & ~ (TINY_QUANTUM - 1)))
6384 return 0;
6385 }
6386 }
6387 }
6388 #endif
6389 return TINY_BYTES_FOR_MSIZE(msize);
6390 }
6391
6392 /*
6393 * Look for it in a small region.
6394 */
6395 if ((uintptr_t)ptr & (SMALL_QUANTUM - 1))
6396 return 0;
6397 if (small_region_for_ptr_no_lock(szone, ptr)) {
6398 if (SMALL_META_INDEX_FOR_PTR(ptr) >= NUM_SMALL_BLOCKS)
6399 return 0;
6400 msize_and_free = *SMALL_METADATA_FOR_PTR(ptr);
6401 if (msize_and_free & SMALL_IS_FREE)
6402 return 0;
6403 #if SMALL_CACHE
6404 {
6405 mag_index_t mag_index = MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(ptr));
6406 if (DEPOT_MAGAZINE_INDEX != mag_index) {
6407 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
6408
6409 if (ptr == (void *)((uintptr_t)(small_mag_ptr->mag_last_free) & ~ (SMALL_QUANTUM - 1)))
6410 return 0;
6411 } else {
6412 for (mag_index = 0; mag_index < szone->num_small_magazines; mag_index++) {
6413 magazine_t *small_mag_ptr = &(szone->small_magazines[mag_index]);
6414
6415 if (ptr == (void *)((uintptr_t)(small_mag_ptr->mag_last_free) & ~ (SMALL_QUANTUM - 1)))
6416 return 0;
6417 }
6418 }
6419 }
6420 #endif
6421 return SMALL_BYTES_FOR_MSIZE(msize_and_free);
6422 }
6423
6424 /*
6425 * If not page-aligned, it cannot have come from a large allocation.
6426 */
6427 if ((uintptr_t)ptr & (vm_page_size - 1))
6428 return 0;
6429
6430 /*
6431 * Look for it in a large entry.
6432 */
6433 return szone_size_try_large(szone, ptr);
6434 }
6435
6436 static NOINLINE void *
6437 szone_realloc(szone_t *szone, void *ptr, size_t new_size)
6438 {
6439 size_t old_size, new_good_size, valid_size;
6440 void *new_ptr;
6441
6442 #if DEBUG_MALLOC
6443 if (LOG(szone, ptr)) {
6444 malloc_printf("in szone_realloc for %p, %d\n", ptr, (unsigned)new_size);
6445 }
6446 #endif
6447 if (NULL == ptr) {
6448 // If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size.
6449 return szone_malloc(szone, new_size);
6450 } else if (0 == new_size) {
6451 // If size is 0 and ptr is not a null pointer, the object pointed to is freed.
6452 szone_free(szone, ptr);
6453 // If size is 0, either a null pointer or a unique pointer that can be successfully passed
6454 // to free() shall be returned.
6455 return szone_malloc(szone, 1);
6456 }
6457
6458 old_size = szone_size(szone, ptr);
6459 if (!old_size) {
6460 szone_error(szone, 1, "pointer being reallocated was not allocated", ptr, NULL);
6461 return NULL;
6462 }
6463
6464 new_good_size = szone_good_size(szone, new_size);
6465 if (new_good_size == old_size) { // Existing allocation is best fit evar?
6466 return ptr;
6467 }
6468
6469 /*
6470 * If the new size suits the tiny allocator and the pointer being resized
6471 * belongs to a tiny region, try to reallocate in-place.
6472 */
6473 if (new_good_size <= (NUM_TINY_SLOTS - 1) * TINY_QUANTUM) {
6474 if (old_size <= (NUM_TINY_SLOTS - 1) * TINY_QUANTUM) {
6475 if (new_good_size <= (old_size >> 1)) {
6476 /*
6477 * Serious shrinkage (more than half). free() the excess.
6478 */
6479 return tiny_try_shrink_in_place(szone, ptr, old_size, new_good_size);
6480 } else if (new_good_size <= old_size) {
6481 /*
6482 * new_good_size smaller than old_size but not by much (less than half).
6483 * Avoid thrashing at the expense of some wasted storage.
6484 */
6485 return ptr;
6486 } else if (tiny_try_realloc_in_place(szone, ptr, old_size, new_good_size)) { // try to grow the allocation
6487 return ptr;
6488 }
6489 }
6490
6491 /*
6492 * Else if the new size suits the small allocator and the pointer being resized
6493 * belongs to a small region, and we're not protecting the small allocations
6494 * try to reallocate in-place.
6495 */
6496 } else if (new_good_size <= szone->large_threshold) {
6497 if ((NUM_TINY_SLOTS - 1) * TINY_QUANTUM < old_size && old_size <= szone->large_threshold) {
6498 if (new_good_size <= (old_size >> 1)) {
6499 return small_try_shrink_in_place(szone, ptr, old_size, new_good_size);
6500 } else if (new_good_size <= old_size) {
6501 return ptr;
6502 } else if (small_try_realloc_in_place(szone, ptr, old_size, new_good_size)) {
6503 return ptr;
6504 }
6505 }
6506 /*
6507 * Else if the allocation's a large allocation, try to reallocate in-place there.
6508 */
6509 } else if (!(szone->debug_flags & SCALABLE_MALLOC_PURGEABLE) && // purgeable needs fresh allocation
6510 (old_size > szone->large_threshold) &&
6511 (new_good_size > szone->large_threshold)) {
6512 if (new_good_size <= (old_size >> 1)) {
6513 return large_try_shrink_in_place(szone, ptr, old_size, new_good_size);
6514 } else if (new_good_size <= old_size) {
6515 return ptr;
6516 } else if (large_try_realloc_in_place(szone, ptr, old_size, new_good_size)) {
6517 return ptr;
6518 }
6519 }
6520
6521 /*
6522 * Can't reallocate in place for whatever reason; allocate a new buffer and copy.
6523 */
6524 if (new_good_size <= (old_size >> 1)) {
6525 /* Serious shrinkage (more than half). FALL THROUGH to alloc/copy/free. */
6526 } else if (new_good_size <= old_size) {
6527 return ptr;
6528 }
6529
6530 new_ptr = szone_malloc(szone, new_size);
6531 if (new_ptr == NULL)
6532 return NULL;
6533
6534 /*
6535 * If the allocation's large enough, try to copy using VM. If that fails, or
6536 * if it's too small, just copy by hand.
6537 */
6538 valid_size = MIN(old_size, new_size);
6539 if ((valid_size < szone->vm_copy_threshold) ||
6540 vm_copy(mach_task_self(), (vm_address_t)ptr, valid_size, (vm_address_t)new_ptr))
6541 memcpy(new_ptr, ptr, valid_size);
6542 szone_free(szone, ptr);
6543
6544 #if DEBUG_MALLOC
6545 if (LOG(szone, ptr)) {
6546 malloc_printf("szone_realloc returned %p for %d\n", new_ptr, (unsigned)new_size);
6547 }
6548 #endif
6549 return new_ptr;
6550 }
6551
6552 static NOINLINE void *
6553 szone_memalign(szone_t *szone, size_t alignment, size_t size)
6554 {
6555 if ((size + alignment) < size) // size_t arithmetic wrapped!
6556 return NULL;
6557
6558 // alignment is gauranteed a power of 2 at least as large as sizeof(void *), hence non-zero.
6559 // Since size + alignment didn't wrap, 0 <= size + alignment - 1 < size + alignment
6560 size_t span = size + alignment - 1;
6561
6562 if (alignment <= TINY_QUANTUM) {
6563 return szone_malloc(szone, size); // Trivially satisfied by tiny, small, or large
6564
6565 } else if (span <= (NUM_TINY_SLOTS - 1)*TINY_QUANTUM) {
6566 msize_t mspan = TINY_MSIZE_FOR_BYTES(span + TINY_QUANTUM - 1);
6567 void *p = szone_malloc(szone, span); // avoids inlining tiny_malloc_should_clear(szone, mspan, 0);
6568
6569 if (NULL == p)
6570 return NULL;
6571
6572 size_t offset = ((uintptr_t) p) & (alignment - 1); // p % alignment
6573 size_t pad = (0 == offset) ? 0 : alignment - offset; // p + pad achieves desired alignment
6574
6575 msize_t msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
6576 msize_t mpad = TINY_MSIZE_FOR_BYTES(pad + TINY_QUANTUM - 1);
6577 msize_t mwaste = mspan - msize - mpad; // excess blocks
6578
6579 if (mpad > 0) {
6580 void *q = (void *)(((uintptr_t) p) + pad);
6581
6582 // Mark q as a block header and in-use, thus creating two blocks.
6583 magazine_t *tiny_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->tiny_magazines,
6584 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p)),
6585 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p)));
6586 set_tiny_meta_header_in_use(q, msize);
6587 tiny_mag_ptr->mag_num_objects++;
6588
6589 // set_tiny_meta_header_in_use() "reaffirms" the block_header on the *following* block, so
6590 // now set its in_use bit as well. But only if its within the original allocation made above.
6591 if (mwaste > 0)
6592 BITARRAY_SET(TINY_INUSE_FOR_HEADER(TINY_BLOCK_HEADER_FOR_PTR(q)), TINY_INDEX_FOR_PTR(q) + msize);
6593 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6594
6595 // Give up mpad blocks beginning at p to the tiny free list
6596 // region_t r = TINY_REGION_FOR_PTR(p);
6597 szone_free(szone, p); // avoids inlining free_tiny(szone, p, &r);
6598
6599 p = q; // advance p to the desired alignment
6600 }
6601
6602 if (mwaste > 0) {
6603 void *q = (void *)(((uintptr_t) p) + TINY_BYTES_FOR_MSIZE(msize));
6604 // Mark q as block header and in-use, thus creating two blocks.
6605 magazine_t *tiny_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->tiny_magazines,
6606 REGION_TRAILER_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p)),
6607 MAGAZINE_INDEX_FOR_TINY_REGION(TINY_REGION_FOR_PTR(p)));
6608 set_tiny_meta_header_in_use(q, mwaste);
6609 tiny_mag_ptr->mag_num_objects++;
6610 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6611
6612 // Give up mwaste blocks beginning at q to the tiny free list
6613 // region_t r = TINY_REGION_FOR_PTR(q);
6614 szone_free(szone, q); // avoids inlining free_tiny(szone, q, &r);
6615 }
6616
6617 return p; // p has the desired size and alignment, and can later be free()'d
6618
6619 } else if ((NUM_TINY_SLOTS - 1)*TINY_QUANTUM < size && alignment <= SMALL_QUANTUM) {
6620 return szone_malloc(szone, size); // Trivially satisfied by small or large
6621
6622 } else if (span <= szone->large_threshold) {
6623
6624 if (size <= (NUM_TINY_SLOTS - 1)*TINY_QUANTUM) {
6625 size = (NUM_TINY_SLOTS - 1)*TINY_QUANTUM + TINY_QUANTUM; // ensure block allocated by small does not have a tiny-possible size
6626 span = size + alignment - 1;
6627 }
6628
6629 msize_t mspan = SMALL_MSIZE_FOR_BYTES(span + SMALL_QUANTUM - 1);
6630 void *p = szone_malloc(szone, span); // avoid inlining small_malloc_should_clear(szone, mspan, 0);
6631
6632 if (NULL == p)
6633 return NULL;
6634
6635 size_t offset = ((uintptr_t) p) & (alignment - 1); // p % alignment
6636 size_t pad = (0 == offset) ? 0 : alignment - offset; // p + pad achieves desired alignment
6637
6638 msize_t msize = SMALL_MSIZE_FOR_BYTES(size + SMALL_QUANTUM - 1);
6639 msize_t mpad = SMALL_MSIZE_FOR_BYTES(pad + SMALL_QUANTUM - 1);
6640 msize_t mwaste = mspan - msize - mpad; // excess blocks
6641
6642 if (mpad > 0) {
6643 void *q = (void *)(((uintptr_t) p) + pad);
6644
6645 // Mark q as block header and in-use, thus creating two blocks.
6646 magazine_t *small_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->small_magazines,
6647 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p)),
6648 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p)));
6649 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p), SMALL_META_INDEX_FOR_PTR(p), mpad);
6650 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q), SMALL_META_INDEX_FOR_PTR(q), msize + mwaste);
6651 small_mag_ptr->mag_num_objects++;
6652 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
6653
6654 // Give up mpad blocks beginning at p to the small free list
6655 // region_t r = SMALL_REGION_FOR_PTR(p);
6656 szone_free(szone, p); // avoid inlining free_small(szone, p, &r);
6657
6658 p = q; // advance p to the desired alignment
6659 }
6660 if (mwaste > 0) {
6661 void *q = (void *)(((uintptr_t) p) + SMALL_BYTES_FOR_MSIZE(msize));
6662 // Mark q as block header and in-use, thus creating two blocks.
6663 magazine_t *small_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->small_magazines,
6664 REGION_TRAILER_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p)),
6665 MAGAZINE_INDEX_FOR_SMALL_REGION(SMALL_REGION_FOR_PTR(p)));
6666 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(p), SMALL_META_INDEX_FOR_PTR(p), msize);
6667 small_meta_header_set_in_use(SMALL_META_HEADER_FOR_PTR(q), SMALL_META_INDEX_FOR_PTR(q), mwaste);
6668 small_mag_ptr->mag_num_objects++;
6669 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
6670
6671 // Give up mwaste blocks beginning at q to the small free list
6672 // region_t r = SMALL_REGION_FOR_PTR(q);
6673 szone_free(szone, q); // avoid inlining free_small(szone, q, &r);
6674 }
6675
6676 return p; // p has the desired size and alignment, and can later be free()'d
6677
6678 } else if (szone->large_threshold < size && alignment <= vm_page_size) {
6679 return szone_malloc(szone, size); // Trivially satisfied by large
6680
6681 } else {
6682 // ensure block allocated by large does not have a small-possible size
6683 size_t num_pages = round_page(MAX(szone->large_threshold + 1, size)) >> vm_page_shift;
6684 void *p;
6685
6686 if (num_pages == 0) /* Overflowed */
6687 p = NULL;
6688 else
6689 p = large_malloc(szone, num_pages, MAX(vm_page_shift, __builtin_ctz(alignment)), 0);
6690
6691 return p;
6692 }
6693 /* NOTREACHED */
6694 }
6695
6696 // given a size, returns the number of pointers allocated capable of holding
6697 // that size, up to the limit specified by the 'count' argument. These pointers
6698 // are stored in the 'results' array, which must be allocated by the caller.
6699 // may return zero, since this function is only a best attempt at allocating
6700 // the pointers. clients should be prepared to call malloc for any additional
6701 // blocks they need.
6702 static NOINLINE unsigned
6703 szone_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count)
6704 {
6705 msize_t msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
6706 unsigned found = 0;
6707 mag_index_t mag_index = mag_get_thread_index(szone);
6708 magazine_t *tiny_mag_ptr = &(szone->tiny_magazines[mag_index]);
6709
6710 // only bother implementing this for tiny
6711 if (size > (NUM_TINY_SLOTS - 1)*TINY_QUANTUM)
6712 return 0;
6713 // make sure to return objects at least one quantum in size
6714 if (!msize)
6715 msize = 1;
6716
6717 CHECK(szone, __PRETTY_FUNCTION__);
6718
6719 // We must lock the zone now, since tiny_malloc_from_free_list assumes that
6720 // the caller has done so.
6721 SZONE_MAGAZINE_PTR_LOCK(szone, tiny_mag_ptr);
6722
6723 // with the zone locked, allocate objects from the free list until all
6724 // sufficiently large objects have been exhausted, or we have met our quota
6725 // of objects to allocate.
6726 while (found < count) {
6727 void *ptr = tiny_malloc_from_free_list(szone, tiny_mag_ptr, mag_index, msize);
6728 if (!ptr)
6729 break;
6730
6731 *results++ = ptr;
6732 found++;
6733 }
6734 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6735 return found;
6736 }
6737
6738 /* Try caching the tiny_region and checking if the next ptr hits there. */
6739 static NOINLINE void
6740 szone_batch_free(szone_t *szone, void **to_be_freed, unsigned count)
6741 {
6742 unsigned cc = 0;
6743 void *ptr;
6744 region_t tiny_region = NULL;
6745 boolean_t is_free;
6746 msize_t msize;
6747 magazine_t *tiny_mag_ptr = NULL;
6748 mag_index_t mag_index = -1;
6749
6750 // frees all the pointers in to_be_freed
6751 // note that to_be_freed may be overwritten during the process
6752 if (!count)
6753 return;
6754
6755 CHECK(szone, __PRETTY_FUNCTION__);
6756 while (cc < count) {
6757 ptr = to_be_freed[cc];
6758 if (ptr) {
6759 if (NULL == tiny_region || tiny_region != TINY_REGION_FOR_PTR(ptr)) { // region same as last iteration?
6760 if (tiny_mag_ptr) { // non-NULL iff magazine lock taken
6761 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6762 tiny_mag_ptr = NULL;
6763 }
6764
6765 tiny_region = tiny_region_for_ptr_no_lock(szone, ptr);
6766
6767 if (tiny_region) {
6768 tiny_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->tiny_magazines,
6769 REGION_TRAILER_FOR_TINY_REGION(tiny_region),
6770 MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region));
6771 mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(tiny_region);
6772 }
6773 }
6774 if (tiny_region) {
6775 // this is a tiny pointer
6776 if (TINY_INDEX_FOR_PTR(ptr) >= NUM_TINY_BLOCKS)
6777 break; // pointer to metadata; let the standard free deal with it
6778 msize = get_tiny_meta_header(ptr, &is_free);
6779 if (is_free)
6780 break; // a double free; let the standard free deal with it
6781
6782 if (!tiny_free_no_lock(szone, tiny_mag_ptr, mag_index, tiny_region, ptr, msize)) {
6783 // Arrange to re-acquire magazine lock
6784 tiny_mag_ptr = NULL;
6785 tiny_region = NULL;
6786 }
6787 to_be_freed[cc] = NULL;
6788 } else {
6789 // No region in this zone claims ptr; let the standard free deal with it
6790 break;
6791 }
6792 }
6793 cc++;
6794 }
6795
6796 if (tiny_mag_ptr) {
6797 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6798 tiny_mag_ptr = NULL;
6799 }
6800
6801 CHECK(szone, __PRETTY_FUNCTION__);
6802 while (count--) {
6803 ptr = to_be_freed[count];
6804 if (ptr)
6805 szone_free(szone, ptr);
6806 }
6807 }
6808
6809 // FIXME: Suppose one of the locks is held?
6810 static void
6811 szone_destroy(szone_t *szone)
6812 {
6813 size_t index;
6814 large_entry_t *large;
6815 vm_range_t range_to_deallocate;
6816
6817 #if LARGE_CACHE
6818 SZONE_LOCK(szone);
6819
6820 /* disable any memory pressure responder */
6821 szone->flotsam_enabled = FALSE;
6822
6823 // stack allocated copy of the death-row cache
6824 int idx = szone->large_entry_cache_oldest, idx_max = szone->large_entry_cache_newest;
6825 large_entry_t local_entry_cache[LARGE_ENTRY_CACHE_SIZE];
6826
6827 memcpy((void *)local_entry_cache, (void *)szone->large_entry_cache, sizeof(local_entry_cache));
6828
6829 szone->large_entry_cache_oldest = szone->large_entry_cache_newest = 0;
6830 szone->large_entry_cache[0].address = 0x0;
6831 szone->large_entry_cache[0].size = 0;
6832 szone->large_entry_cache_bytes = 0;
6833 szone->large_entry_cache_reserve_bytes = 0;
6834
6835 SZONE_UNLOCK(szone);
6836
6837 // deallocate the death-row cache outside the zone lock
6838 while (idx != idx_max) {
6839 deallocate_pages(szone, (void *) local_entry_cache[idx].address, local_entry_cache[idx].size, 0);
6840 if (++idx == LARGE_ENTRY_CACHE_SIZE) idx = 0;
6841 }
6842 if (0 != local_entry_cache[idx].address && 0 != local_entry_cache[idx].size) {
6843 deallocate_pages(szone, (void *) local_entry_cache[idx].address, local_entry_cache[idx].size, 0);
6844 }
6845 #endif
6846
6847 /* destroy large entries */
6848 index = szone->num_large_entries;
6849 while (index--) {
6850 large = szone->large_entries + index;
6851 if (large->address) {
6852 // we deallocate_pages, including guard pages
6853 deallocate_pages(szone, (void *)(large->address), large->size, szone->debug_flags);
6854 }
6855 }
6856 large_entries_free_no_lock(szone, szone->large_entries, szone->num_large_entries, &range_to_deallocate);
6857 if (range_to_deallocate.size)
6858 deallocate_pages(szone, (void *)range_to_deallocate.address, (size_t)range_to_deallocate.size, 0);
6859
6860 /* destroy tiny regions */
6861 for (index = 0; index < szone->tiny_region_generation->num_regions_allocated; ++index)
6862 if ((HASHRING_OPEN_ENTRY != szone->tiny_region_generation->hashed_regions[index]) &&
6863 (HASHRING_REGION_DEALLOCATED != szone->tiny_region_generation->hashed_regions[index]))
6864 deallocate_pages(szone, szone->tiny_region_generation->hashed_regions[index], TINY_REGION_SIZE, 0);
6865
6866 /* destroy small regions */
6867 for (index = 0; index < szone->small_region_generation->num_regions_allocated; ++index)
6868 if ((HASHRING_OPEN_ENTRY != szone->small_region_generation->hashed_regions[index]) &&
6869 (HASHRING_REGION_DEALLOCATED != szone->small_region_generation->hashed_regions[index]))
6870 deallocate_pages(szone, szone->small_region_generation->hashed_regions[index], SMALL_REGION_SIZE, 0);
6871
6872 /* destroy region hash rings, if any */
6873 if (szone->tiny_region_generation->hashed_regions != szone->initial_tiny_regions) {
6874 size_t size = round_page(szone->tiny_region_generation->num_regions_allocated * sizeof(region_t));
6875 deallocate_pages(szone, szone->tiny_region_generation->hashed_regions, size, 0);
6876 }
6877 if (szone->small_region_generation->hashed_regions != szone->initial_small_regions) {
6878 size_t size = round_page(szone->small_region_generation->num_regions_allocated * sizeof(region_t));
6879 deallocate_pages(szone, szone->small_region_generation->hashed_regions, size, 0);
6880 }
6881
6882 /* Now destroy the separate szone region */
6883 if (szone->cpu_id_key != (pthread_key_t) -1)
6884 (void)pthread_key_delete(szone->cpu_id_key);
6885 deallocate_pages(szone, (void *)&(szone->tiny_magazines[-1]), TINY_MAGAZINE_PAGED_SIZE, SCALABLE_MALLOC_ADD_GUARD_PAGES);
6886 deallocate_pages(szone, (void *)&(szone->small_magazines[-1]), SMALL_MAGAZINE_PAGED_SIZE, SCALABLE_MALLOC_ADD_GUARD_PAGES);
6887 deallocate_pages(szone, (void *)szone, SZONE_PAGED_SIZE, 0);
6888 }
6889
6890 static NOINLINE size_t
6891 szone_good_size(szone_t *szone, size_t size)
6892 {
6893 msize_t msize;
6894
6895 // Find a good size for this tiny allocation.
6896 if (size <= (NUM_TINY_SLOTS - 1) * TINY_QUANTUM) {
6897 msize = TINY_MSIZE_FOR_BYTES(size + TINY_QUANTUM - 1);
6898 if (!msize)
6899 msize = 1;
6900 return TINY_BYTES_FOR_MSIZE(msize);
6901 }
6902
6903 // Find a good size for this small allocation.
6904 if (size <= szone->large_threshold) {
6905 msize = SMALL_MSIZE_FOR_BYTES(size + SMALL_QUANTUM - 1);
6906 if (!msize)
6907 msize = 1;
6908 return SMALL_BYTES_FOR_MSIZE(msize);
6909 }
6910
6911 // Check for integer overflow on the size, since unlike the two cases above,
6912 // there is no upper bound on allocation size at this point.
6913 if (size > round_page(size))
6914 return (size_t)(-1LL);
6915
6916 #if DEBUG_MALLOC
6917 // It is not acceptable to see a size of zero here, since that means we
6918 // failed to catch a request for zero bytes in the tiny check, or the size
6919 // overflowed to zero during some arithmetic.
6920 if (size == 0)
6921 malloc_printf("szone_good_size() invariant broken %y\n", size);
6922 #endif
6923 return round_page(size);
6924 }
6925
6926 unsigned szone_check_counter = 0;
6927 unsigned szone_check_start = 0;
6928 unsigned szone_check_modulo = 1;
6929
6930 static NOINLINE boolean_t
6931 szone_check_all(szone_t *szone, const char *function)
6932 {
6933 size_t index;
6934
6935 /* check tiny regions - chould check region count */
6936 for (index = 0; index < szone->tiny_region_generation->num_regions_allocated; ++index) {
6937 region_t tiny = szone->tiny_region_generation->hashed_regions[index];
6938
6939 if (HASHRING_REGION_DEALLOCATED == tiny)
6940 continue;
6941
6942 if (tiny) {
6943 magazine_t *tiny_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->tiny_magazines,
6944 REGION_TRAILER_FOR_TINY_REGION(tiny), MAGAZINE_INDEX_FOR_TINY_REGION(tiny));
6945
6946 if (!tiny_check_region(szone, tiny)) {
6947 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6948 szone->debug_flags &= ~ CHECK_REGIONS;
6949 szone_error(szone, 1, "check: tiny region incorrect", NULL,
6950 "*** tiny region %ld incorrect szone_check_all(%s) counter=%d\n",
6951 index, function, szone_check_counter);
6952 return 0;
6953 }
6954 SZONE_MAGAZINE_PTR_UNLOCK(szone, tiny_mag_ptr);
6955 }
6956 }
6957 /* check tiny free lists */
6958 for (index = 0; index < NUM_TINY_SLOTS; ++index) {
6959 if (!tiny_free_list_check(szone, index)) {
6960 szone->debug_flags &= ~ CHECK_REGIONS;
6961 szone_error(szone, 1, "check: tiny free list incorrect", NULL,
6962 "*** tiny free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6963 index, function, szone_check_counter);
6964 return 0;
6965 }
6966 }
6967
6968 /* check small regions - could check region count */
6969 for (index = 0; index < szone->small_region_generation->num_regions_allocated; ++index) {
6970 region_t small = szone->small_region_generation->hashed_regions[index];
6971
6972 if (HASHRING_REGION_DEALLOCATED == small)
6973 continue;
6974
6975 if (small) {
6976 magazine_t *small_mag_ptr = mag_lock_zine_for_region_trailer(szone, szone->small_magazines,
6977 REGION_TRAILER_FOR_SMALL_REGION(small), MAGAZINE_INDEX_FOR_SMALL_REGION(small));
6978
6979 if (!small_check_region(szone, small)) {
6980 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
6981 szone->debug_flags &= ~ CHECK_REGIONS;
6982 szone_error(szone, 1, "check: small region incorrect", NULL,
6983 "*** small region %ld incorrect szone_check_all(%s) counter=%d\n",
6984 index, function, szone_check_counter);
6985 return 0;
6986 }
6987 SZONE_MAGAZINE_PTR_UNLOCK(szone, small_mag_ptr);
6988 }
6989 }
6990 /* check small free lists */
6991 for (index = 0; index < szone->num_small_slots; ++index) {
6992 if (!small_free_list_check(szone, index)) {
6993 szone->debug_flags &= ~ CHECK_REGIONS;
6994 szone_error(szone, 1, "check: small free list incorrect", NULL,
6995 "*** small free list incorrect (slot=%ld) szone_check_all(%s) counter=%d\n",
6996 index, function, szone_check_counter);
6997 return 0;
6998 }
6999 }
7000
7001 return 1;
7002 }
7003
7004 static boolean_t
7005 szone_check(szone_t *szone)
7006 {
7007 if ((++szone_check_counter % 10000) == 0)
7008 _malloc_printf(ASL_LEVEL_NOTICE, "at szone_check counter=%d\n", szone_check_counter);
7009
7010 if (szone_check_counter < szone_check_start)
7011 return 1;
7012
7013 if (szone_check_counter % szone_check_modulo)
7014 return 1;
7015
7016 return szone_check_all(szone, "");
7017 }
7018
7019 static kern_return_t
7020 szone_ptr_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t zone_address,
7021 memory_reader_t reader, vm_range_recorder_t recorder)
7022 {
7023 szone_t *szone;
7024 kern_return_t err;
7025
7026 if (!reader) reader = _szone_default_reader;
7027
7028 err = reader(task, zone_address, sizeof(szone_t), (void **)&szone);
7029 if (err) return err;
7030
7031 err = tiny_in_use_enumerator(task, context, type_mask, szone, reader, recorder);
7032 if (err) return err;
7033
7034 err = small_in_use_enumerator(task, context, type_mask, szone, reader, recorder);
7035 if (err) return err;
7036
7037 err = large_in_use_enumerator(task, context, type_mask,
7038 (vm_address_t)szone->large_entries, szone->num_large_entries, reader, recorder);
7039 return err;
7040 }
7041
7042 // Following method is deprecated: use scalable_zone_statistics instead
7043 void
7044 scalable_zone_info(malloc_zone_t *zone, unsigned *info_to_fill, unsigned count)
7045 {
7046 szone_t *szone = (void *)zone;
7047 unsigned info[13];
7048
7049 // We do not lock to facilitate debug
7050
7051 size_t s = 0;
7052 unsigned t = 0;
7053 size_t u = 0;
7054 mag_index_t mag_index;
7055
7056 for (mag_index = -1; mag_index < szone->num_tiny_magazines; mag_index++) {
7057 s += szone->tiny_magazines[mag_index].mag_bytes_free_at_start;
7058 s += szone->tiny_magazines[mag_index].mag_bytes_free_at_end;
7059 t += szone->tiny_magazines[mag_index].mag_num_objects;
7060 u += szone->tiny_magazines[mag_index].mag_num_bytes_in_objects;
7061 }
7062
7063 info[4] = t;
7064 info[5] = u;
7065
7066 for (t = 0, u = 0, mag_index = -1; mag_index < szone->num_small_magazines; mag_index++) {
7067 s += szone->small_magazines[mag_index].mag_bytes_free_at_start;
7068 s += szone->small_magazines[mag_index].mag_bytes_free_at_end;
7069 t += szone->small_magazines[mag_index].mag_num_objects;
7070 u += szone->small_magazines[mag_index].mag_num_bytes_in_objects;
7071 }
7072
7073 info[6] = t;
7074 info[7] = u;
7075
7076 info[8] = szone->num_large_objects_in_use;
7077 info[9] = szone->num_bytes_in_large_objects;
7078
7079 info[10] = 0; // DEPRECATED szone->num_huge_entries;
7080 info[11] = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7081
7082 info[12] = szone->debug_flags;
7083
7084 info[0] = info[4] + info[6] + info[8] + info[10];
7085 info[1] = info[5] + info[7] + info[9] + info[11];
7086
7087 info[3] = (szone->num_tiny_regions - szone->num_tiny_regions_dealloc) * TINY_REGION_SIZE +
7088 (szone->num_small_regions - szone->num_small_regions_dealloc) * SMALL_REGION_SIZE + info[9] + info[11];
7089
7090 info[2] = info[3] - s;
7091 memcpy(info_to_fill, info, sizeof(unsigned)*count);
7092 }
7093
7094 // FIXME: consistent picture requires locking!
7095 static NOINLINE void
7096 szone_print(szone_t *szone, boolean_t verbose)
7097 {
7098 unsigned info[13];
7099 size_t index;
7100 region_t region;
7101
7102 scalable_zone_info((void *)szone, info, 13);
7103 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7104 "Scalable zone %p: inUse=%u(%y) touched=%y allocated=%y flags=%d\n",
7105 szone, info[0], info[1], info[2], info[3], info[12]);
7106 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7107 "\ttiny=%u(%y) small=%u(%y) large=%u(%y) huge=%u(%y)\n",
7108 info[4], info[5], info[6], info[7], info[8], info[9], info[10], info[11]);
7109 // tiny
7110 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7111 "%lu tiny regions:\n", szone->num_tiny_regions);
7112 if (szone->num_tiny_regions_dealloc)
7113 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7114 "[%lu tiny regions have been vm_deallocate'd]\n", szone->num_tiny_regions_dealloc);
7115 for (index = 0; index < szone->tiny_region_generation->num_regions_allocated; ++index) {
7116 region = szone->tiny_region_generation->hashed_regions[index];
7117 if (HASHRING_OPEN_ENTRY != region && HASHRING_REGION_DEALLOCATED != region) {
7118 mag_index_t mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(region);
7119 print_tiny_region(verbose, region,
7120 (region == szone->tiny_magazines[mag_index].mag_last_region) ?
7121 szone->tiny_magazines[mag_index].mag_bytes_free_at_start : 0,
7122 (region == szone->tiny_magazines[mag_index].mag_last_region) ?
7123 szone->tiny_magazines[mag_index].mag_bytes_free_at_end : 0);
7124 }
7125 }
7126 if (verbose)
7127 print_tiny_free_list(szone);
7128 // small
7129 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7130 "%lu small regions:\n", szone->num_small_regions);
7131 if (szone->num_small_regions_dealloc)
7132 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7133 "[%lu small regions have been vm_deallocate'd]\n", szone->num_small_regions_dealloc);
7134 for (index = 0; index < szone->small_region_generation->num_regions_allocated; ++index) {
7135 region = szone->small_region_generation->hashed_regions[index];
7136 if (HASHRING_OPEN_ENTRY != region && HASHRING_REGION_DEALLOCATED != region) {
7137 mag_index_t mag_index = MAGAZINE_INDEX_FOR_SMALL_REGION(region);
7138 print_small_region(szone, verbose, region,
7139 (region == szone->small_magazines[mag_index].mag_last_region) ?
7140 szone->small_magazines[mag_index].mag_bytes_free_at_start : 0,
7141 (region == szone->small_magazines[mag_index].mag_last_region) ?
7142 szone->small_magazines[mag_index].mag_bytes_free_at_end : 0);
7143 }
7144 }
7145 if (verbose)
7146 print_small_free_list(szone);
7147 }
7148
7149 static void
7150 szone_log(malloc_zone_t *zone, void *log_address)
7151 {
7152 szone_t *szone = (szone_t *)zone;
7153
7154 szone->log_address = log_address;
7155 }
7156
7157 static void
7158 szone_force_lock(szone_t *szone)
7159 {
7160 mag_index_t i;
7161
7162 for (i = 0; i < szone->num_tiny_magazines; ++i) {
7163 SZONE_MAGAZINE_PTR_LOCK(szone, (&(szone->tiny_magazines[i])));
7164 }
7165 SZONE_MAGAZINE_PTR_LOCK(szone, (&(szone->tiny_magazines[DEPOT_MAGAZINE_INDEX])));
7166
7167 for (i = 0; i < szone->num_small_magazines; ++i) {
7168 SZONE_MAGAZINE_PTR_LOCK(szone, (&(szone->small_magazines[i])));
7169 }
7170 SZONE_MAGAZINE_PTR_LOCK(szone, (&(szone->small_magazines[DEPOT_MAGAZINE_INDEX])));
7171
7172 SZONE_LOCK(szone);
7173 }
7174
7175 static void
7176 szone_force_unlock(szone_t *szone)
7177 {
7178 mag_index_t i;
7179
7180 SZONE_UNLOCK(szone);
7181
7182 for (i = -1; i < szone->num_small_magazines; ++i) {
7183 SZONE_MAGAZINE_PTR_UNLOCK(szone, (&(szone->small_magazines[i])));
7184 }
7185
7186 for (i = -1; i < szone->num_tiny_magazines; ++i) {
7187 SZONE_MAGAZINE_PTR_UNLOCK(szone, (&(szone->tiny_magazines[i])));
7188 }
7189 }
7190
7191 static boolean_t
7192 szone_locked(szone_t *szone)
7193 {
7194 mag_index_t i;
7195 int tookLock;
7196
7197 tookLock = SZONE_TRY_LOCK(szone);
7198 if (tookLock == 0)
7199 return 1;
7200 SZONE_UNLOCK(szone);
7201
7202 for (i = -1; i < szone->num_small_magazines; ++i) {
7203 tookLock = SZONE_MAGAZINE_PTR_TRY_LOCK(szone, (&(szone->small_magazines[i])));
7204 if (tookLock == 0)
7205 return 1;
7206 SZONE_MAGAZINE_PTR_UNLOCK(szone, (&(szone->small_magazines[i])));
7207 }
7208
7209 for (i = -1; i < szone->num_tiny_magazines; ++i) {
7210 tookLock = SZONE_MAGAZINE_PTR_TRY_LOCK(szone, (&(szone->tiny_magazines[i])));
7211 if (tookLock == 0)
7212 return 1;
7213 SZONE_MAGAZINE_PTR_UNLOCK(szone, (&(szone->tiny_magazines[i])));
7214 }
7215 return 0;
7216 }
7217
7218 static size_t
7219 szone_pressure_relief(szone_t *szone, size_t goal)
7220 {
7221 #if LARGE_CACHE
7222 if (!szone->flotsam_enabled)
7223 return 0;
7224
7225 SZONE_LOCK(szone);
7226
7227 // stack allocated copy of the death-row cache
7228 int idx = szone->large_entry_cache_oldest, idx_max = szone->large_entry_cache_newest;
7229 large_entry_t local_entry_cache[LARGE_ENTRY_CACHE_SIZE];
7230
7231 memcpy((void *)local_entry_cache, (void *)szone->large_entry_cache, sizeof(local_entry_cache));
7232
7233 szone->large_entry_cache_oldest = szone->large_entry_cache_newest = 0;
7234 szone->large_entry_cache[0].address = 0x0;
7235 szone->large_entry_cache[0].size = 0;
7236 szone->large_entry_cache_bytes = 0;
7237 szone->large_entry_cache_reserve_bytes = 0;
7238
7239 szone->flotsam_enabled = FALSE;
7240
7241 SZONE_UNLOCK(szone);
7242
7243 // deallocate the death-row cache outside the zone lock
7244 size_t total = 0;
7245 while (idx != idx_max) {
7246 deallocate_pages(szone, (void *) local_entry_cache[idx].address, local_entry_cache[idx].size, 0);
7247 total += local_entry_cache[idx].size;
7248 if (++idx == LARGE_ENTRY_CACHE_SIZE) idx = 0;
7249 }
7250 if (0 != local_entry_cache[idx].address && 0 != local_entry_cache[idx].size) {
7251 deallocate_pages(szone, (void *) local_entry_cache[idx].address, local_entry_cache[idx].size, 0);
7252 total += local_entry_cache[idx].size;
7253 }
7254 MAGMALLOC_PRESSURERELIEF((void *)szone, goal, total); // DTrace USDT Probe
7255 return total;
7256 #else
7257 return 0;
7258 #endif
7259 }
7260
7261 boolean_t
7262 scalable_zone_statistics(malloc_zone_t *zone, malloc_statistics_t *stats, unsigned subzone)
7263 {
7264 szone_t *szone = (szone_t *)zone;
7265
7266 switch (subzone) {
7267 case 0:
7268 {
7269 size_t s = 0;
7270 unsigned t = 0;
7271 size_t u = 0;
7272 mag_index_t mag_index;
7273
7274 for (mag_index = -1; mag_index < szone->num_tiny_magazines; mag_index++) {
7275 s += szone->tiny_magazines[mag_index].mag_bytes_free_at_start;
7276 s += szone->tiny_magazines[mag_index].mag_bytes_free_at_end;
7277 t += szone->tiny_magazines[mag_index].mag_num_objects;
7278 u += szone->tiny_magazines[mag_index].mag_num_bytes_in_objects;
7279 }
7280
7281 stats->blocks_in_use = t;
7282 stats->size_in_use = u;
7283 stats->size_allocated = (szone->num_tiny_regions - szone->num_tiny_regions_dealloc) * TINY_REGION_SIZE;
7284 stats->max_size_in_use = stats->size_allocated - s;
7285 return 1;
7286 }
7287 case 1:
7288 {
7289 size_t s = 0;
7290 unsigned t = 0;
7291 size_t u = 0;
7292 mag_index_t mag_index;
7293
7294 for (mag_index = -1; mag_index < szone->num_small_magazines; mag_index++) {
7295 s += szone->small_magazines[mag_index].mag_bytes_free_at_start;
7296 s += szone->small_magazines[mag_index].mag_bytes_free_at_end;
7297 t += szone->small_magazines[mag_index].mag_num_objects;
7298 u += szone->small_magazines[mag_index].mag_num_bytes_in_objects;
7299 }
7300
7301 stats->blocks_in_use = t;
7302 stats->size_in_use = u;
7303 stats->size_allocated = (szone->num_small_regions - szone->num_small_regions_dealloc) * SMALL_REGION_SIZE;
7304 stats->max_size_in_use = stats->size_allocated - s;
7305 return 1;
7306 }
7307 case 2:
7308 stats->blocks_in_use = szone->num_large_objects_in_use;
7309 stats->size_in_use = szone->num_bytes_in_large_objects;
7310 stats->max_size_in_use = stats->size_allocated = stats->size_in_use;
7311 return 1;
7312 case 3:
7313 stats->blocks_in_use = 0; // DEPRECATED szone->num_huge_entries;
7314 stats->size_in_use = 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7315 stats->max_size_in_use = stats->size_allocated = 0;
7316 return 1;
7317 }
7318 return 0;
7319 }
7320
7321 static void
7322 szone_statistics(szone_t *szone, malloc_statistics_t *stats)
7323 {
7324 size_t large;
7325
7326 size_t s = 0;
7327 unsigned t = 0;
7328 size_t u = 0;
7329 mag_index_t mag_index;
7330
7331 for (mag_index = -1; mag_index < szone->num_tiny_magazines; mag_index++) {
7332 s += szone->tiny_magazines[mag_index].mag_bytes_free_at_start;
7333 s += szone->tiny_magazines[mag_index].mag_bytes_free_at_end;
7334 t += szone->tiny_magazines[mag_index].mag_num_objects;
7335 u += szone->tiny_magazines[mag_index].mag_num_bytes_in_objects;
7336 }
7337
7338 for (mag_index = -1; mag_index < szone->num_small_magazines; mag_index++) {
7339 s += szone->small_magazines[mag_index].mag_bytes_free_at_start;
7340 s += szone->small_magazines[mag_index].mag_bytes_free_at_end;
7341 t += szone->small_magazines[mag_index].mag_num_objects;
7342 u += szone->small_magazines[mag_index].mag_num_bytes_in_objects;
7343 }
7344
7345 large = szone->num_bytes_in_large_objects + 0; // DEPRECATED szone->num_bytes_in_huge_objects;
7346
7347 stats->blocks_in_use = t + szone->num_large_objects_in_use + 0; // DEPRECATED szone->num_huge_entries;
7348 stats->size_in_use = u + large;
7349 stats->max_size_in_use = stats->size_allocated =
7350 (szone->num_tiny_regions - szone->num_tiny_regions_dealloc) * TINY_REGION_SIZE +
7351 (szone->num_small_regions - szone->num_small_regions_dealloc) * SMALL_REGION_SIZE + large;
7352 // Now we account for the untouched areas
7353 stats->max_size_in_use -= s;
7354 }
7355
7356 static void *
7357 legacy_zeroing_large_malloc(szone_t *szone, size_t size) {
7358 if (size > LARGE_THRESHOLD) // Leopard and earlier returned a ZFOD range, so ...
7359 return szone_calloc(szone, 1, size); // Clear to zero always, ham-handedly touching in each page
7360 else
7361 return szone_malloc(szone, size);
7362 }
7363
7364 static void *
7365 legacy_zeroing_large_valloc(szone_t *szone, size_t size) {
7366 void *p = szone_valloc(szone, size);
7367
7368 // Leopard and earlier returned a ZFOD range, so ...
7369 memset(p, 0, size); // Clear to zero always, ham-handedly touching in each page
7370 return p;
7371 }
7372
7373 void zeroify_scalable_zone(malloc_zone_t *zone)
7374 {
7375 szone_t *szone = (szone_t *)zone;
7376
7377 if (szone) {
7378 mprotect(szone, sizeof(szone->basic_zone), PROT_READ | PROT_WRITE);
7379 szone->basic_zone.malloc = (void *)legacy_zeroing_large_malloc;
7380 szone->basic_zone.valloc = (void *)legacy_zeroing_large_valloc;
7381 mprotect(szone, sizeof(szone->basic_zone), PROT_READ);
7382 }
7383 }
7384
7385 static const struct malloc_introspection_t szone_introspect = {
7386 (void *)szone_ptr_in_use_enumerator,
7387 (void *)szone_good_size,
7388 (void *)szone_check,
7389 (void *)szone_print,
7390 szone_log,
7391 (void *)szone_force_lock,
7392 (void *)szone_force_unlock,
7393 (void *)szone_statistics,
7394 (void *)szone_locked,
7395 NULL, NULL, NULL, NULL, /* Zone enumeration version 7 and forward. */
7396 }; // marked as const to spare the DATA section
7397
7398 malloc_zone_t *
7399 create_scalable_zone(size_t initial_size, unsigned debug_flags)
7400 {
7401 szone_t *szone;
7402 uint64_t hw_memsize = 0;
7403
7404 /*
7405 * Sanity-check our build-time assumptions about the size of a page.
7406 * Since we have sized various things assuming the default page size,
7407 * attempting to determine it dynamically is not useful.
7408 */
7409 if ((vm_page_size != _vm_page_size) || (vm_page_shift != _vm_page_shift)) {
7410 malloc_printf("*** FATAL ERROR - machine page size does not match our assumptions.\n");
7411 exit(-1);
7412 }
7413
7414 #if defined(__i386__) || defined(__x86_64__)
7415 if (_COMM_PAGE_VERSION_REQD > (*((short *) _COMM_PAGE_VERSION))) { // _COMM_PAGE_CPU_NUMBER must be present at runtime
7416 malloc_printf("*** ERROR - comm page version mismatch.\n");
7417 exit(-1);
7418 }
7419 #endif
7420
7421 /* get memory for the zone. */
7422 szone = allocate_pages(NULL, SZONE_PAGED_SIZE, 0, 0, VM_MEMORY_MALLOC);
7423 if (!szone)
7424 return NULL;
7425
7426 /* set up the szone structure */
7427 #if 0
7428 #warning CHECK_REGIONS enabled
7429 debug_flags |= CHECK_REGIONS;
7430 #endif
7431 #if 0
7432 #warning LOG enabled
7433 szone->log_address = ~0;
7434 #endif
7435 szone->trg[0].nextgen = &(szone->trg[1]);
7436 szone->trg[1].nextgen = &(szone->trg[0]);
7437 szone->tiny_region_generation = &(szone->trg[0]);
7438
7439 szone->tiny_region_generation->hashed_regions = szone->initial_tiny_regions;
7440 szone->tiny_region_generation->num_regions_allocated = INITIAL_NUM_REGIONS;
7441 szone->tiny_region_generation->num_regions_allocated_shift = INITIAL_NUM_REGIONS_SHIFT;
7442
7443 szone->srg[0].nextgen = &(szone->srg[1]);
7444 szone->srg[1].nextgen = &(szone->srg[0]);
7445 szone->small_region_generation = &(szone->srg[0]);
7446
7447 szone->small_region_generation->hashed_regions = szone->initial_small_regions;
7448 szone->small_region_generation->num_regions_allocated = INITIAL_NUM_REGIONS;
7449 szone->small_region_generation->num_regions_allocated_shift = INITIAL_NUM_REGIONS_SHIFT;
7450
7451
7452 /*
7453 * Initialize variables that size the free list for SMALL allocations based
7454 * upon the amount of memory in the system. Switch to a larger number of
7455 * free list entries at 1GB.
7456 */
7457 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7458 if ((hw_memsize = *(uint64_t *)(uintptr_t)_COMM_PAGE_MEMORY_SIZE) >= (1ULL << 30))
7459 #else
7460 size_t uint64_t_size = sizeof(hw_memsize);
7461
7462 if (0 == sysctlbyname("hw.memsize", &hw_memsize, &uint64_t_size, 0, 0) &&
7463 hw_memsize >= (1ULL << 30))
7464 #endif
7465 {
7466 szone->is_largemem = 1;
7467 szone->num_small_slots = NUM_SMALL_SLOTS_LARGEMEM;
7468 szone->large_threshold = LARGE_THRESHOLD_LARGEMEM;
7469 szone->vm_copy_threshold = VM_COPY_THRESHOLD_LARGEMEM;
7470 } else {
7471 szone->is_largemem = 0;
7472 szone->num_small_slots = NUM_SMALL_SLOTS;
7473 szone->large_threshold = LARGE_THRESHOLD;
7474 szone->vm_copy_threshold = VM_COPY_THRESHOLD;
7475 }
7476 #if LARGE_CACHE
7477 szone->large_entry_cache_reserve_limit =
7478 hw_memsize >> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
7479
7480 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7481 int32_t libSystemVersion = NSVersionOfLinkTimeLibrary("System");
7482 if ((-1 != libSystemVersion) && ((libSystemVersion >> 16) < 112) /* CFSystemVersionSnowLeopard */)
7483 szone->large_legacy_reset_mprotect = TRUE;
7484 else
7485 szone->large_legacy_reset_mprotect = FALSE;
7486 #endif
7487
7488 // Initialize the security token.
7489 szone->cookie = (uintptr_t)malloc_entropy[0];
7490
7491 // Prepare ASLR
7492 #if __i386__ || __LP64__ || TARGET_OS_EMBEDDED
7493 #if __i386__
7494 uintptr_t stackbase = 0x8fe00000;
7495 int entropic_bits = 3;
7496 #elif __LP64__
7497 uintptr_t stackbase = USRSTACK64;
7498 int entropic_bits = 16;
7499 #else
7500 uintptr_t stackbase = USRSTACK;
7501 int entropic_bits = 3;
7502 #endif
7503 if (0 != _dyld_get_image_slide((const struct mach_header*)_NSGetMachExecuteHeader())) {
7504 if (0 == entropic_address) {
7505 uintptr_t t = stackbase - MAXSSIZ - ((uintptr_t) (malloc_entropy[1] & ((1 << entropic_bits) - 1)) << SMALL_BLOCKS_ALIGN);
7506 (void)__sync_bool_compare_and_swap(&entropic_limit, 0, t); // Just one initialization please
7507 (void)__sync_bool_compare_and_swap(&entropic_address, 0, t - ENTROPIC_KABILLION); // Just one initialization please
7508 }
7509 debug_flags &= ~DISABLE_ASLR;
7510 } else {
7511 debug_flags |= DISABLE_ASLR;
7512 }
7513
7514 #else
7515 debug_flags |= DISABLE_ASLR;
7516 #endif
7517
7518 szone->basic_zone.version = 8;
7519 szone->basic_zone.size = (void *)szone_size;
7520 szone->basic_zone.malloc = (void *)szone_malloc;
7521 szone->basic_zone.calloc = (void *)szone_calloc;
7522 szone->basic_zone.valloc = (void *)szone_valloc;
7523 szone->basic_zone.free = (void *)szone_free;
7524 szone->basic_zone.realloc = (void *)szone_realloc;
7525 szone->basic_zone.destroy = (void *)szone_destroy;
7526 szone->basic_zone.batch_malloc = (void *)szone_batch_malloc;
7527 szone->basic_zone.batch_free = (void *)szone_batch_free;
7528 szone->basic_zone.introspect = (struct malloc_introspection_t *)&szone_introspect;
7529 szone->basic_zone.memalign = (void *)szone_memalign;
7530 szone->basic_zone.free_definite_size = (void *)szone_free_definite_size;
7531 szone->basic_zone.pressure_relief = (void *)szone_pressure_relief;
7532
7533 szone->basic_zone.reserved1 = 0; /* Set to zero once and for all as required by CFAllocator. */
7534 szone->basic_zone.reserved2 = 0; /* Set to zero once and for all as required by CFAllocator. */
7535 mprotect(szone, sizeof(szone->basic_zone), PROT_READ); /* Prevent overwriting the function pointers in basic_zone. */
7536
7537 szone->debug_flags = debug_flags;
7538 LOCK_INIT(szone->large_szone_lock);
7539
7540 #if defined(__ppc__) || defined(__ppc64__)
7541 /*
7542 * In the interest of compatibility for PPC applications executing via Rosetta,
7543 * arrange to zero-fill allocations as occurred by side effect in Leopard and earlier.
7544 */
7545 zeroify_scalable_zone((malloc_zone_t *)szone);
7546 #endif
7547
7548 #if defined(__i386__) || defined(__x86_64__)
7549 szone->cpu_id_key = (pthread_key_t) -1; // Unused. _COMM_PAGE_CPU_NUMBER preferred.
7550 #else
7551 int err;
7552 if ((err = pthread_key_create(&(szone->cpu_id_key), NULL))) {
7553 malloc_printf("*** ERROR -pthread_key_create failure err=%d.\n", err);
7554 szone->cpu_id_key = (pthread_key_t) -1;
7555 }
7556 #endif
7557
7558 // Query the number of configured processors.
7559 // Uniprocessor case gets just one tiny and one small magazine (whose index is zero). This gives
7560 // the same behavior as the original scalable malloc. MP gets per-CPU magazines
7561 // that scale (way) better.
7562 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7563 int nproc = *(uint8_t *)(uintptr_t)_COMM_PAGE_NCPUS;
7564 #else
7565 int nproc = sysconf(_SC_NPROCESSORS_CONF);
7566 #endif
7567 szone->num_tiny_magazines = (nproc > 1) ? MIN(nproc, TINY_MAX_MAGAZINES) : 1;
7568
7569 // FIXME vm_allocate() based on number of configured CPUs
7570 magazine_t *tiny_magazines = allocate_pages(NULL, TINY_MAGAZINE_PAGED_SIZE, 0,
7571 SCALABLE_MALLOC_ADD_GUARD_PAGES, VM_MEMORY_MALLOC);
7572 if (NULL == tiny_magazines)
7573 return NULL;
7574
7575 szone->tiny_magazines = &(tiny_magazines[1]); // szone->tiny_magazines[-1] is the Depot
7576
7577 // The magazines are indexed in [0 .. (num_tiny_magazines - 1)]
7578 // Find the smallest power of 2 that exceeds (num_tiny_magazines - 1)
7579 szone->num_tiny_magazines_mask_shift = 0;
7580 int i = 1;
7581 while( i <= (szone->num_tiny_magazines - 1) ) {
7582 szone->num_tiny_magazines_mask_shift++;
7583 i <<= 1;
7584 }
7585
7586 // Now if i <= TINY_MAX_MAGAZINES we'll never access tiny_magazines[] out of bounds.
7587 if (i > TINY_MAX_MAGAZINES) {
7588 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
7589 exit(-1);
7590 }
7591
7592 // Reduce i by 1 to obtain a mask covering [0 .. (num_tiny_magazines - 1)]
7593 szone->num_tiny_magazines_mask = i - 1; // A mask used for hashing to a magazine index (and a safety aid)
7594 #if TARGET_OS_EMBEDDED
7595 szone->last_tiny_advise = 0;
7596 #endif
7597
7598 // Init the tiny_magazine locks
7599 LOCK_INIT(szone->tiny_regions_lock);
7600 LOCK_INIT(szone->tiny_magazines[DEPOT_MAGAZINE_INDEX].magazine_lock);
7601 for (i = 0; i < szone->num_tiny_magazines; ++i) {
7602 LOCK_INIT(szone->tiny_magazines[i].magazine_lock);
7603 }
7604
7605 szone->num_small_magazines = (nproc > 1) ? MIN(nproc, SMALL_MAX_MAGAZINES) : 1;
7606
7607 // FIXME vm_allocate() based on number of configured CPUs
7608 magazine_t *small_magazines = allocate_pages(NULL, SMALL_MAGAZINE_PAGED_SIZE, 0,
7609 SCALABLE_MALLOC_ADD_GUARD_PAGES, VM_MEMORY_MALLOC);
7610 if (NULL == small_magazines)
7611 return NULL;
7612
7613 szone->small_magazines = &(small_magazines[1]); // szone->small_magazines[-1] is the Depot
7614
7615 // The magazines are indexed in [0 .. (num_small_magazines - 1)]
7616 // Find the smallest power of 2 that exceeds (num_small_magazines - 1)
7617 szone->num_small_magazines_mask_shift = 0;
7618 while( i <= (szone->num_small_magazines - 1) ) {
7619 szone->num_small_magazines_mask_shift++;
7620 i <<= 1;
7621 }
7622
7623 // Now if i <= SMALL_MAX_MAGAZINES we'll never access small_magazines[] out of bounds.
7624 if (i > SMALL_MAX_MAGAZINES) {
7625 malloc_printf("*** FATAL ERROR - magazine mask exceeds allocated magazines.\n");
7626 exit(-1);
7627 }
7628
7629 // Reduce i by 1 to obtain a mask covering [0 .. (num_small_magazines - 1)]
7630 szone->num_small_magazines_mask = i - 1; // A mask used for hashing to a magazine index (and a safety aid)
7631 #if TARGET_OS_EMBEDDED
7632 szone->last_small_advise = 0;
7633 #endif
7634
7635 // Init the small_magazine locks
7636 LOCK_INIT(szone->small_regions_lock);
7637 LOCK_INIT(szone->small_magazines[DEPOT_MAGAZINE_INDEX].magazine_lock);
7638 for (i = 0; i < szone->num_small_magazines; ++i) {
7639 LOCK_INIT(szone->small_magazines[i].magazine_lock);
7640 }
7641
7642 CHECK(szone, __PRETTY_FUNCTION__);
7643 return (malloc_zone_t *)szone;
7644 }
7645
7646 //
7647 // purgeable zones have their own "large" allocation pool, but share "tiny" and "large"
7648 // heaps with a helper_zone identified in the call to create_purgeable_zone()
7649 //
7650 static size_t
7651 purgeable_size(szone_t *szone, const void *ptr)
7652 {
7653 // Only claim our large allocations, leave the shared tiny/small for the helper zone to claim.
7654 return szone_size_try_large(szone, ptr);
7655 }
7656
7657 static void *
7658 purgeable_malloc(szone_t *szone, size_t size) {
7659 if (size <= szone->large_threshold)
7660 return szone_malloc(szone->helper_zone, size);
7661 else
7662 return szone_malloc(szone, size);
7663 }
7664
7665 static void *
7666 purgeable_calloc(szone_t *szone, size_t num_items, size_t size)
7667 {
7668 size_t total_bytes = num_items * size;
7669
7670 // Check for overflow of integer multiplication
7671 if (num_items > 1) {
7672 #if __LP64__ /* size_t is uint64_t */
7673 if ((num_items | size) & 0xffffffff00000000ul) {
7674 // num_items or size equals or exceeds sqrt(2^64) == 2^32, appeal to wider arithmetic
7675 __uint128_t product = ((__uint128_t)num_items) * ((__uint128_t)size);
7676 if ((uint64_t)(product >> 64)) // compiles to test on upper register of register pair
7677 return NULL;
7678 }
7679 #else /* size_t is uint32_t */
7680 if ((num_items | size) & 0xffff0000ul) {
7681 // num_items or size equals or exceeds sqrt(2^32) == 2^16, appeal to wider arithmetic
7682 uint64_t product = ((uint64_t)num_items) * ((uint64_t)size);
7683 if ((uint32_t)(product >> 32)) // compiles to test on upper register of register pair
7684 return NULL;
7685 }
7686 #endif
7687 }
7688
7689 if (total_bytes <= szone->large_threshold)
7690 return szone_calloc(szone->helper_zone, 1, total_bytes);
7691 else
7692 return szone_calloc(szone, 1, total_bytes);
7693 }
7694
7695 static void *
7696 purgeable_valloc(szone_t *szone, size_t size)
7697 {
7698 if (size <= szone->large_threshold)
7699 return szone_valloc(szone->helper_zone, size);
7700 else
7701 return szone_valloc(szone, size);
7702 }
7703
7704 static void
7705 purgeable_free(szone_t *szone, void *ptr)
7706 {
7707 large_entry_t *entry;
7708
7709 SZONE_LOCK(szone);
7710 entry = large_entry_for_pointer_no_lock(szone, ptr);
7711 SZONE_UNLOCK(szone);
7712 if (entry) {
7713 return free_large(szone, ptr);
7714 } else {
7715 return szone_free(szone->helper_zone, ptr);
7716 }
7717 }
7718
7719 static void
7720 purgeable_free_definite_size(szone_t *szone, void *ptr, size_t size)
7721 {
7722 if (size <= szone->large_threshold)
7723 return szone_free_definite_size(szone->helper_zone, ptr, size);
7724 else
7725 return szone_free_definite_size(szone, ptr, size);
7726 }
7727
7728 static void *
7729 purgeable_realloc(szone_t *szone, void *ptr, size_t new_size)
7730 {
7731 size_t old_size;
7732
7733 if (NULL == ptr) {
7734 // If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size.
7735 return purgeable_malloc(szone, new_size);
7736 } else if (0 == new_size) {
7737 // If size is 0 and ptr is not a null pointer, the object pointed to is freed.
7738 purgeable_free(szone, ptr);
7739 // If size is 0, either a null pointer or a unique pointer that can be successfully passed
7740 // to free() shall be returned.
7741 return purgeable_malloc(szone, 1);
7742 }
7743
7744 old_size = purgeable_size(szone, ptr); // Now ptr can be safely size()'d
7745 if (!old_size)
7746 old_size = szone_size(szone->helper_zone, ptr);
7747
7748 if (!old_size) {
7749 szone_error(szone, 1, "pointer being reallocated was not allocated", ptr, NULL);
7750 return NULL;
7751 }
7752
7753 // Distinguish 4 cases: {oldsize, newsize} x { <= , > large_threshold }
7754 // and deal with the allocation crossing from the purgeable zone to the helper zone and vice versa.
7755 if (old_size <= szone->large_threshold) {
7756 if (new_size <= szone->large_threshold)
7757 return szone_realloc(szone->helper_zone, ptr, new_size);
7758 else {
7759 // allocation crosses from helper to purgeable zone
7760 void * new_ptr = purgeable_malloc(szone, new_size);
7761 if (new_ptr) {
7762 memcpy(new_ptr, ptr, old_size);
7763 szone_free_definite_size(szone->helper_zone, ptr, old_size);
7764 }
7765 return new_ptr; // in state VM_PURGABLE_NONVOLATILE
7766 }
7767 } else {
7768 if (new_size <= szone->large_threshold) {
7769 // allocation crosses from purgeable to helper zone
7770 void * new_ptr = szone_malloc(szone->helper_zone, new_size);
7771 if (new_ptr) {
7772 memcpy(new_ptr, ptr, new_size);
7773 purgeable_free_definite_size(szone, ptr, old_size);
7774 }
7775 return new_ptr;
7776 } else {
7777 void * new_ptr = purgeable_malloc(szone, new_size);
7778 if (new_ptr) {
7779 memcpy(new_ptr, ptr, MIN(old_size, new_size));
7780 purgeable_free_definite_size(szone, ptr, old_size);
7781 }
7782 return new_ptr; // in state VM_PURGABLE_NONVOLATILE
7783 }
7784 }
7785 /* NOTREACHED */
7786 }
7787
7788 static void
7789 purgeable_destroy(szone_t *szone)
7790 {
7791 /* destroy large entries */
7792 size_t index = szone->num_large_entries;
7793 large_entry_t *large;
7794 vm_range_t range_to_deallocate;
7795
7796 while (index--) {
7797 large = szone->large_entries + index;
7798 if (large->address) {
7799 // we deallocate_pages, including guard pages
7800 deallocate_pages(szone, (void *)(large->address), large->size, szone->debug_flags);
7801 }
7802 }
7803 large_entries_free_no_lock(szone, szone->large_entries, szone->num_large_entries, &range_to_deallocate);
7804 if (range_to_deallocate.size)
7805 deallocate_pages(szone, (void *)range_to_deallocate.address, (size_t)range_to_deallocate.size, 0);
7806
7807 /* Now destroy the separate szone region */
7808 deallocate_pages(szone, (void *)szone, SZONE_PAGED_SIZE, 0);
7809 }
7810
7811 static unsigned
7812 purgeable_batch_malloc(szone_t *szone, size_t size, void **results, unsigned count)
7813 {
7814 return szone_batch_malloc(szone->helper_zone, size, results, count);
7815 }
7816
7817 static void
7818 purgeable_batch_free(szone_t *szone, void **to_be_freed, unsigned count)
7819 {
7820 return szone_batch_free(szone->helper_zone, to_be_freed, count);
7821 }
7822
7823 static void *
7824 purgeable_memalign(szone_t *szone, size_t alignment, size_t size)
7825 {
7826 if (size <= szone->large_threshold)
7827 return szone_memalign(szone->helper_zone, alignment, size);
7828 else
7829 return szone_memalign(szone, alignment, size);
7830 }
7831
7832 static kern_return_t
7833 purgeable_ptr_in_use_enumerator(task_t task, void *context, unsigned type_mask, vm_address_t zone_address,
7834 memory_reader_t reader, vm_range_recorder_t recorder)
7835 {
7836 szone_t *szone;
7837 kern_return_t err;
7838
7839 if (!reader) reader = _szone_default_reader;
7840
7841 err = reader(task, zone_address, sizeof(szone_t), (void **)&szone);
7842 if (err) return err;
7843
7844 err = large_in_use_enumerator(task, context, type_mask,
7845 (vm_address_t)szone->large_entries, szone->num_large_entries, reader, recorder);
7846 return err;
7847 }
7848
7849 static size_t
7850 purgeable_good_size(szone_t *szone, size_t size)
7851 {
7852 if (size <= szone->large_threshold)
7853 return szone_good_size(szone->helper_zone, size);
7854 else
7855 return szone_good_size(szone, size);
7856 }
7857
7858 static boolean_t
7859 purgeable_check(szone_t *szone)
7860 {
7861 return 1;
7862 }
7863
7864 static void
7865 purgeable_print(szone_t *szone, boolean_t verbose)
7866 {
7867 _malloc_printf(MALLOC_PRINTF_NOLOG | MALLOC_PRINTF_NOPREFIX,
7868 "Scalable zone %p: inUse=%u(%y) flags=%d\n",
7869 szone, szone->num_large_objects_in_use, szone->num_bytes_in_large_objects, szone->debug_flags);
7870 }
7871
7872 static void
7873 purgeable_log(malloc_zone_t *zone, void *log_address)
7874 {
7875 szone_t *szone = (szone_t *)zone;
7876
7877 szone->log_address = log_address;
7878 }
7879
7880 static void
7881 purgeable_force_lock(szone_t *szone)
7882 {
7883 SZONE_LOCK(szone);
7884 }
7885
7886 static void
7887 purgeable_force_unlock(szone_t *szone)
7888 {
7889 SZONE_UNLOCK(szone);
7890 }
7891
7892 static void
7893 purgeable_statistics(szone_t *szone, malloc_statistics_t *stats)
7894 {
7895 stats->blocks_in_use = szone->num_large_objects_in_use;
7896 stats->size_in_use = stats->max_size_in_use = stats->size_allocated = szone->num_bytes_in_large_objects;
7897 }
7898
7899 static boolean_t
7900 purgeable_locked(szone_t *szone)
7901 {
7902 int tookLock;
7903
7904 tookLock = SZONE_TRY_LOCK(szone);
7905 if (tookLock == 0)
7906 return 1;
7907 SZONE_UNLOCK(szone);
7908 return 0;
7909 }
7910
7911 static size_t
7912 purgeable_pressure_relief(szone_t *szone, size_t goal)
7913 {
7914 return szone_pressure_relief(szone, goal) + szone_pressure_relief(szone->helper_zone, goal);
7915 }
7916
7917 static const struct malloc_introspection_t purgeable_introspect = {
7918 (void *)purgeable_ptr_in_use_enumerator,
7919 (void *)purgeable_good_size,
7920 (void *)purgeable_check,
7921 (void *)purgeable_print,
7922 purgeable_log,
7923 (void *)purgeable_force_lock,
7924 (void *)purgeable_force_unlock,
7925 (void *)purgeable_statistics,
7926 (void *)purgeable_locked,
7927 NULL, NULL, NULL, NULL, /* Zone enumeration version 7 and forward. */
7928 }; // marked as const to spare the DATA section
7929
7930 __private_extern__ malloc_zone_t *
7931 create_purgeable_zone(size_t initial_size, malloc_zone_t *malloc_default_zone, unsigned debug_flags)
7932 {
7933 szone_t *szone;
7934 uint64_t hw_memsize = 0;
7935
7936 /* get memory for the zone. */
7937 szone = allocate_pages(NULL, SZONE_PAGED_SIZE, 0, 0, VM_MEMORY_MALLOC);
7938 if (!szone)
7939 return NULL;
7940
7941 /* set up the szone structure */
7942 #if 0
7943 #warning LOG enabled
7944 szone->log_address = ~0;
7945 #endif
7946
7947 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7948 hw_memsize = *(uint64_t *)(uintptr_t)_COMM_PAGE_MEMORY_SIZE;
7949 #else
7950 size_t uint64_t_size = sizeof(hw_memsize);
7951
7952 sysctlbyname("hw.memsize", &hw_memsize, &uint64_t_size, 0, 0);
7953 #endif
7954 /* Purgeable zone does not participate in the adaptive "largemem" sizing. */
7955 szone->is_largemem = 0;
7956 szone->large_threshold = LARGE_THRESHOLD;
7957 szone->vm_copy_threshold = VM_COPY_THRESHOLD;
7958
7959 #if LARGE_CACHE
7960 szone->large_entry_cache_reserve_limit =
7961 hw_memsize >> 10; // madvise(..., MADV_REUSABLE) death-row arrivals above this threshold [~0.1%]
7962
7963 /* <rdar://problem/6610904> Reset protection when returning a previous large allocation? */
7964 int32_t libSystemVersion = NSVersionOfLinkTimeLibrary("System");
7965 if ((-1 != libSystemVersion) && ((libSystemVersion >> 16) < 112) /* CFSystemVersionSnowLeopard */)
7966 szone->large_legacy_reset_mprotect = TRUE;
7967 else
7968 szone->large_legacy_reset_mprotect = FALSE;
7969 #endif
7970
7971 szone->basic_zone.version = 8;
7972 szone->basic_zone.size = (void *)purgeable_size;
7973 szone->basic_zone.malloc = (void *)purgeable_malloc;
7974 szone->basic_zone.calloc = (void *)purgeable_calloc;
7975 szone->basic_zone.valloc = (void *)purgeable_valloc;
7976 szone->basic_zone.free = (void *)purgeable_free;
7977 szone->basic_zone.realloc = (void *)purgeable_realloc;
7978 szone->basic_zone.destroy = (void *)purgeable_destroy;
7979 szone->basic_zone.batch_malloc = (void *)purgeable_batch_malloc;
7980 szone->basic_zone.batch_free = (void *)purgeable_batch_free;
7981 szone->basic_zone.introspect = (struct malloc_introspection_t *)&purgeable_introspect;
7982 szone->basic_zone.memalign = (void *)purgeable_memalign;
7983 szone->basic_zone.free_definite_size = (void *)purgeable_free_definite_size;
7984 szone->basic_zone.pressure_relief = (void *)purgeable_pressure_relief;
7985
7986 szone->basic_zone.reserved1 = 0; /* Set to zero once and for all as required by CFAllocator. */
7987 szone->basic_zone.reserved2 = 0; /* Set to zero once and for all as required by CFAllocator. */
7988 mprotect(szone, sizeof(szone->basic_zone), PROT_READ); /* Prevent overwriting the function pointers in basic_zone. */
7989
7990 szone->debug_flags = debug_flags | SCALABLE_MALLOC_PURGEABLE;
7991
7992 /* Purgeable zone does not support SCALABLE_MALLOC_ADD_GUARD_PAGES. */
7993 if (szone->debug_flags & SCALABLE_MALLOC_ADD_GUARD_PAGES) {
7994 _malloc_printf(ASL_LEVEL_INFO, "purgeable zone does not support guard pages\n");
7995 szone->debug_flags &= ~SCALABLE_MALLOC_ADD_GUARD_PAGES;
7996 }
7997
7998 LOCK_INIT(szone->large_szone_lock);
7999
8000 szone->helper_zone = (struct szone_s *)malloc_default_zone;
8001
8002 CHECK(szone, __PRETTY_FUNCTION__);
8003 return (malloc_zone_t *)szone;
8004 }
8005
8006 /*
8007 * For use by CheckFix: create a new zone whose behavior is, apart from
8008 * the use of death-row and per-CPU magazines, that of Leopard.
8009 */
8010 static NOINLINE void *
8011 legacy_valloc(szone_t *szone, size_t size)
8012 {
8013 void *ptr;
8014 size_t num_pages;
8015
8016 num_pages = round_page(size) >> vm_page_shift;
8017 ptr = large_malloc(szone, num_pages, 0, TRUE);
8018 #if DEBUG_MALLOC
8019 if (LOG(szone, ptr))
8020 malloc_printf("legacy_valloc returned %p\n", ptr);
8021 #endif
8022 return ptr;
8023 }
8024
8025 __private_extern__ malloc_zone_t *
8026 create_legacy_scalable_zone(size_t initial_size, unsigned debug_flags)
8027 {
8028 malloc_zone_t *mzone = create_scalable_zone(initial_size, debug_flags);
8029 szone_t *szone = (szone_t *)mzone;
8030
8031 if (!szone)
8032 return NULL;
8033
8034 szone->is_largemem = 0;
8035 szone->num_small_slots = NUM_SMALL_SLOTS;
8036 szone->large_threshold = LARGE_THRESHOLD;
8037 szone->vm_copy_threshold = VM_COPY_THRESHOLD;
8038
8039 mprotect(szone, sizeof(szone->basic_zone), PROT_READ | PROT_WRITE);
8040 szone->basic_zone.valloc = (void *)legacy_valloc;
8041 szone->basic_zone.free_definite_size = NULL;
8042 mprotect(szone, sizeof(szone->basic_zone), PROT_READ);
8043
8044 return mzone;
8045 }
8046
8047 /********* Support code for emacs unexec ************/
8048
8049 /* History of freezedry version numbers:
8050 *
8051 * 1) Old malloc (before the scalable malloc implementation in this file
8052 * existed).
8053 * 2) Original freezedrying code for scalable malloc. This code was apparently
8054 * based on the old freezedrying code and was fundamentally flawed in its
8055 * assumption that tracking allocated memory regions was adequate to fake
8056 * operations on freezedried memory. This doesn't work, since scalable
8057 * malloc does not store flags in front of large page-aligned allocations.
8058 * 3) Original szone-based freezedrying code.
8059 * 4) Fresher malloc with tiny zone
8060 * 5) 32/64bit compatible malloc
8061 * 6) Metadata within 1MB and 8MB region for tiny and small
8062 *
8063 * No version backward compatibility is provided, but the version number does
8064 * make it possible for malloc_jumpstart() to return an error if the application
8065 * was freezedried with an older version of malloc.
8066 */
8067 #define MALLOC_FREEZEDRY_VERSION 6
8068
8069 typedef struct {
8070 unsigned version;
8071 unsigned nszones;
8072 szone_t *szones;
8073 } malloc_frozen;
8074
8075 static void *
8076 frozen_malloc(szone_t *zone, size_t new_size)
8077 {
8078 return malloc(new_size);
8079 }
8080
8081 static void *
8082 frozen_calloc(szone_t *zone, size_t num_items, size_t size)
8083 {
8084 return calloc(num_items, size);
8085 }
8086
8087 static void *
8088 frozen_valloc(szone_t *zone, size_t new_size)
8089 {
8090 return valloc(new_size);
8091 }
8092
8093 static void *
8094 frozen_realloc(szone_t *zone, void *ptr, size_t new_size)
8095 {
8096 size_t old_size = szone_size(zone, ptr);
8097 void *new_ptr;
8098
8099 if (new_size <= old_size) {
8100 return ptr;
8101 }
8102 new_ptr = malloc(new_size);
8103 if (old_size > 0) {
8104 memcpy(new_ptr, ptr, old_size);
8105 }
8106 return new_ptr;
8107 }
8108
8109 static void
8110 frozen_free(szone_t *zone, void *ptr)
8111 {
8112 }
8113
8114 static void
8115 frozen_destroy(szone_t *zone)
8116 {
8117 }
8118
8119 /********* Pseudo-private API for emacs unexec ************/
8120
8121 /*
8122 * malloc_freezedry() records all of the szones in use, so that they can be
8123 * partially reconstituted by malloc_jumpstart(). Due to the differences
8124 * between reconstituted memory regions and those created by the szone code,
8125 * care is taken not to reallocate from the freezedried memory, except in the
8126 * case of a non-growing realloc().
8127 *
8128 * Due to the flexibility provided by the zone registration mechanism, it is
8129 * impossible to implement generic freezedrying for any zone type. This code
8130 * only handles applications that use the szone allocator, so malloc_freezedry()
8131 * returns 0 (error) if any non-szone zones are encountered.
8132 */
8133
8134 uintptr_t
8135 malloc_freezedry(void)
8136 {
8137 extern unsigned malloc_num_zones;
8138 extern malloc_zone_t **malloc_zones;
8139 malloc_frozen *data;
8140 unsigned i;
8141
8142 /* Allocate space in which to store the freezedry state. */
8143 data = (malloc_frozen *) malloc(sizeof(malloc_frozen));
8144
8145 /* Set freezedry version number so that malloc_jumpstart() can check for compatibility. */
8146 data->version = MALLOC_FREEZEDRY_VERSION;
8147
8148 /* Allocate the array of szone pointers. */
8149 data->nszones = malloc_num_zones;
8150 data->szones = (szone_t *) calloc(malloc_num_zones, sizeof(szone_t));
8151
8152 /*
8153 * Fill in the array of szone structures. They are copied rather than
8154 * referenced, since the originals are likely to be clobbered during malloc
8155 * initialization.
8156 */
8157 for (i = 0; i < malloc_num_zones; i++) {
8158 if (strcmp(malloc_zones[i]->zone_name, "DefaultMallocZone")) {
8159 /* Unknown zone type. */
8160 free(data->szones);
8161 free(data);
8162 return 0;
8163 }
8164 memcpy(&data->szones[i], malloc_zones[i], sizeof(szone_t));
8165 }
8166
8167 return((uintptr_t)data);
8168 }
8169
8170 int
8171 malloc_jumpstart(uintptr_t cookie)
8172 {
8173 malloc_frozen *data = (malloc_frozen *)cookie;
8174 unsigned i;
8175
8176 if (data->version != MALLOC_FREEZEDRY_VERSION) {
8177 /* Unsupported freezedry version. */
8178 return 1;
8179 }
8180
8181 for (i = 0; i < data->nszones; i++) {
8182 /* Set function pointers. Even the functions that stay the same must be
8183 * set, since there are no guarantees that they will be mapped to the
8184 * same addresses. */
8185 data->szones[i].basic_zone.size = (void *) szone_size;
8186 data->szones[i].basic_zone.malloc = (void *) frozen_malloc;
8187 data->szones[i].basic_zone.calloc = (void *) frozen_calloc;
8188 data->szones[i].basic_zone.valloc = (void *) frozen_valloc;
8189 data->szones[i].basic_zone.free = (void *) frozen_free;
8190 data->szones[i].basic_zone.realloc = (void *) frozen_realloc;
8191 data->szones[i].basic_zone.destroy = (void *) frozen_destroy;
8192 data->szones[i].basic_zone.introspect = (struct malloc_introspection_t *)&szone_introspect;
8193
8194 /* Register the freezedried zone. */
8195 malloc_zone_register(&data->szones[i].basic_zone);
8196 }
8197
8198 return 0;
8199 }