xref: /xnu-11417.101.15/tests/sched/sched_test_harness/sched_edge_harness.c (revision e3723e1f17661b24996789d8afc084c0c3303b26) !
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