Loading...
src/nano_malloc_common.c libmalloc-317.140.5 libmalloc-521.120.7
--- libmalloc/libmalloc-317.140.5/src/nano_malloc_common.c
+++ libmalloc/libmalloc-521.120.7/src/nano_malloc_common.c
@@ -23,99 +23,133 @@
 
 #include "internal.h"
 
-// Code that is common to Nano V1 and Nano V2. When Nano V1 is removed,
-// most of this file will move to nanov2_malloc.c.
-
 #if CONFIG_NANOZONE
 
 // Possible enablement modes for Nano V2
 typedef enum {
-	NANO_INACTIVE,	// Inactive, but can be selected with MallocNanoZone=V2
 	NANO_ENABLED,	// Available and default if Nano is turned on.
 	NANO_FORCED,	// Force use of Nano V2 for all processes.
+	NANO_CONDITIONAL,	// Use Nano V2 in non space-efficient processes
 } nanov2_mode_t;
 
-// Which version of Nano is engaged. By default, none.
+// Whether Nano is engaged. By default, none.
 nano_version_t _malloc_engaged_nano = NANO_NONE;
 
 // Nano mode selection boot argument
 static const char mode_boot_arg[] = "nanov2_mode";
-static const char inactive_mode[] = "inactive"; // Use Nano V1 for Nano
 static const char enabled_mode[] = "enabled";	// Use Nano V2 for Nano
 static const char forced_mode[] = "forced";		// Force Nano V2 everywhere
+static const char conditional_mode[] = "conditional"; // Use Nano V2 in non space-efficient processes
 
 // The maximum number of per-CPU allocation regions to use for Nano.
 unsigned int nano_common_max_magazines;
-boolean_t nano_common_max_magazines_is_ncpu = true;
+bool nano_common_max_magazines_is_ncpu = true;
+
+unsigned int nano_max_region = NANOV2_MAX_REGION_NUMBER;
 
 // Boot argument for nano_common_max_magazines
 static const char nano_max_magazines_boot_arg[] = "malloc_nano_max_magazines";
 
+
 #pragma mark -
 #pragma mark Initialization
+
+nano_version_t
+_nano_common_init_pick_mode(const char *envp[], const char *apple[], const char *bootargs, bool space_efficient_enabled)
+{
+	const char *p = NULL;
+	const char *flag = NULL;
+	nano_version_t ret = NANO_NONE;
+
+	// Use the nanov2_mode boot argument and MallocNanoZone to determine
+	// whether to use nano
+	nanov2_mode_t nanov2_mode = NANOV2_DEFAULT_MODE;
+
+	p = malloc_common_value_for_key(bootargs, mode_boot_arg);
+	if (p) {
+		if (!strncmp(p, enabled_mode, sizeof(enabled_mode) - 1)) {
+			nanov2_mode = NANO_ENABLED;
+		} else if (!strncmp(p, forced_mode, sizeof(forced_mode) - 1)) {
+			nanov2_mode = NANO_FORCED;
+		} else if (!strncmp(p, conditional_mode, sizeof(conditional_mode) - 1)) {
+			nanov2_mode = NANO_CONDITIONAL;
+		}
+	}
+
+	if (nanov2_mode == NANO_FORCED) {
+		ret = NANO_V2;
+	} else {
+		if (nanov2_mode == NANO_CONDITIONAL) {
+			// If conditional mode is selected, ignore the apple[] array and
+			// make the decision based of space efficient mode.
+			ret = space_efficient_enabled ? NANO_NONE : NANO_V2;
+		} else {
+			flag = _simple_getenv(apple, "MallocNanoZone");
+			if (flag && flag[0] == '1') {
+				ret = NANO_V2;
+			}
+		}
+		/* Explicit overrides from the environment */
+		flag = _simple_getenv(envp, "MallocNanoZone");
+		if (flag) {
+			if (flag[0] == '1') {
+				ret = NANO_V2;
+			} else if (flag[0] == '0') {
+				ret = NANO_NONE;
+			} else if (flag[0] == 'V' || flag[0] == 'v') {
+				if (flag[1] == '1' || flag[1] == '2') {
+					ret = NANO_V2;
+				}
+			}
+		}
+	}
+
+	return ret;
+}
 
 // Shared initialization code. Determines which version of Nano should be used,
 // if any, and sets _malloc_engaged_nano. The Nano version is determined as
 // follows:
 // 1. If the nanov2_mode boot arg has value "forced", Nano V2 is used
