Loading...
--- libmalloc/libmalloc-166.220.1/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;
+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;
@@ -162,6 +193,11 @@
// Environment variable overrides boot arg, unless it's not valid.
const char *flag = getenv("MallocNanoMaxMagazines");
+#if RDAR_48993662
+ if (!flag) {
+ flag = getenv("_MallocNanoMaxMagazines");
+ }
+#endif // RDAR_48993662
if (flag) {
int value = (int)strtol(flag, NULL, 0);
if (value < 0) {
@@ -188,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;
@@ -228,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
@@ -289,11 +352,17 @@
void
nano_common_cpu_number_override_set()
{
+ boolean_t is_ncpu = _os_cpu_number_override == -1 && nano_common_max_magazines == phys_ncpus;
+
// This facilitates a shortcut in nanov2_get_allocation_block_index() --
// if nano_common_max_magazines_is_ncpu is true, we can also assume that
// _os_cpu_number_override == -1 (i.e. we are not in malloc_replay).
- nano_common_max_magazines_is_ncpu = _os_cpu_number_override == -1 &&
- nano_common_max_magazines == phys_ncpus;
+ //
+ // We check here for false, because we don't want to write "true" to a __DATA page because
+ // that would make it dirty: <rdar://problem/46994833>
+ if (!is_ncpu) {
+ nano_common_max_magazines_is_ncpu = is_ncpu;
+ }
}
#endif // CONFIG_NANOZONE