xref: /xnu-12377.81.4/tests/coalition_policy.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2024 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 #include <sys/coalition_private.h>
30 
31 #include <mach/coalition.h>
32 #include <sys/coalition.h>
33 #include <libproc.h>
34 
35 #include <sys/types.h>
36 #include <unistd.h>
37 
38 #include <darwintest.h>
39 #include <darwintest_utils.h>
40 #include <mach/task.h>
41 #include <mach/task_policy.h>
42 #include <mach/mach.h>
43 
44 #include <sched/sched_test_utils.h>
45 
46 T_GLOBAL_META(T_META_NAMESPACE("xnu.scheduler"),
47     T_META_RADAR_COMPONENT_NAME("xnu"),
48     T_META_RADAR_COMPONENT_VERSION("scheduler"),
49     T_META_OWNER("chimene"),
50     T_META_RUN_CONCURRENTLY(false));
51 
52 static uint64_t
get_jet_id(void)53 get_jet_id(void)
54 {
55 	T_LOG("uid: %d, pid %d", getuid(), getpid());
56 
57 	struct proc_pidcoalitioninfo idinfo;
58 
59 	int ret = proc_pidinfo(getpid(), PROC_PIDCOALITIONINFO, 0,
60 	    &idinfo, sizeof(idinfo));
61 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(... PROC_PIDCOALITIONINFO ...)");
62 
63 	uint64_t res_id = idinfo.coalition_id[COALITION_TYPE_RESOURCE];
64 	uint64_t jet_id = idinfo.coalition_id[COALITION_TYPE_JETSAM];
65 
66 	T_LOG("Resource coalition: %lld, Jetsam coalition: %lld", res_id, jet_id);
67 
68 	return jet_id;
69 }
70 
71 static void
check_is_bg(bool wants_bg)72 check_is_bg(bool wants_bg)
73 {
74 	kern_return_t kr;
75 	struct task_policy_state policy_state;
76 
77 	mach_msg_type_number_t count = TASK_POLICY_STATE_COUNT;
78 	boolean_t get_default = FALSE;
79 
80 	kr = task_policy_get(mach_task_self(), TASK_POLICY_STATE,
81 	    (task_policy_t)&policy_state, &count, &get_default);
82 
83 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "task_policy_get(TASK_POLICY_STATE)");
84 
85 	/*
86 	 * A test reporting type=APPLICATION should have the live donor bit set.
87 	 * If this fails, the test may have been launched as a daemon instead.
88 	 */
89 	T_QUIET; T_ASSERT_BITS_SET(policy_state.flags, TASK_IMP_LIVE_DONOR, "test should be live donor enabled");
90 
91 	/*
92 	 * The BG bit is updated via task_policy_update_internal_locked,
93 	 * checking this proves that the first phase update ran on this task.
94 	 */
95 	if (wants_bg) {
96 		T_ASSERT_BITS_SET(policy_state.effective, POLICY_EFF_DARWIN_BG, "%d: is BG", getpid());
97 	} else {
98 		T_ASSERT_BITS_NOTSET(policy_state.effective, POLICY_EFF_DARWIN_BG, "%d: is not BG", getpid());
99 	}
100 
101 	/*
102 	 * The live donor bit is updated via task_policy_update_complete_unlocked,
103 	 * checking this proves that the second phase update ran on this task.
104 	 */
105 	if (wants_bg) {
106 		T_ASSERT_BITS_NOTSET(policy_state.flags, TASK_IMP_DONOR, "%d: is not live donor", getpid());
107 	} else {
108 		T_ASSERT_BITS_SET(policy_state.flags, TASK_IMP_DONOR, "%d: is live donor", getpid());
109 	}
110 }
111 
112 static void
set_coalition_bg(uint64_t jet_id,bool set_bg)113 set_coalition_bg(uint64_t jet_id, bool set_bg)
114 {
115 	if (set_bg) {
116 		T_ASSERT_POSIX_SUCCESS(coalition_policy_set(jet_id, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_DARWIN_BG),
117 		    "coalition_policy_set(%lld, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_DARWIN_BG)", jet_id);
118 	} else {
119 		T_ASSERT_POSIX_SUCCESS(coalition_policy_set(jet_id, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE),
120 		    "coalition_policy_set(%lld, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE)", jet_id);
121 	}
122 }
123 
124 static void
log_suppress(uint64_t jet_id)125 log_suppress(uint64_t jet_id)
126 {
127 	int suppress = coalition_policy_get(jet_id, COALITION_POLICY_SUPPRESS);
128 	T_ASSERT_POSIX_SUCCESS(suppress, "coalition_policy_get(%lld, COALITION_POLICY_SUPPRESS)", jet_id);
129 	T_LOG("suppress: %d", suppress);
130 }
131 
132 static void
restore_coalition_state(void)133 restore_coalition_state(void)
134 {
135 	struct proc_pidcoalitioninfo idinfo;
136 
137 	int ret = proc_pidinfo(getpid(), PROC_PIDCOALITIONINFO, 0,
138 	    &idinfo, sizeof(idinfo));
139 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(... PROC_PIDCOALITIONINFO ...)");
140 
141 	uint64_t jet_id = idinfo.coalition_id[COALITION_TYPE_JETSAM];
142 
143 	T_QUIET; T_ASSERT_POSIX_SUCCESS(coalition_policy_set(jet_id, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE),
144 	    "coalition_policy_set(%lld, COALITION_POLICY_SUPPRESS, COALITION_POLICY_SUPPRESS_NONE)", jet_id);
145 }
146 
147 static void
quiesce(int argc,char * const * argv)148 quiesce(int argc, char *const *argv)
149 {
150 	if (!wait_for_quiescence_default(argc, argv)) {
151 		T_LOG("WARN: System did not quiesce. BG threads may experience starvation, causing this test to fail.");
152 	}
153 }
154 
155 T_DECL(coalition_suppress_read_entitled, "COALITION_POLICY_SUPPRESS should be readable with entitlement")
156 {
157 	T_ATEND(restore_coalition_state);
158 
159 	uint64_t jet_id = get_jet_id();
160 
161 	int suppress = coalition_policy_get(jet_id, COALITION_POLICY_SUPPRESS);
162 
163 	T_ASSERT_POSIX_SUCCESS(suppress, "coalition_policy_get(%lld, COALITION_POLICY_SUPPRESS)", jet_id);
164 
165 	T_LOG("suppress: %d", suppress);
166 }
167 
168 T_DECL(coalition_suppress_read_rsrc_coalition, "COALITION_POLICY_SUPPRESS shouldn't work on resource coalitions")
169 {
170 	T_ATEND(restore_coalition_state);
171 
172 	T_LOG("uid: %d, pid %d", getuid(), getpid());
173 
174 	struct proc_pidcoalitioninfo idinfo;
175 
176 	int ret = proc_pidinfo(getpid(), PROC_PIDCOALITIONINFO, 0,
177 	    &idinfo, sizeof(idinfo));
178 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(... PROC_PIDCOALITIONINFO ...)");
179 
180 	uint64_t res_id = idinfo.coalition_id[COALITION_TYPE_RESOURCE];
181 	uint64_t jet_id = idinfo.coalition_id[COALITION_TYPE_JETSAM];
182 
183 	T_LOG("res_id: %lld, jet_id: %lld", res_id, jet_id);
184 
185 	int suppress = coalition_policy_get(res_id, COALITION_POLICY_SUPPRESS);
186 
187 	T_EXPECT_POSIX_FAILURE(suppress,
188 	    ENOTSUP, "coalition_policy_get(%lld, COALITION_POLICY_SUPPRESS)", res_id);
189 
190 	T_LOG("suppress: %d", suppress);
191 }
192 
193 T_DECL(coalition_suppress_set, "COALITION_POLICY_SUPPRESS should be settable with entitlement")
194 {
195 	T_ATEND(restore_coalition_state);
196 	quiesce(argc, argv);
197 
198 	uint64_t jet_id = get_jet_id();
199 
200 	set_coalition_bg(jet_id, true);
201 
202 	log_suppress(jet_id);
203 
204 	set_coalition_bg(jet_id, false);
205 
206 	log_suppress(jet_id);
207 }
208 
209 T_DECL(coalition_suppress_set_check_task, "current task should become BG when coalition changes", T_META_ASROOT(true))
210 {
211 	T_ATEND(restore_coalition_state);
212 	quiesce(argc, argv);
213 
214 	uint64_t jet_id = get_jet_id();
215 
216 	log_suppress(jet_id);
217 
218 	check_is_bg(false);
219 
220 	set_coalition_bg(jet_id, true);
221 
222 	log_suppress(jet_id);
223 
224 	check_is_bg(true);
225 
226 	set_coalition_bg(jet_id, false);
227 
228 	log_suppress(jet_id);
229 
230 	check_is_bg(false);
231 }
232 
233 T_DECL(coalition_suppress_child_bg, "child spawned into bg coalition should be bg", T_META_ASROOT(true))
234 {
235 	T_ATEND(restore_coalition_state);
236 	quiesce(argc, argv);
237 
238 	uint64_t jet_id = get_jet_id();
239 
240 	check_is_bg(false);
241 
242 	set_coalition_bg(jet_id, true);
243 
244 	check_is_bg(true);
245 
246 	T_LOG("Spawning child");
247 
248 	pid_t child_pid = fork();
249 
250 	if (child_pid == 0) {
251 		/* child process */
252 
253 		//T_LOG("child pid %d sleeping", getpid());
254 
255 		//sleep(10000);
256 
257 		check_is_bg(true);
258 
259 		T_LOG("Exit pid %d", getpid());
260 
261 		exit(0);
262 	} else {
263 		T_ASSERT_POSIX_SUCCESS(child_pid, "fork returned, child pid %d", child_pid);
264 
265 		/* wait for child process to exit */
266 		int exit_status = 0, signum = 0;
267 
268 		T_ASSERT_TRUE(dt_waitpid(child_pid, &exit_status, &signum, 500000), /* TODO */
269 		    "wait for child (%d) complete", child_pid);
270 
271 		T_QUIET; T_ASSERT_EQ(exit_status, 0, "dt_waitpid: exit_status");
272 		T_QUIET; T_ASSERT_EQ(signum, 0, "dt_waitpid: signum");
273 	}
274 
275 	check_is_bg(true);
276 
277 	set_coalition_bg(jet_id, false);
278 
279 	check_is_bg(false);
280 }
281 
282 T_DECL(coalition_suppress_child_change_bg, "child changing coalition to bg should affect parent", T_META_ASROOT(true))
283 {
284 	T_ATEND(restore_coalition_state);
285 	quiesce(argc, argv);
286 
287 	uint64_t jet_id = get_jet_id();
288 
289 	check_is_bg(false);
290 
291 	T_LOG("Spawning child");
292 
293 	pid_t child_pid = fork();
294 
295 	if (child_pid == 0) {
296 		/* child process */
297 
298 		check_is_bg(false);
299 
300 		set_coalition_bg(jet_id, true);
301 
302 		check_is_bg(true);
303 
304 		T_LOG("Exit pid %d", getpid());
305 
306 		exit(0);
307 	} else {
308 		T_ASSERT_POSIX_SUCCESS(child_pid, "fork returned, child pid %d", child_pid);
309 
310 		/* wait for child process to exit */
311 		int exit_status = 0, signum = 0;
312 
313 		T_ASSERT_TRUE(dt_waitpid(child_pid, &exit_status, &signum, 5),
314 		    "wait for child (%d) complete", child_pid);
315 
316 		T_QUIET; T_ASSERT_EQ(exit_status, 0, "dt_waitpid: exit_status");
317 		T_QUIET; T_ASSERT_EQ(signum, 0, "dt_waitpid: signum");
318 	}
319 
320 	check_is_bg(true);
321 
322 	set_coalition_bg(jet_id, false);
323 
324 	check_is_bg(false);
325 }
326