-//		unconditionally in every process, except in processes that have
-//		the MallocNanoZone variable set to V1.
+//		unconditionally in every process
 // 2. If the nanov2_mode boot arg has value "enabled", Nano V2 is used if
-//		the process wants to use Nano (i.e. the kernel opts the process in, or
-//		the environment variable MallocNanoZone is 1).
-// 3. If the nanov2_mode boot arg is not present or has any other value,
-//		Nano V1 is used if the process wants to use Nano (i.e. the kernel opts
-//		the process in, or the environment variable MallocNanoZone is 1).
-//
-// In cases (2) and (3), the selection can be explicitly overridden by setting
-// the environment variable MallocNanoZone to V1 or V2.
+//		the process wants to use Nano
 void
 nano_common_init(const char *envp[], const char *apple[], const char *bootargs)
 {
-	// Use the nanov2_mode boot argument and MallocNanoZone to determine
-	// which version of Nano to use, if any.
-	nanov2_mode_t nanov2_mode = NANO_ENABLED;
-	const char *p = malloc_common_value_for_key(bootargs, mode_boot_arg);
+	_malloc_engaged_nano = _nano_common_init_pick_mode(envp, apple, bootargs, malloc_space_efficient_enabled);
+
+#if NANOV2_MULTIPLE_REGIONS
+	// Override max region number from environment
+	const char *p = malloc_common_value_for_key(bootargs, "malloc_nano_max_region");
 	if (p) {
-		if (!strncmp(p, inactive_mode, sizeof(inactive_mode) - 1)) {
-			nanov2_mode = NANO_INACTIVE;
-		} else if (!strncmp(p, enabled_mode, sizeof(enabled_mode) - 1)) {
-			nanov2_mode = NANO_ENABLED;
-		} else if (!strncmp(p, forced_mode, sizeof(forced_mode) - 1)) {
-			nanov2_mode = NANO_FORCED;
-		}
-	}
-
-	if (nanov2_mode == NANO_FORCED) {
-		// We will use Nano V2 unless MallocNanoZone is "V1".
-		const char *flag = _simple_getenv(envp, "MallocNanoZone");
-		if (flag && (flag[0] == 'V' || flag[0] == 'v') && flag[1] == '1') {
-			_malloc_engaged_nano = NANO_V1;
-		} else {
-			_malloc_engaged_nano = NANO_V2;
-		}
-	} else {
-		const char *flag = _simple_getenv(apple, "MallocNanoZone");
-		if (flag && flag[0] == '1') {
-			_malloc_engaged_nano = nanov2_mode == NANO_ENABLED ? NANO_V2 : NANO_V1;
-		}
-		/* Explicit overrides from the environment */
-		flag = _simple_getenv(envp, "MallocNanoZone");
-		if (flag) {
-			if (flag[0] == '1') {
-				_malloc_engaged_nano = nanov2_mode == NANO_ENABLED ? NANO_V2 : NANO_V1;
-			} else if (flag[0] == '0') {
-				_malloc_engaged_nano = NANO_NONE;
-			} else if (flag[0] == 'V' || flag[0] == 'v') {
-				if (flag[1] == '1') {
-					_malloc_engaged_nano = NANO_V1;
-				} else if (flag[1] == '2') {
-					_malloc_engaged_nano = NANO_V2;
-				}
-			}
-		}
-	}
-
+		long value = strtol(p, NULL, 10);
+		if (value) {
+			if (value > NANOV2_MAX_REGION_NUMBER) {
+				nano_max_region = NANOV2_MAX_REGION_NUMBER;
+				malloc_report(ASL_LEVEL_INFO, "Capping 'malloc_nano_max_region' to %d\n", nano_max_region);
+			} else if (value >= 0) {
+				nano_max_region = (unsigned int)value;
+			} else {
+				malloc_report(ASL_LEVEL_ERR, "Received invalid value for 'malloc_nano_max_region': %d\n", (int)value);
+			}
+		}
+	}
+	const char *flag = _simple_getenv(envp, "MallocNanoMaxRegion");
+	if (flag) {
+		long value = strtol(flag, NULL, 10);
+		if (value) {
+			if (value > NANOV2_MAX_REGION_NUMBER) {
+				nano_max_region = NANOV2_MAX_REGION_NUMBER;
+				malloc_report(ASL_LEVEL_INFO, "Capping 'MallocNanoMaxRegion' to %d\n", nano_max_region);
+			} else if (value >= 0) {
+				nano_max_region = (unsigned int)value;
+			} else {
+				malloc_report(ASL_LEVEL_ERR, "Received invalid value for 'MallocNanoMaxRegion': %d\n", (int)value);
+			}
+		}
+	}
+#endif // NANOV2_MULTIPLE_REGIONS
 	if (_malloc_engaged_nano) {
 		// The maximum number of nano magazines can be set either via a
 		// boot argument or from the environment. Get the boot argument value
@@ -138,9 +172,6 @@
 	}
 
 	switch (_malloc_engaged_nano) {
-	case NANO_V1:
-		nano_init(envp, apple, bootargs);
-		break;
 	case NANO_V2:
 		nanov2_init(envp, apple, bootargs);
 		break;
@@ -193,9 +224,6 @@
 	nano_common_cpu_number_override_set();
 
 	switch (_malloc_engaged_nano) {
-	case NANO_V1:
-		nano_configure();
-		break;
 	case NANO_V2:
 		nanov2_configure();
 		break;
@@ -233,34 +261,64 @@
 			allocation_mask, alloc_flags, MEMORY_OBJECT_NULL, 0, FALSE,
 			VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT);
 	if (kr) {
-		malloc_zone_error(debug_flags, false, "*** can't allocate pages: "
-				"mach_vm_map(size=%lu) failed (error code=%d)\n", size, kr);
+		if (kr != KERN_NO_SPACE) {
+			malloc_zone_error(debug_flags, false, "*** can't allocate pages: "
+					"mach_vm_map(size=%lu) failed (error code=%d)\n", size, kr);
+		}
 		return NULL;
 	}
 	addr = (uintptr_t)vm_addr;
 
 	return (void *)addr;
+}
+
+static boolean_t
+_nano_common_map_vm_space(mach_vm_address_t base, mach_vm_size_t size, 
+		vm_prot_t cur_protection)
+{
+	mach_vm_address_t vm_addr = base;
+
+	kern_return_t kr = mach_vm_map(mach_task_self(), &vm_addr, size, 0,
+		VM_MAKE_TAG(VM_MEMORY_MALLOC_NANO), MEMORY_OBJECT_NULL, 0, FALSE,
+		cur_protection, VM_PROT_ALL, VM_INHERIT_DEFAULT);
+
+	if (kr != KERN_SUCCESS) {
+		return FALSE;
+	} else if (vm_addr != base) {
+		// allocated somewhere else
+		mach_vm_deallocate(mach_task_self(), vm_addr, size);
+		return FALSE;
+	}
+	return TRUE;
 }
 
 // Allocates virtual address from a given address for a given size. Succeeds
 // (and returns TRUE) only if we get exactly the range of addresses that we
 // asked for.
