5 #include <sys/kern_sysctl.h>
8 #include <darwintest.h>
9 #include <darwintest_utils.h>
12 static const char *g_sysctl_no_wire_name
= "vm.global_no_user_wire_amount";
13 static const char *g_sysctl_wire_name
= "vm.global_user_wire_limit";
14 static const char *g_sysctl_per_task_wire_name
= "vm.user_wire_limit";
15 static const char *g_sysctl_current_wired_count_name
= "vm.page_wire_count";
16 static const char *g_sysctl_current_free_count_name
= "vm.lopage_free_count";
17 static const char *g_sysctl_vm_page_size_name
= "vm.pagesize";
18 static const char *g_sysctl_memsize_name
= "hw.memsize";
21 ptoa(size_t num_pages
)
23 static size_t page_size
= 0;
25 size_t page_size_size
= sizeof(page_size
);
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");
30 return num_pages
* (size_t) page_size
;
34 T_DECL(global_no_user_wire_amount
, "no_user_wire_amount <= 32G") {
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.");
43 T_DECL(user_wire_amount
, "max_mem > user_wire_amount >= 0.7 * 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.");
58 * Sets the no wire limit, and ensures that the wire_limit
62 set_no_wire_limit(vm_map_size_t value
, uint64_t max_mem
)
65 size_t wire_size
= sizeof(wire
);
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");
75 * Sets the wire limit, and ensures that the no_wire_limit
79 set_wire_limit(vm_map_size_t value
, uint64_t max_mem
)
81 vm_map_size_t no_wire
;
82 size_t no_wire_size
= sizeof(no_wire
);
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");
91 T_DECL(set_global_no_user_wire_amount
, "Setting no_user_wire_amount changes global_user_wire_amount", T_META_ASROOT(true)) {
93 vm_map_size_t no_wire
, wire
;
94 vm_map_size_t no_wire_delta
= 16 * (1 << 10);
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");
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
);
115 T_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
);
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.");
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");
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.
141 wire_to_limit(size_t limit
, size_t *size
)
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;
148 unsigned int current_wired
, current_free
;
149 size_t buffer_size
, offset_from_limit
;
151 size_t current_wired_size
= sizeof(current_wired
);
152 size_t current_free_size
= sizeof(current_free
);
154 ret
= sysctlbyname(g_sysctl_current_wired_count_name
, ¤t_wired
, ¤t_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
, ¤t_free
, ¤t_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
);
173 T_DECL(wire_stress_test
, "wire up to global_user_wire_limit and spin for 120 seconds.") {
174 static const int kNumSecondsToSpin
= 120;
176 struct timespec start
, now
;
179 size_t wire_limit_size
= sizeof(wire_limit
);
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.");
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
) {
194 ret
= munlock(buffer
, buffer_size
);
195 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "Unable to unlock memory.");
198 #endif /* TARGET_OS_OSX */