Loading...
--- libmalloc/libmalloc-166.220.1/src/magazine_tiny.c
+++ libmalloc/libmalloc-166.251.2/src/magazine_tiny.c
@@ -57,6 +57,12 @@
 #endif // CONFIG_SMALL_USES_HYPER_SHIFT
 }
 
+static inline grain_t
+tiny_slot_from_msize(msize_t msize)
+{
+	return (!msize || (msize > NUM_TINY_SLOTS) ? NUM_TINY_SLOTS : msize - 1);
+}
+
 /*
  * Get the size of the previous free block, which is stored in the last two
  * bytes of the block.  If the previous block is not free, then the result is
@@ -91,11 +97,14 @@
 	uint32_t val = (1 << (index & 31));
 
 #if DEBUG_MALLOC
-	if (msize >= NUM_TINY_SLOTS) {
+	if (msize > NUM_TINY_SLOTS) {
 		malloc_report(ASL_LEVEL_ERR, "set_tiny_meta_header_in_use() invariant broken %p %d\n", ptr, msize);
 	}
 	if ((unsigned)index + (unsigned)msize > 0x10000) {
 		malloc_report(ASL_LEVEL_ERR, "set_tiny_meta_header_in_use() invariant broken (2) %p %d\n", ptr, msize);
+	}
+	if (msize > TINY_BITMAP_RANGE_LIMIT) {
+		malloc_report(ASL_LEVEL_ERROR, "set_tiny_meta_header_in_use() invariant broken (3) %p %d\n", ptr, msize);
 	}
 #endif
 
@@ -292,7 +301,7 @@
 static void
 tiny_free_list_add_ptr(rack_t *rack, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize)
 {
-	grain_t slot = (!msize || (msize >= NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS - 1 : msize - 1;
+	grain_t slot = (!msize || (msize > NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS : msize - 1;
 	tiny_free_list_t *free_ptr = ptr;
 	tiny_free_list_t *free_head = tiny_mag_ptr->mag_free_list[slot].p;
 
@@ -335,7 +344,7 @@
 static void
 tiny_free_list_remove_ptr(rack_t *rack, magazine_t *tiny_mag_ptr, void *ptr, msize_t msize)
 {
-	grain_t slot = (!msize || (msize >= NUM_TINY_SLOTS)) ? NUM_TINY_SLOTS - 1 : msize - 1;
+	grain_t slot = tiny_slot_from_msize(msize);
 	tiny_free_list_t *free_ptr = ptr, *next, *previous;
 
 	next = free_list_unchecksum_ptr(rack, &free_ptr->next);
@@ -616,7 +625,7 @@
 tiny_find_msize_region(rack_t *rack, magazine_t *tiny_mag_ptr, mag_index_t mag_index, msize_t msize)
 {
 	tiny_free_list_t *ptr;
-	grain_t slot = msize - 1;
+	grain_t slot = tiny_slot_from_msize(msize);
 	free_list_t *free_list = tiny_mag_ptr->mag_free_list;
 	free_list_t *the_slot = free_list + slot;
 	free_list_t *limit;
@@ -647,7 +656,7 @@
 	}
 
 	slot = BITMAPV_CTZ(bitmap);
-	limit = free_list + NUM_TINY_SLOTS - 1;
+	limit = free_list + NUM_TINY_SLOTS;
 	free_list += slot;
 
 	if (free_list < limit) {
@@ -663,7 +672,7 @@
 	}
 
 	// We are now looking at the last slot, which contains blocks equal to, or
-	// due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
+	// due to coalescing of free blocks, larger than NUM_TINY_SLOTS * tiny quantum size.
 	ptr = limit->p;
 	if (ptr) {
 		return TINY_REGION_FOR_PTR(ptr);
@@ -671,6 +680,89 @@
 
 	return NULL;
 }
+
+#if CONFIG_MADVISE_PRESSURE_RELIEF
+void
+tiny_madvise_pressure_relief(rack_t *rack)
+{
+	mag_index_t mag_index;
+	magazine_t *tiny_depot_ptr = (&rack->magazines[DEPOT_MAGAZINE_INDEX]);
+
+	for (mag_index = 0; mag_index < rack->num_magazines; mag_index++) {
+		size_t index;
+		for (index = 0; index < rack->region_generation->num_regions_allocated; ++index) {
+			SZONE_LOCK(TINY_SZONE_FROM_RACK(rack));
+
+			region_t tiny = rack->region_generation->hashed_regions[index];
+			if (!tiny || tiny == HASHRING_REGION_DEALLOCATED) {
+				SZONE_UNLOCK(TINY_SZONE_FROM_RACK(rack));
+				continue;
+			}
+
+			magazine_t *mag_ptr = mag_lock_zine_for_region_trailer(rack->magazines,
+					REGION_TRAILER_FOR_TINY_REGION(tiny),
+					MAGAZINE_INDEX_FOR_TINY_REGION(tiny));
+			SZONE_UNLOCK(TINY_SZONE_FROM_RACK(rack));
+
+			/* Ordering is important here, the magazine of a region may potentially change
+			 * during mag_lock_zine_for_region_trailer, so src_mag_index must be taken
+			 * after we've obtained the lock.
+			 */
+			mag_index_t src_mag_index = MAGAZINE_INDEX_FOR_TINY_REGION(tiny);
+
+			/* We can (and must) ignore magazines that are already in the recirc depot. */
+			if (src_mag_index == DEPOT_MAGAZINE_INDEX) {
+				SZONE_MAGAZINE_PTR_UNLOCK(mag_ptr);
+				continue;
+			}
+
+			if (tiny == mag_ptr->mag_last_region && (mag_ptr->mag_bytes_free_at_end || mag_ptr->mag_bytes_free_at_start)) {
+				tiny_finalize_region(rack, mag_ptr);
+			}
+
+			/* Because this region is currently in use, we can't safely madvise it while
+			 * it's attached to the magazine. For this operation we have to remove it from
+			 * the current mag, attach it to the depot and then madvise.
+			 */
+
+			recirc_list_extract(rack, mag_ptr, REGION_TRAILER_FOR_TINY_REGION(tiny));
+			int objects_in_use = tiny_free_detach_region(rack, mag_ptr, tiny);
+
+			SZONE_MAGAZINE_PTR_LOCK(tiny_depot_ptr);
+			MAGAZINE_INDEX_FOR_TINY_REGION(tiny) = DEPOT_MAGAZINE_INDEX;
+			REGION_TRAILER_FOR_TINY_REGION(tiny)->pinned_to_depot = 0;
+
+			size_t bytes_inplay = tiny_free_reattach_region(rack, tiny_depot_ptr, tiny);
+
+			/* Fix up the metadata of the target magazine while the region is in the depot. */
+			mag_ptr->mag_num_bytes_in_objects -= bytes_inplay;
+			mag_ptr->num_bytes_in_magazine -= TINY_REGION_PAYLOAD_BYTES;
+			mag_ptr->mag_num_objects -= objects_in_use;
+
+			/* Now we can drop the magazine lock of the source mag. */
+			SZONE_MAGAZINE_PTR_UNLOCK(mag_ptr);
+
+			tiny_depot_ptr->mag_num_bytes_in_objects += bytes_inplay;
+			tiny_depot_ptr->num_bytes_in_magazine += TINY_REGION_PAYLOAD_BYTES;
+			tiny_depot_ptr->mag_num_objects -= objects_in_use;
+
+			recirc_list_splice_last(rack, tiny_depot_ptr, REGION_TRAILER_FOR_TINY_REGION(tiny));
+
+			/* Actually do the scan, done holding the depot lock, the call will drop the lock
+			 * around the actual madvise syscalls.
+			 */
+			tiny_free_scan_madvise_free(rack, tiny_depot_ptr, tiny);
+
+			/* Now the region is in the recirc depot, the next allocations to require more
+			 * blocks will come along and take one of these regions back out of the depot.
+			 * As OS X madvise's reuse on an per-region basis, we leave as many of these
+			 * regions in the depot as possible after memory pressure.
+			 */
+			SZONE_MAGAZINE_PTR_UNLOCK(tiny_depot_ptr);
+		}
+	}
+}
+#endif // CONFIG_MADVISE_PRESSURE_RELIEF
 
 static MALLOC_INLINE void
 tiny_madvise_free_range_no_lock(rack_t *rack,
@@ -1033,9 +1125,9 @@
 #endif
 		// If we are coalescing with the next block, and the next block is in
 		// the last slot of the free list, then we optimize this case here to
-		// avoid removing next_block from the slot (NUM_TINY_SLOTS - 1) and then adding ptr back
-		// to slot (NUM_TINY_SLOTS - 1).
-		if (next_msize >= NUM_TINY_SLOTS) {
+		// avoid removing next_block from the slot NUM_TINY_SLOTS and then adding ptr back
+		// to slot NUM_TINY_SLOTS.
+		if (next_msize > NUM_TINY_SLOTS) {
 			msize += next_msize;
 
 			big_free_block = (tiny_free_list_t *)next_block;
@@ -1043,7 +1135,7 @@
 			before_next_block = free_list_unchecksum_ptr(rack, &big_free_block->previous);
 
 			if (!before_next_block) {
-				tiny_mag_ptr->mag_free_list[NUM_TINY_SLOTS - 1].p = ptr;
+				tiny_mag_ptr->mag_free_list[NUM_TINY_SLOTS].p = ptr;
 			} else {
 				before_next_block->next.u = free_list_checksum_ptr(rack, ptr);
 			}
@@ -1487,10 +1579,10 @@
 
 		if (!is_free) {
 			/*
-			 * In use blocks cannot be more than (NUM_TINY_SLOTS - 1) quanta large.
+			 * In use blocks cannot be more than NUM_TINY_SLOTS quanta large.
 			 */
 			prev_free = 0;
-			if (msize > (NUM_TINY_SLOTS - 1)) {
+			if (msize > NUM_TINY_SLOTS) {
 				TINY_CHECK_FAIL("*** invariant broken for %p this tiny msize=%d - size is too large\n", (void *)ptr, msize);
 				return 0;
 			}
@@ -1719,7 +1811,7 @@
 {
 	tiny_free_list_t *ptr;
 	msize_t this_msize;
-	grain_t slot = msize - 1;
+	grain_t slot = tiny_slot_from_msize(msize);
 	free_list_t *free_list = tiny_mag_ptr->mag_free_list;
 	free_list_t *the_slot = free_list + slot;
 	tiny_free_list_t *next;
@@ -1768,7 +1860,7 @@
 	}
 
 	slot = BITMAPV_CTZ(bitmap);
-	limit = free_list + NUM_TINY_SLOTS - 1;
+	limit = free_list + NUM_TINY_SLOTS;
 	free_list += slot;
 
 	if (free_list < limit) {
@@ -1790,14 +1882,14 @@
 	}
 
 	// We are now looking at the last slot, which contains blocks equal to, or
-	// due to coalescing of free blocks, larger than (NUM_TINY_SLOTS - 1) * tiny quantum size.
+	// due to coalescing of free blocks, larger than NUM_TINY_SLOTS * tiny quantum size.
 	// If the last freelist is not empty, and the head contains a block that is
 	// larger than our request, then the remainder is put back on the free list.
 	ptr = limit->p;
 	if (ptr) {
 		this_msize = get_tiny_free_size(ptr);
 		next = free_list_unchecksum_ptr(rack, &ptr->next);
-		if (this_msize - msize >= NUM_TINY_SLOTS) {
+		if (this_msize - msize > NUM_TINY_SLOTS) {
 			// the leftover will go back to the free list, so we optimize by
 			// modifying the free list rather than a pop and push of the head
 			leftover_msize = this_msize - msize;
@@ -2269,7 +2361,7 @@
 			while (slot < NUM_TINY_SLOTS) {
 				ptr = rack->magazines[mag_index].mag_free_list[slot].p;
 				if (ptr) {
-					_simple_sprintf(b, "%s%y[%d]; ", (slot == NUM_TINY_SLOTS - 1) ? ">=" : "", (slot + 1) * TINY_QUANTUM,
+					_simple_sprintf(b, "%s%y[%d]; ", (slot == NUM_TINY_SLOTS) ? ">=" : "", (slot + 1) * TINY_QUANTUM,
 									free_list_count(rack, (free_list_t){ .p = ptr }));
 				}
 				slot++;