1*bbb1b6f9SApple OSS Distributions #include <time.h>
2*bbb1b6f9SApple OSS Distributions #include <errno.h>
3*bbb1b6f9SApple OSS Distributions
4*bbb1b6f9SApple OSS Distributions #include <mach/mach.h>
5*bbb1b6f9SApple OSS Distributions #include <sys/kern_sysctl.h>
6*bbb1b6f9SApple OSS Distributions #include <sys/mman.h>
7*bbb1b6f9SApple OSS Distributions
8*bbb1b6f9SApple OSS Distributions #include <darwintest.h>
9*bbb1b6f9SApple OSS Distributions #include <darwintest_utils.h>
10*bbb1b6f9SApple OSS Distributions
11*bbb1b6f9SApple OSS Distributions
12*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_no_wire_name = "vm.global_no_user_wire_amount";
13*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_wire_name = "vm.global_user_wire_limit";
14*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_per_task_wire_name = "vm.user_wire_limit";
15*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_current_wired_count_name = "vm.page_wire_count";
16*bbb1b6f9SApple OSS Distributions #if __x86_64__
17*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_current_free_count_name = "vm.lopage_free_count";
18*bbb1b6f9SApple OSS Distributions #endif /* __x86_64__ */
19*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_vm_page_size_name = "vm.pagesize";
20*bbb1b6f9SApple OSS Distributions static const char *g_sysctl_memsize_name = "hw.memsize";
21*bbb1b6f9SApple OSS Distributions
22*bbb1b6f9SApple OSS Distributions static size_t
ptoa(size_t num_pages)23*bbb1b6f9SApple OSS Distributions ptoa(size_t num_pages)
24*bbb1b6f9SApple OSS Distributions {
25*bbb1b6f9SApple OSS Distributions static size_t page_size = 0;
26*bbb1b6f9SApple OSS Distributions int ret;
27*bbb1b6f9SApple OSS Distributions size_t page_size_size = sizeof(page_size);
28*bbb1b6f9SApple OSS Distributions if (page_size == 0) {
29*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_vm_page_size_name, &page_size, &page_size_size, NULL, 0);
30*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Unable to get page size");
31*bbb1b6f9SApple OSS Distributions }
32*bbb1b6f9SApple OSS Distributions return num_pages * (size_t) page_size;
33*bbb1b6f9SApple OSS Distributions }
34*bbb1b6f9SApple OSS Distributions
35*bbb1b6f9SApple OSS Distributions
36*bbb1b6f9SApple OSS Distributions T_DECL(global_no_user_wire_amount, "no_user_wire_amount <= 32G", T_META_TAG_VM_PREFERRED) {
37*bbb1b6f9SApple OSS Distributions int ret;
38*bbb1b6f9SApple OSS Distributions vm_map_size_t no_wire;
39*bbb1b6f9SApple OSS Distributions size_t no_wire_size = sizeof(no_wire);
40*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_no_wire_name, &no_wire, &no_wire_size, NULL, 0);
41*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "no_user_wire sysctl failed");
42*bbb1b6f9SApple OSS Distributions T_QUIET; T_EXPECT_LE(no_wire, 32 * 2ULL << 30, "no_user_wire_amount is too big.");
43*bbb1b6f9SApple OSS Distributions }
44*bbb1b6f9SApple OSS Distributions
45*bbb1b6f9SApple OSS Distributions T_DECL(user_wire_amount, "max_mem > user_wire_amount >= 0.7 * max_mem", T_META_TAG_VM_NOT_PREFERRED) {
46*bbb1b6f9SApple OSS Distributions int ret;
47*bbb1b6f9SApple OSS Distributions vm_map_size_t wire;
48*bbb1b6f9SApple OSS Distributions uint64_t max_mem;
49*bbb1b6f9SApple OSS Distributions size_t max_mem_size = sizeof(max_mem);
50*bbb1b6f9SApple OSS Distributions size_t wire_size = sizeof(wire);
51*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_memsize_name, &max_mem, &max_mem_size, NULL, 0);
52*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "memsize sysctl failed");
53*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_wire_name, &wire, &wire_size, NULL, 0);
54*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl failed");
55*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_LT(wire, max_mem, "wire limit is too big");
56*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_GE(wire, max_mem * 70 / 100, "wire limit is too small.");
57*bbb1b6f9SApple OSS Distributions }
58*bbb1b6f9SApple OSS Distributions
59*bbb1b6f9SApple OSS Distributions /*
60*bbb1b6f9SApple OSS Distributions * Sets the no wire limit, and ensures that the wire_limit
61*bbb1b6f9SApple OSS Distributions * changes correctly.
62*bbb1b6f9SApple OSS Distributions */
63*bbb1b6f9SApple OSS Distributions static void
set_no_wire_limit(vm_map_size_t value,uint64_t max_mem)64*bbb1b6f9SApple OSS Distributions set_no_wire_limit(vm_map_size_t value, uint64_t max_mem)
65*bbb1b6f9SApple OSS Distributions {
66*bbb1b6f9SApple OSS Distributions vm_map_size_t wire;
67*bbb1b6f9SApple OSS Distributions size_t wire_size = sizeof(wire);
68*bbb1b6f9SApple OSS Distributions int ret;
69*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_no_wire_name, NULL, 0, &value, sizeof(value));
70*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "no_user_wire sysctl set failed");
71*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_wire_name, &wire, &wire_size, NULL, 0);
72*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl failed");
73*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(max_mem - wire, value, "no wire size is incorrect");
74*bbb1b6f9SApple OSS Distributions }
75*bbb1b6f9SApple OSS Distributions
76*bbb1b6f9SApple OSS Distributions /*
77*bbb1b6f9SApple OSS Distributions * Sets the wire limit, and ensures that the no_wire_limit
78*bbb1b6f9SApple OSS Distributions * changes correctly.
79*bbb1b6f9SApple OSS Distributions */
80*bbb1b6f9SApple OSS Distributions static void
set_wire_limit(vm_map_size_t value,uint64_t max_mem)81*bbb1b6f9SApple OSS Distributions set_wire_limit(vm_map_size_t value, uint64_t max_mem)
82*bbb1b6f9SApple OSS Distributions {
83*bbb1b6f9SApple OSS Distributions vm_map_size_t no_wire;
84*bbb1b6f9SApple OSS Distributions size_t no_wire_size = sizeof(no_wire);
85*bbb1b6f9SApple OSS Distributions int ret;
86*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_wire_name, NULL, 0, &value, sizeof(value));
87*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl set failed");
88*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_no_wire_name, &no_wire, &no_wire_size, NULL, 0);
89*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "no_user_wire sysctl failed");
90*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(max_mem - value, no_wire, "no wire size is incorrect");
91*bbb1b6f9SApple OSS Distributions }
92*bbb1b6f9SApple OSS Distributions
93*bbb1b6f9SApple OSS Distributions T_DECL(set_global_no_user_wire_amount, "Setting no_user_wire_amount changes global_user_wire_amount", T_META_ASROOT(true), T_META_TAG_VM_PREFERRED) {
94*bbb1b6f9SApple OSS Distributions int ret;
95*bbb1b6f9SApple OSS Distributions vm_map_size_t no_wire, wire;
96*bbb1b6f9SApple OSS Distributions vm_map_size_t no_wire_delta = 16 * (1 << 10);
97*bbb1b6f9SApple OSS Distributions uint64_t max_mem;
98*bbb1b6f9SApple OSS Distributions size_t no_wire_size = sizeof(no_wire);
99*bbb1b6f9SApple OSS Distributions size_t wire_size = sizeof(wire);
100*bbb1b6f9SApple OSS Distributions size_t max_mem_size = sizeof(max_mem);
101*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_memsize_name, &max_mem, &max_mem_size, NULL, 0);
102*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "max_mem sysctl failed");
103*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_no_wire_name, &no_wire, &no_wire_size, NULL, 0);
104*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "no_user_wire sysctl failed");
105*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_wire_name, &wire, &wire_size, NULL, 0);
106*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl failed");
107*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(max_mem - wire, no_wire, "no wire size is incorrect");
108*bbb1b6f9SApple OSS Distributions
109*bbb1b6f9SApple OSS Distributions // Set the no_wire limit and ensure that the wire_size changed.
110*bbb1b6f9SApple OSS Distributions set_no_wire_limit(no_wire + no_wire_delta, max_mem);
111*bbb1b6f9SApple OSS Distributions set_no_wire_limit(no_wire, max_mem);
112*bbb1b6f9SApple OSS Distributions // Set the wire limit and ensure that the no_wire_limit has changed
113*bbb1b6f9SApple OSS Distributions set_wire_limit(wire - no_wire_delta, max_mem);
114*bbb1b6f9SApple OSS Distributions set_wire_limit(wire, max_mem);
115*bbb1b6f9SApple OSS Distributions }
116*bbb1b6f9SApple OSS Distributions
117*bbb1b6f9SApple OSS Distributions T_DECL(set_user_wire_limit, "Set user_wire_limit", T_META_ASROOT(true), T_META_TAG_VM_PREFERRED) {
118*bbb1b6f9SApple OSS Distributions vm_map_size_t wire, original_wire;
119*bbb1b6f9SApple OSS Distributions size_t wire_size = sizeof(wire);
120*bbb1b6f9SApple OSS Distributions int ret;
121*bbb1b6f9SApple OSS Distributions vm_map_size_t wire_delta = 48 * (1 << 10);
122*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_per_task_wire_name, &original_wire, &wire_size, NULL, 0);
123*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl get failed");
124*bbb1b6f9SApple OSS Distributions wire = original_wire + wire_delta;
125*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_per_task_wire_name, NULL, 0, &wire, wire_size);
126*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl set failed");
127*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_per_task_wire_name, &wire, &wire_size, NULL, 0);
128*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl get failed");
129*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(wire, original_wire + wire_delta, "user_wire sysctl didn't set the correct value.");
130*bbb1b6f9SApple OSS Distributions
131*bbb1b6f9SApple OSS Distributions // Cleanup
132*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_per_task_wire_name, NULL, 0, &original_wire, wire_size);
133*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl set failed");
134*bbb1b6f9SApple OSS Distributions }
135*bbb1b6f9SApple OSS Distributions
136*bbb1b6f9SApple OSS Distributions #if TARGET_OS_OSX
137*bbb1b6f9SApple OSS Distributions /*
138*bbb1b6f9SApple OSS Distributions * Test that wiring up to the limit doesn't hang the system.
139*bbb1b6f9SApple OSS Distributions * We only test this on OS X. On all other platforms, we'd expect
140*bbb1b6f9SApple OSS Distributions * to get jetsamm'ed for doing this.
141*bbb1b6f9SApple OSS Distributions */
142*bbb1b6f9SApple OSS Distributions static void *
wire_to_limit(size_t limit,size_t * size)143*bbb1b6f9SApple OSS Distributions wire_to_limit(size_t limit, size_t *size)
144*bbb1b6f9SApple OSS Distributions {
145*bbb1b6f9SApple OSS Distributions // Trying to wire directly to the limit is likely to fail
146*bbb1b6f9SApple OSS Distributions // repeatedly since other wired pages are probably coming and going
147*bbb1b6f9SApple OSS Distributions // so we just try to get close.
148*bbb1b6f9SApple OSS Distributions const unsigned int wiggle_room_pages = 1000;
149*bbb1b6f9SApple OSS Distributions int ret;
150*bbb1b6f9SApple OSS Distributions unsigned int current_wired, current_free;
151*bbb1b6f9SApple OSS Distributions size_t buffer_size, offset_from_limit;
152*bbb1b6f9SApple OSS Distributions void *buffer;
153*bbb1b6f9SApple OSS Distributions size_t current_wired_size = sizeof(current_wired);
154*bbb1b6f9SApple OSS Distributions #if __x86_64__
155*bbb1b6f9SApple OSS Distributions size_t current_free_size = sizeof(current_free);
156*bbb1b6f9SApple OSS Distributions #endif /* __x86_64__ */
157*bbb1b6f9SApple OSS Distributions while (true) {
158*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_current_wired_count_name, ¤t_wired, ¤t_wired_size, NULL, 0);
159*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "get current wired count failed");
160*bbb1b6f9SApple OSS Distributions #if __x86_64__
161*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_current_free_count_name, ¤t_free, ¤t_free_size, NULL, 0);
162*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "get current free count failed");
163*bbb1b6f9SApple OSS Distributions #else
164*bbb1b6f9SApple OSS Distributions current_free = 0;
165*bbb1b6f9SApple OSS Distributions #endif /* __x86_64__ */
166*bbb1b6f9SApple OSS Distributions offset_from_limit = ptoa(current_wired + current_free + wiggle_room_pages);
167*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_GE(limit, offset_from_limit, "more pages are wired than the limit.");
168*bbb1b6f9SApple OSS Distributions buffer_size = limit - offset_from_limit;
169*bbb1b6f9SApple OSS Distributions buffer = malloc(buffer_size);
170*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(buffer, "Unable to allocate buffer");
171*bbb1b6f9SApple OSS Distributions ret = mlock(buffer, buffer_size);
172*bbb1b6f9SApple OSS Distributions if (ret == 0) {
173*bbb1b6f9SApple OSS Distributions break;
174*bbb1b6f9SApple OSS Distributions }
175*bbb1b6f9SApple OSS Distributions free(buffer);
176*bbb1b6f9SApple OSS Distributions }
177*bbb1b6f9SApple OSS Distributions *size = buffer_size;
178*bbb1b6f9SApple OSS Distributions return buffer;
179*bbb1b6f9SApple OSS Distributions }
180*bbb1b6f9SApple OSS Distributions
181*bbb1b6f9SApple OSS Distributions T_DECL(wire_stress_test, "wire up to global_user_wire_limit and spin for 120 seconds.",
182*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_NE("kern.hv_vmm_present", 1),
183*bbb1b6f9SApple OSS Distributions T_META_TAG_VM_NOT_ELIGIBLE,
184*bbb1b6f9SApple OSS Distributions T_META_ENABLED(false) /* rdar://145613247 */)
185*bbb1b6f9SApple OSS Distributions {
186*bbb1b6f9SApple OSS Distributions static const int kNumSecondsToSpin = 120;
187*bbb1b6f9SApple OSS Distributions int ret;
188*bbb1b6f9SApple OSS Distributions struct timespec start, now;
189*bbb1b6f9SApple OSS Distributions size_t buffer_size;
190*bbb1b6f9SApple OSS Distributions size_t wire_limit;
191*bbb1b6f9SApple OSS Distributions size_t wire_limit_size = sizeof(wire_limit);
192*bbb1b6f9SApple OSS Distributions void *buffer;
193*bbb1b6f9SApple OSS Distributions
194*bbb1b6f9SApple OSS Distributions ret = sysctlbyname(g_sysctl_wire_name, &wire_limit, &wire_limit_size, NULL, 0);
195*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "user_wire sysctl failed");
196*bbb1b6f9SApple OSS Distributions buffer = wire_to_limit(wire_limit, &buffer_size);
197*bbb1b6f9SApple OSS Distributions ret = clock_gettime(CLOCK_MONOTONIC, &start);
198*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Unable to get current time.");
199*bbb1b6f9SApple OSS Distributions while (true) {
200*bbb1b6f9SApple OSS Distributions ret = clock_gettime(CLOCK_MONOTONIC, &now);
201*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Unable to get current time.");
202*bbb1b6f9SApple OSS Distributions if (now.tv_sec - start.tv_sec >= kNumSecondsToSpin) {
203*bbb1b6f9SApple OSS Distributions break;
204*bbb1b6f9SApple OSS Distributions }
205*bbb1b6f9SApple OSS Distributions }
206*bbb1b6f9SApple OSS Distributions ret = munlock(buffer, buffer_size);
207*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Unable to unlock memory.");
208*bbb1b6f9SApple OSS Distributions free(buffer);
209*bbb1b6f9SApple OSS Distributions }
210*bbb1b6f9SApple OSS Distributions #endif /* TARGET_OS_OSX */
211