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