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