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