-boolean_t
+bool
 nano_common_allocate_vm_space(mach_vm_address_t base, mach_vm_size_t size)
 {
-	mach_vm_address_t vm_addr = base;
-	kern_return_t kr = mach_vm_map(mach_task_self(), &vm_addr, size, 0,
-		VM_MAKE_TAG(VM_MEMORY_MALLOC_NANO), MEMORY_OBJECT_NULL, 0, FALSE,
-		VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT);
-	
-	if (kr != KERN_SUCCESS || vm_addr != base) {
-		// Failed or we got allocated somewhere else.
-		if (!kr) {
-			mach_vm_deallocate(mach_task_self(), vm_addr, size);
-		}
-		return FALSE;
-	}
-	return TRUE;
+	return _nano_common_map_vm_space(base, size, VM_PROT_DEFAULT);
+}
+
+// Reserve virtual address range by allocating without perimissions
+bool
+nano_common_reserve_vm_space(mach_vm_address_t base, mach_vm_size_t size)
+{
+	return _nano_common_map_vm_space(base, size, VM_PROT_NONE);
+}
+
+// Set protection to default for address range. Return true on success.
+bool
+nano_common_unprotect_vm_space(mach_vm_address_t base, mach_vm_size_t size)
+{
+	kern_return_t kr = mach_vm_protect(mach_task_self(), base,
+			size, false, VM_PROT_DEFAULT);
+	if (kr != KERN_SUCCESS) {
+		malloc_report(ASL_LEVEL_ERR, "mach_vm_protect ret: %d\n", kr);
+		return false;
+	}
+	return true;
 }
 
 void