1 // Copyright (c) 2024 Apple Inc. All rights reserved.
2
3 #include <stdint.h>
4 #include <stdbool.h>
5
6 /* Edge shares some of its implementation with the Clutch scheduler */
7 #include "sched_clutch_harness_impl.c"
8
9 /* Machine-layer mocking */
10
11 processor_t
current_processor(void)12 current_processor(void)
13 {
14 return cpus[_curr_cpu];
15 }
16
17 unsigned int
ml_get_die_id(unsigned int cluster_id)18 ml_get_die_id(unsigned int cluster_id)
19 {
20 return curr_hw_topo.psets[cluster_id].die_id;
21 }
22
23 uint64_t
ml_cpu_signal_deferred_get_timer(void)24 ml_cpu_signal_deferred_get_timer(void)
25 {
26 /* Matching deferred_ipi_timer_ns */
27 return 64 * NSEC_PER_USEC;
28 }
29
30 static test_cpu_type_t
cluster_type_to_test_cpu_type(cluster_type_t cluster_type)31 cluster_type_to_test_cpu_type(cluster_type_t cluster_type)
32 {
33 return (test_cpu_type_t)(cluster_type - 1);
34 }
35
36 static unsigned int cpu_count_for_type[TEST_CPU_TYPE_MAX] = { 0 };
37 static unsigned int recommended_cpu_count_for_type[TEST_CPU_TYPE_MAX] = { 0 };
38
39 unsigned int
ml_get_cpu_number_type(cluster_type_t cluster_type,bool logical,bool available)40 ml_get_cpu_number_type(cluster_type_t cluster_type, bool logical, bool available)
41 {
42 (void)logical;
43 if (available) {
44 return recommended_cpu_count_for_type[cluster_type_to_test_cpu_type(cluster_type)];
45 } else {
46 return cpu_count_for_type[cluster_type_to_test_cpu_type(cluster_type)];
47 }
48 }
49
50 static unsigned int cluster_count_for_type[TEST_CPU_TYPE_MAX] = { 0 };
51
52 unsigned int
ml_get_cluster_number_type(cluster_type_t cluster_type)53 ml_get_cluster_number_type(cluster_type_t cluster_type)
54 {
55 return cluster_count_for_type[cluster_type_to_test_cpu_type(cluster_type)];
56 }
57
58 int sched_amp_spill_deferred_ipi = 1;
59 int sched_amp_pcores_preempt_immediate_ipi = 1;
60
61 sched_ipi_type_t
sched_ipi_action(processor_t dst,thread_t thread,sched_ipi_event_t event)62 sched_ipi_action(processor_t dst, thread_t thread, sched_ipi_event_t event)
63 {
64 /* Forward to the policy-specific implementation */
65 return SCHED(ipi_policy)(dst, thread, (dst->active_thread == NULL), event);
66 }
67
68 #define MAX_LOGGED_IPIS 10000
69 typedef struct {
70 int cpu_id;
71 sched_ipi_type_t ipi_type;
72 } logged_ipi_t;
73 static logged_ipi_t logged_ipis[MAX_LOGGED_IPIS];
74 static uint32_t curr_ipi_ind = 0;
75 static uint32_t expect_ipi_ind = 0;
76
77 void
sched_ipi_perform(processor_t dst,sched_ipi_type_t ipi)78 sched_ipi_perform(processor_t dst, sched_ipi_type_t ipi)
79 {
80 /* Record the IPI type and where we sent it */
81 logged_ipis[curr_ipi_ind].cpu_id = dst->cpu_id;
82 logged_ipis[curr_ipi_ind].ipi_type = ipi;
83 curr_ipi_ind++;
84 }
85
86 sched_ipi_type_t
sched_ipi_policy(processor_t dst,thread_t thread,boolean_t dst_idle,sched_ipi_event_t event)87 sched_ipi_policy(processor_t dst, thread_t thread,
88 boolean_t dst_idle, sched_ipi_event_t event)
89 {
90 (void)dst;
91 (void)thread;
92 (void)dst_idle;
93 (void)event;
94 /* For now, only send IPIs based on a policy-specific decision */
95 return SCHED_IPI_NONE;
96 }
97
98 sched_ipi_type_t
sched_ipi_deferred_policy(processor_set_t pset,processor_t dst,thread_t thread,sched_ipi_event_t event)99 sched_ipi_deferred_policy(processor_set_t pset,
100 processor_t dst, thread_t thread, sched_ipi_event_t event)
101 {
102 (void)pset;
103 (void)dst;
104 (void)thread;
105 (void)event;
106 return SCHED_IPI_NONE;
107 }
108
109 /* Implementation of sched_runqueue_harness.h interface */
110
111 static test_pset_t basic_amp_psets[2] = {
112 {
113 .cpu_type = TEST_CPU_TYPE_PERFORMANCE,
114 .num_cpus = 2,
115 .die_id = 0,
116 },
117 {
118 .cpu_type = TEST_CPU_TYPE_EFFICIENCY,
119 .num_cpus = 4,
120 .die_id = 0,
121 },
122 };
123 test_hw_topology_t basic_amp = {
124 .psets = &basic_amp_psets[0],
125 .num_psets = 2,
126 };
127
128 static test_pset_t dual_die_psets[6] = {
129 {
130 .cpu_type = TEST_CPU_TYPE_EFFICIENCY,
131 .num_cpus = 2,
132 .die_id = 0,
133 },
134 {
135 .cpu_type = TEST_CPU_TYPE_PERFORMANCE,
136 .num_cpus = 4,
137 .die_id = 0,
138 },
139 {
140 .cpu_type = TEST_CPU_TYPE_PERFORMANCE,
141 .num_cpus = 4,
142 .die_id = 0,
143 },
144 {
145 .cpu_type = TEST_CPU_TYPE_EFFICIENCY,
146 .num_cpus = 2,
147 .die_id = 1,
148 },
149 {
150 .cpu_type = TEST_CPU_TYPE_PERFORMANCE,
151 .num_cpus = 4,
152 .die_id = 1,
153 },
154 {
155 .cpu_type = TEST_CPU_TYPE_PERFORMANCE,
156 .num_cpus = 4,
157 .die_id = 1,
158 },
159 };
160 test_hw_topology_t dual_die = {
161 .psets = &dual_die_psets[0],
162 .num_psets = 6,
163 };
164
165 #define MAX_NODES 2
166 static struct pset_node node_array[MAX_NODES];
167
168 static void
edge_impl_set_cluster_type(processor_set_t pset,test_cpu_type_t type)169 edge_impl_set_cluster_type(processor_set_t pset, test_cpu_type_t type)
170 {
171 switch (type) {
172 case TEST_CPU_TYPE_EFFICIENCY:
173 pset->pset_cluster_type = PSET_AMP_E;
174 pset->node = &node_array[0];
175 break;
176 case TEST_CPU_TYPE_PERFORMANCE:
177 pset->pset_cluster_type = PSET_AMP_P;
178 pset->node = &node_array[1];
179 break;
180 default:
181 assert(false);
182 break;
183 }
184 }
185
186 static void
edge_impl_init_runqueues(void)187 edge_impl_init_runqueues(void)
188 {
189 assert(curr_hw_topo.num_psets != 0);
190 clutch_impl_init_topology(curr_hw_topo);
191 sched_edge_init();
192 node_array[0].pset_cluster_type = PSET_AMP_E;
193 os_atomic_store(&node_array[0].pset_recommended_map, 0, relaxed);
194 atomic_bit_set(&node_array[0].pset_recommended_map, 0, memory_order_relaxed);
195 node_array[1].pset_cluster_type = PSET_AMP_P;
196 os_atomic_store(&node_array[1].pset_recommended_map, 0, relaxed);
197 atomic_bit_set(&node_array[1].pset_recommended_map, 1, memory_order_relaxed);
198 for (int i = 0; i < curr_hw_topo.num_psets; i++) {
199 pset_array[i] = psets[i];
200 edge_impl_set_cluster_type(psets[i], curr_hw_topo.psets[i].cpu_type);
201 sched_edge_pset_init(psets[i]);
202 bzero(&psets[i]->pset_load_average, sizeof(psets[i]->pset_load_average));
203 bzero(&psets[i]->pset_execution_time, sizeof(psets[i]->pset_execution_time));
204 assert(psets[i]->cpu_bitmask != 0);
205 psets[i]->foreign_psets[0] = 0;
206 psets[i]->native_psets[0] = 0;
207 psets[i]->local_psets[0] = 0;
208 psets[i]->remote_psets[0] = 0;
209 cluster_count_for_type[curr_hw_topo.psets[i].cpu_type]++;
210 cpu_count_for_type[curr_hw_topo.psets[i].cpu_type] += curr_hw_topo.psets[i].num_cpus;
211 recommended_cpu_count_for_type[curr_hw_topo.psets[i].cpu_type] +=
212 curr_hw_topo.psets[i].num_cpus;
213 }
214 for (unsigned int j = 0; j < processor_avail_count; j++) {
215 processor_array[j] = cpus[j];
216 sched_clutch_processor_init(cpus[j]);
217 }
218 sched_edge_cpu_init_completed();
219 increment_mock_time(100);
220 clutch_impl_init_params();
221 clutch_impl_init_tracepoints();
222 }
223
224 void
impl_init_runqueue(void)225 impl_init_runqueue(void)
226 {
227 assert(curr_hw_topo.num_psets == 0);
228 curr_hw_topo = single_core;
229 edge_impl_init_runqueues();
230 }
231
232 void
impl_init_migration_harness(test_hw_topology_t hw_topology)233 impl_init_migration_harness(test_hw_topology_t hw_topology)
234 {
235 assert(curr_hw_topo.num_psets == 0);
236 curr_hw_topo = hw_topology;
237 edge_impl_init_runqueues();
238 }
239
240 struct thread_group *
impl_create_tg(int interactivity_score)241 impl_create_tg(int interactivity_score)
242 {
243 return clutch_impl_create_tg(interactivity_score);
244 }
245
246 test_thread_t
impl_create_thread(int root_bucket,struct thread_group * tg,int pri)247 impl_create_thread(int root_bucket, struct thread_group *tg, int pri)
248 {
249 return clutch_impl_create_thread(root_bucket, tg, pri);
250 }
251
252 void
impl_set_thread_sched_mode(test_thread_t thread,int mode)253 impl_set_thread_sched_mode(test_thread_t thread, int mode)
254 {
255 clutch_impl_set_thread_sched_mode(thread, mode);
256 }
257
258 void
impl_set_thread_processor_bound(test_thread_t thread,int cpu_id)259 impl_set_thread_processor_bound(test_thread_t thread, int cpu_id)
260 {
261 _curr_cpu = cpu_id;
262 clutch_impl_set_thread_processor_bound(thread, cpu_id);
263 }
264
265 void
impl_set_thread_cluster_bound(test_thread_t thread,int cluster_id)266 impl_set_thread_cluster_bound(test_thread_t thread, int cluster_id)
267 {
268 /* Should not be already enqueued */
269 assert(thread_get_runq_locked((thread_t)thread) == NULL);
270 ((thread_t)thread)->th_bound_cluster_id = cluster_id;
271 }
272
273 void
impl_cpu_set_thread_current(int cpu_id,test_thread_t thread)274 impl_cpu_set_thread_current(int cpu_id, test_thread_t thread)
275 {
276 _curr_cpu = cpu_id;
277 clutch_impl_cpu_set_thread_current(cpu_id, thread);
278 }
279
280 void
impl_cpu_clear_thread_current(int cpu_id)281 impl_cpu_clear_thread_current(int cpu_id)
282 {
283 _curr_cpu = cpu_id;
284 clutch_impl_cpu_clear_thread_current(cpu_id);
285 }
286
287 void
impl_cpu_enqueue_thread(int cpu_id,test_thread_t thread)288 impl_cpu_enqueue_thread(int cpu_id, test_thread_t thread)
289 {
290 _curr_cpu = cpu_id;
291 sched_clutch_processor_enqueue(cpus[cpu_id], thread, SCHED_TAILQ);
292 }
293
294 test_thread_t
impl_cpu_dequeue_thread(int cpu_id)295 impl_cpu_dequeue_thread(int cpu_id)
296 {
297 _curr_cpu = cpu_id;
298 return sched_clutch_choose_thread(cpus[cpu_id], MINPRI, NULL, 0);
299 }
300
301 test_thread_t
impl_cpu_dequeue_thread_compare_current(int cpu_id)302 impl_cpu_dequeue_thread_compare_current(int cpu_id)
303 {
304 _curr_cpu = cpu_id;
305 assert(cpus[cpu_id]->active_thread != NULL);
306 return sched_clutch_choose_thread(cpus[cpu_id], MINPRI, cpus[cpu_id]->active_thread, 0);
307 }
308
309 bool
impl_processor_csw_check(int cpu_id)310 impl_processor_csw_check(int cpu_id)
311 {
312 _curr_cpu = cpu_id;
313 assert(cpus[cpu_id]->active_thread != NULL);
314 ast_t preempt_ast = sched_clutch_processor_csw_check(cpus[cpu_id]);
315 return preempt_ast & AST_PREEMPT;
316 }
317
318 void
impl_pop_tracepoint(uint64_t * clutch_trace_code,uint64_t * arg1,uint64_t * arg2,uint64_t * arg3,uint64_t * arg4)319 impl_pop_tracepoint(uint64_t *clutch_trace_code, uint64_t *arg1, uint64_t *arg2,
320 uint64_t *arg3, uint64_t *arg4)
321 {
322 clutch_impl_pop_tracepoint(clutch_trace_code, arg1, arg2, arg3, arg4);
323 }
324
325 int
impl_choose_pset_for_thread(test_thread_t thread)326 impl_choose_pset_for_thread(test_thread_t thread)
327 {
328 /* Begins search starting from current pset */
329 processor_t chosen_processor = sched_edge_choose_processor(
330 current_processor()->processor_set, current_processor(), (thread_t)thread);
331 return chosen_processor->processor_set->pset_id;
332 }
333
334 void
impl_set_current_processor(int cpu_id)335 impl_set_current_processor(int cpu_id)
336 {
337 _curr_cpu = cpu_id;
338 }
339
340 void
impl_set_tg_sched_bucket_preferred_pset(struct thread_group * tg,int sched_bucket,int cluster_id)341 impl_set_tg_sched_bucket_preferred_pset(struct thread_group *tg, int sched_bucket, int cluster_id)
342 {
343 assert(sched_bucket > 0 && sched_bucket < TH_BUCKET_SCHED_MAX);
344 sched_clutch_t clutch = sched_clutch_for_thread_group(tg);
345 bitmap_t modify_bitmap[BITMAP_LEN(TH_BUCKET_SCHED_MAX)] = {0};
346 bitmap_set(modify_bitmap, sched_bucket);
347 uint32_t tg_bucket_preferred_cluster[TH_BUCKET_SCHED_MAX] = {0};
348 tg_bucket_preferred_cluster[sched_bucket] = cluster_id;
349 sched_edge_update_preferred_cluster(clutch, modify_bitmap, tg_bucket_preferred_cluster);
350 }
351
352 void
impl_set_pset_load_avg(int cluster_id,int QoS,uint64_t load_avg)353 impl_set_pset_load_avg(int cluster_id, int QoS, uint64_t load_avg)
354 {
355 assert(QoS > 0 && QoS < TH_BUCKET_SCHED_MAX);
356 pset_array[cluster_id]->pset_load_average[QoS] = load_avg;
357 }
358
359 void
edge_set_thread_shared_rsrc(test_thread_t thread,bool native_first)360 edge_set_thread_shared_rsrc(test_thread_t thread, bool native_first)
361 {
362 int shared_rsrc_type = native_first ? CLUSTER_SHARED_RSRC_TYPE_NATIVE_FIRST :
363 CLUSTER_SHARED_RSRC_TYPE_RR;
364 ((thread_t)thread)->th_shared_rsrc_heavy_user[shared_rsrc_type] = true;
365 }
366
367 void
impl_set_pset_derecommended(int cluster_id)368 impl_set_pset_derecommended(int cluster_id)
369 {
370 processor_set_t pset = pset_array[cluster_id];
371 pset->recommended_bitmask = 0;
372 atomic_bit_clear(&pset->node->pset_recommended_map, cluster_id, memory_order_relaxed);
373 recommended_cpu_count_for_type[cluster_type_to_test_cpu_type(pset->pset_type)] -=
374 bit_count(pset->cpu_bitmask);
375 }
376
377 void
impl_set_pset_recommended(int cluster_id)378 impl_set_pset_recommended(int cluster_id)
379 {
380 processor_set_t pset = pset_array[cluster_id];
381 pset->recommended_bitmask = pset->cpu_bitmask;
382 atomic_bit_set(&pset->node->pset_recommended_map, cluster_id, memory_order_relaxed);
383 recommended_cpu_count_for_type[cluster_type_to_test_cpu_type(pset->pset_type)] +=
384 bit_count(pset->cpu_bitmask);
385 }
386
387 void
impl_pop_ipi(int * cpu_id,test_ipi_type_t * ipi_type)388 impl_pop_ipi(int *cpu_id, test_ipi_type_t *ipi_type)
389 {
390 assert(expect_ipi_ind < curr_ipi_ind);
391 *cpu_id = logged_ipis[expect_ipi_ind].cpu_id;
392 *ipi_type = (test_ipi_type_t)logged_ipis[expect_ipi_ind].ipi_type;
393 expect_ipi_ind++;
394 }
395
396 bool
impl_thread_should_yield(int cpu_id)397 impl_thread_should_yield(int cpu_id)
398 {
399 _curr_cpu = cpu_id;
400 assert(cpus[cpu_id]->active_thread != NULL);
401 return sched_edge_thread_should_yield(cpus[cpu_id], cpus[cpu_id]->active_thread);
402 }
403
404 void
impl_send_ipi(int cpu_id,test_thread_t thread,test_ipi_event_t event)405 impl_send_ipi(int cpu_id, test_thread_t thread, test_ipi_event_t event)
406 {
407 sched_ipi_type_t triggered_ipi = sched_ipi_action(cpus[cpu_id],
408 (thread_t)thread, (sched_ipi_event_t)event);
409 sched_ipi_perform(cpus[cpu_id], triggered_ipi);
410 }
411
412 uint32_t
impl_qos_max_parallelism(int qos,uint64_t options)413 impl_qos_max_parallelism(int qos, uint64_t options)
414 {
415 return sched_edge_qos_max_parallelism(qos, options);
416 }
417