1 /*
2 * Copyright (c) 2025 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * The only difference between this file and vm_mte_reclamation.c is that
31 * this file runs with MTE turned on.
32 */
33
34 #include <ctype.h>
35 #include <darwintest.h>
36 #include <darwintest_utils.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/sysctl.h>
41 #include <signal.h>
42 #include <sys/types.h>
43 #include <sys/wait.h>
44 #include <unistd.h>
45
46 #include "arm_mte_utilities.h"
47 #include "test_utils.h"
48
49 #if __arm64__
50 T_GLOBAL_META(
51 T_META_NAMESPACE("xnu.vm.mteinfo"),
52 T_META_RADAR_COMPONENT_NAME("xnu"),
53 T_META_RADAR_COMPONENT_VERSION("VM"),
54 T_META_RUN_CONCURRENTLY(true),
55 T_META_TAG_VM_PREFERRED);
56
57 static void
tear_down(void)58 tear_down(void)
59 {
60 /* Terminate munch */
61 T_QUIET; T_EXPECT_POSIX_SUCCESS(system("killall -9 munch"),
62 "terminated munch");
63 }
64
65 void
run_munch(void)66 run_munch(void)
67 {
68 /*
69 * Allocates anonymous memory using the 'munch' tool to create
70 * memory pressure in the system, so that we can compress to free up
71 * some pages (triggers compressor)
72 */
73 char *munch_args[] =
74 {
75 "/usr/local/bin/munch",
76 "--lim-jetsam=idle",
77 "--type=wired",
78 "--cfg-background",
79 NULL
80 };
81 posix_spawn_then_perform_action_from_process(munch_args,
82 MTE_SPAWN_USE_LEGACY_API, 0);
83 sleep(30);/* Let the system stabilize */
84 }
85
86 void
run_munch_mte(void)87 run_munch_mte(void)
88 {
89 /*
90 * Allocates tagged memory using the 'munch' tool
91 * to trigger tag storage page relocations
92 */
93
94 pid_t pid = fork();
95 if (pid == 0) {
96 execl("/bin/sh",
97 "sh",
98 "-c",
99 "taskpolicy -S explicit-enable-inherit munch "
100 "--block=1 --type=malloc 10g",
101 (char*)NULL);
102 perror("execl");
103 exit(EXIT_FAILURE);
104 } else if (pid < 0) {
105 perror("fork");
106 exit(EXIT_FAILURE);
107 }
108 sleep(30);
109 kill(pid, SIGTERM);
110 sleep(10);
111 kill(pid, SIGKILL);
112 waitpid(pid, NULL, 0);
113 }
114
115 T_DECL(vm_mte_ts_for_tagged_malloc,
116 "Make sure we don't use tag storage page for tagged malloc heap",
117 T_META_BOOTARGS_SET("mte_ts_vm_tag=1_4,7_9,11_12"),
118 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
119 T_META_ASROOT(true),
120 XNU_T_META_SOC_SPECIFIC)
121 {
122 struct {
123 uint32_t vm_tag;
124 bool mte;
125 bool expect_ts;
126 } args = {VM_MEMORY_MALLOC_SMALL, true, false};
127 T_EXPECT_EQ_INT(1,
128 (int)run_sysctl_test("vm_mte_tag_storage_for_vm_tag", (int64_t)(&args)),
129 "didn't get tag storage page for allocation");
130 }
131
132 T_DECL(test_mte_tag_storage_reclamation,
133 "test verifies correct allocation and reclamation of tag storage pages"
134 "in the compressor pool controlled by a boot-arg."
135 "For test, we take initial readings of vm.mte.compress_ts_pages_used,"
136 "vm.mte.cell.inactive,and vm.mte.tag_storage.compressor_relocations."
137 "Then we allocate anonymous memory to trigger compressor via munch usage"
138 "(vm.mte.compress_ts_pages_used should increase;"
139 "vm.mte.cell.inactive should decrease). Then we add tagged memory pressure"
140 "to initiate the fill thread (vm.mte.tag_storage.compressor_relocations"
141 "and vm.mte.cell.active should increase,"
142 "vm.mte.compress_ts_pages_used should decrease ).",
143 T_META_BOOTARGS_SET("mte_ts_vmtag=2,7,11"),
144 T_META_BOOTARGS_SET("mte_ts_compressor=1"),
145 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE2", 1),
146 XNU_T_META_SOC_SPECIFIC,
147 T_META_ASROOT(true),
148 T_META_ENABLED(!TARGET_OS_XR) /* rdar://165838166 */)
149 {
150 T_ATEND(tear_down);
151 /*
152 * Allocate a significant amount of wired memory to trigger compressor use,
153 * Check if the compressor is actively compressing pages,
154 * "vm.mte.compress_ts_pages_used" is increasing,
155 * vm.mte.cell.inactive" is decreasing
156 */
157 int64_t compress_to_pages_used_initial =
158 sysctl_get_Q("vm.mte.compress_ts_pages_used");
159 int64_t inactive_pages_initial = sysctl_get_Q("vm.mte.cell.inactive");
160 int64_t relocation_pages_initial =
161 sysctl_get_Q("vm.mte.tag_storage.compressor_relocations");
162
163 run_munch();
164
165 int64_t compress_to_pages_used_final_1 =
166 sysctl_get_Q("vm.mte.compress_ts_pages_used");
167 T_QUIET; T_EXPECT_GT(compress_to_pages_used_final_1,
168 compress_to_pages_used_initial,
169 "vm.mte.compress_ts_pages_used is behaving as expected (increasing)");
170 sleep(15);
171 int64_t inactive_pages_final = sysctl_get_Q("vm.mte.cell.inactive");
172 T_QUIET; T_EXPECT_LT(inactive_pages_final, inactive_pages_initial,
173 "vm.mte.cell.inactive is behaving as expected (decreasing)");
174
175 /*
176 * Track active page counts pre- and post-tagged allocation to
177 * compare for increase
178 */
179 int64_t active_pages_initial = sysctl_get_Q("vm.mte.cell.active");
180
181 /*
182 * Allocate heavy tagged memory to trigger fill thread,
183 * vm.mte.tag_storage.compressor_relocations to increase
184 */
185 run_munch_mte();
186
187 int64_t relocation_pages_final =
188 sysctl_get_Q("vm.mte.tag_storage.compressor_relocations");
189 T_QUIET; T_EXPECT_GT(relocation_pages_final, relocation_pages_initial,
190 "vm.mte.cell.relocation is behaving as expected (increasing)");
191 int64_t active_pages_final = sysctl_get_Q("vm.mte.cell.active");
192 T_QUIET; T_EXPECT_GT(active_pages_final, active_pages_initial,
193 "vm.mte.cell.active is behaving as expected (increasing)");
194 }
195 #endif /* __arm64__ */
196