1 /*
2 * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
3 * Licensed under the Mulan PSL v2.
4 * You can use this software according to the terms and conditions of the Mulan PSL v2.
5 * You may obtain a copy of Mulan PSL v2 at:
6 * http://license.coscl.org.cn/MulanPSL2
7 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9 * PURPOSE.
10 * See the Mulan PSL v2 for more details.
11 */
12 #include <sched/sched.h>
13 #include <sched/context.h>
14 #include <sched/fpu.h>
15 #include <mm/kmalloc.h>
16 #include <irq/ipi.h>
17 #include <common/util.h>
18
19 /* Scheduler global data */
20 struct thread *current_threads[PLAT_CPU_NUM];
21 struct thread idle_threads[PLAT_CPU_NUM];
22
23 #if PLAT_CPU_NUM <= 32
24 unsigned int resched_bitmaps[PLAT_CPU_NUM];
25 #else /* PLAT_CPU_NUM <= 32 */
26 #error PLAT_CPU_NUM exceeds support
27 #endif /* PLAT_CPU_NUM <= 32 */
28
29 /* Chosen Scheduling Policies */
30 struct sched_ops *cur_sched_ops;
31
32 /* Scheduler module local interfaces */
33
34 /*
35 * Set the current_thread to @target.
36 * Note: the switch is between current_thread and @target.
37 */
switch_to_thread(struct thread * target)38 int switch_to_thread(struct thread *target)
39 {
40 BUG_ON(!target);
41 BUG_ON(!target->thread_ctx);
42 BUG_ON((target->thread_ctx->state == TS_READY));
43 BUG_ON((target->thread_ctx->thread_exit_state == TE_EXITED));
44
45 unsigned int cpuid = smp_get_cpu_id();
46
47 #ifdef CHCORE_KERNEL_RT
48 resched_bitmaps[cpuid] &= ~BIT(cpuid);
49 #endif
50
51 /* No thread switch happens actually */
52 if (target == current_thread) {
53 target->thread_ctx->state = TS_RUNNING;
54
55 /* The previous thread is the thread itself */
56 target->prev_thread = THREAD_ITSELF;
57 return 0;
58 }
59
60 target->thread_ctx->cpuid = cpuid;
61
62 target->thread_ctx->state = TS_RUNNING;
63
64 /* Record the thread transferring the CPU */
65 target->prev_thread = current_thread;
66
67 target->thread_ctx->kernel_stack_state = KS_LOCKED;
68
69 current_thread = target;
70
71 return 0;
72 }
73
74 /*
75 * Return the thread's affinity if possible.
76 * Otherwise, return its previously running CPU (FPU owner).
77 */
get_cpubind(struct thread * thread)78 int get_cpubind(struct thread *thread)
79 {
80 #if FPU_SAVING_MODE == LAZY_FPU_MODE
81 int affinity, is_fpu_owner;
82 unsigned int local_cpuid;
83
84 local_cpuid = smp_get_cpu_id();
85 affinity = thread->thread_ctx->affinity;
86 is_fpu_owner = thread->thread_ctx->is_fpu_owner;
87
88 /*
89 * If the thread is the FPU owner of current CPU core,
90 * save_and_release_fpu() can make it become not a owner.
91 * Therefore, it can be migrated.
92 *
93 * However, if thread is a FPU owner of some other CPU,
94 * we cannot directly migrate it.
95 */
96 if (is_fpu_owner < 0) {
97 return affinity;
98 } else if (is_fpu_owner == local_cpuid) {
99 if (affinity == local_cpuid || affinity == NO_AFF) {
100 return local_cpuid;
101 } else {
102 save_and_release_fpu(thread);
103 return affinity;
104 }
105 } else {
106 return is_fpu_owner;
107 }
108 #else
109 return thread->thread_ctx->affinity;
110 #endif
111 }
112
113 /*
114 * find_runnable_thread
115 * ** Shoule hold a dedicated lock for the thread_list and this function can
116 * only be called in the critical section!! ** Only the thread which kernel
117 * state is free can be choosed
118 */
find_runnable_thread(struct list_head * thread_list)119 struct thread *find_runnable_thread(struct list_head *thread_list)
120 {
121 struct thread *thread;
122
123 for_each_in_list (thread, struct thread, ready_queue_node, thread_list) {
124 if (thread->thread_ctx->kernel_stack_state == KS_FREE
125 || thread == current_thread) {
126 return thread;
127 }
128 }
129 return NULL;
130 }
131
132 /* Global interfaces */
133
print_thread(struct thread * thread)134 void print_thread(struct thread *thread)
135 {
136 #define TYPE_STR_LEN 20
137 char thread_type[][TYPE_STR_LEN] = {
138 "IDLE", "KERNEL", "USER", "SHADOW", "REGISTER", "TESTS"};
139
140 #define STATE_STR_LEN 20
141 char thread_state[][STATE_STR_LEN] = {
142 "TS_INIT ",
143 "TS_READY ",
144 "TS_INTER ",
145 "TS_RUNNING ",
146 "TS_EXIT ",
147 "TS_WAITING ",
148 };
149
150 printk("Thread %p\tType: %s\tState: %s\tCPU %d\tAFF %d\t"
151 "Budget %d\tPrio: %d\tIP: %p\tCMD: %s\n",
152 thread,
153 thread_type[thread->thread_ctx->type],
154 thread_state[thread->thread_ctx->state],
155 thread->thread_ctx->cpuid,
156 thread->thread_ctx->affinity,
157 /* REGISTER and SHADOW threads may have no sc, so just print -1.
158 */
159 thread->thread_ctx->sc ? thread->thread_ctx->sc->budget : -1,
160 thread->thread_ctx->sc ? thread->thread_ctx->sc->prio : -1,
161 arch_get_thread_next_ip(thread),
162 thread->cap_group->cap_group_name);
163 }
164
165 /*
166 * This function is used after current_thread is set (a new thread needs to be
167 * scheduled).
168 *
169 * Switch context between current_thread and current_thread->prev_thread:
170 * including: vmspace, fpu, tls, ...
171 *
172 * Return the context pointer which should be set to stack pointer register.
173 */
switch_context(void)174 vaddr_t switch_context(void)
175 {
176 struct thread *target_thread;
177 struct thread_ctx *target_ctx;
178 struct thread *prev_thread;
179
180 target_thread = current_thread;
181 BUG_ON(!target_thread);
182 BUG_ON(!target_thread->thread_ctx);
183
184 target_ctx = target_thread->thread_ctx;
185
186 prev_thread = target_thread->prev_thread;
187 if (prev_thread == THREAD_ITSELF)
188 return (vaddr_t)target_ctx;
189
190 #if FPU_SAVING_MODE == EAGER_FPU_MODE
191 save_fpu_state(prev_thread);
192 restore_fpu_state(target_thread);
193 #else
194 /* FPU_SAVING_MODE == LAZY_FPU_MODE */
195 if (target_thread->thread_ctx->type > TYPE_KERNEL)
196 disable_fpu_usage();
197 #endif
198
199 /* Switch the TLS information: save and restore */
200 switch_tls_info(prev_thread, target_thread);
201
202 #ifndef CHCORE_KERNEL_TEST
203 BUG_ON(!target_thread->vmspace);
204 /*
205 * Recording the CPU the thread runs on: for TLB maintainence.
206 * switch_context is always required for running a (new) thread.
207 * So, we invoke record_running_cpu here.
208 */
209 record_history_cpu(target_thread->vmspace, smp_get_cpu_id());
210 if ((!prev_thread) || (prev_thread->vmspace != target_thread->vmspace))
211 switch_thread_vmspace_to(target_thread);
212 #else /* CHCORE_KERNEL_TEST */
213 /* TYPE_TESTS threads do not have vmspace. */
214 if (target_thread->thread_ctx->type != TYPE_TESTS) {
215 BUG_ON(!target_thread->vmspace);
216 record_history_cpu(target_thread->vmspace, smp_get_cpu_id());
217 switch_thread_vmspace_to(target_thread);
218 }
219 #endif /* CHCORE_KERNEL_TEST */
220
221 arch_switch_context(target_thread);
222
223 return (vaddr_t)target_ctx;
224 }
225
do_pending_resched(void)226 void do_pending_resched(void)
227 {
228 unsigned int cpuid;
229 unsigned int local_cpuid = smp_get_cpu_id();
230 bool local_cpu_need_resched = false;
231
232 while (resched_bitmaps[local_cpuid]) {
233 cpuid = bsr(resched_bitmaps[local_cpuid]);
234 resched_bitmaps[local_cpuid] &= ~BIT(cpuid);
235 if (cpuid != local_cpuid) {
236 send_ipi(cpuid, IPI_RESCHED);
237 } else {
238 local_cpu_need_resched = true;
239 }
240 }
241
242 if (local_cpu_need_resched) {
243 sched();
244 eret_to_thread(switch_context());
245 }
246 }
247
finish_switch(void)248 void finish_switch(void)
249 {
250 struct thread *prev_thread;
251
252 prev_thread = current_thread->prev_thread;
253 if ((prev_thread == THREAD_ITSELF) || (prev_thread == NULL))
254 return;
255
256 if (prev_thread->thread_ctx->type == TYPE_SHADOW
257 && prev_thread->thread_ctx->state == TS_EXIT) {
258 cap_free(prev_thread->cap_group, prev_thread->cap);
259 current_thread->prev_thread = NULL;
260 return;
261 }
262
263 prev_thread->thread_ctx->kernel_stack_state = KS_FREE;
264
265 #ifdef CHCORE_KERNEL_RT
266 /* If a resched IPI is received during send_ipi(), the local CPU will
267 * re-schedule. */
268 do_pending_resched();
269 #endif
270 }
271
272 /*
273 * Transfer the CPU control flow to current_thread (sp).
274 * The usage: eret_to_thread(switch_context());
275 */
eret_to_thread(vaddr_t sp)276 void eret_to_thread(vaddr_t sp)
277 {
278 #ifndef CHCORE_KERNEL_RT
279 finish_switch();
280 #endif
281 /* Defined as an asm func. */
282 __eret_to_thread(sp);
283 }
284
285 /*
286 * Exposed for (directly) scheduling to one thread (for example, in IPC and
287 * Notification).
288 * - Fast path: if direct switch is possible, the core scheduler will not
289 * involve.
290 * - Slow path: otherwise, fallback to the core scheduler (no direct thread
291 * switch).
292 *
293 * Note that this function never return back to the caller.
294 */
sched_to_thread(struct thread * target)295 void sched_to_thread(struct thread *target)
296 {
297 int is_fpu_owner;
298
299 /* TS_INTER may be set in signal_notific */
300 BUG_ON((target->thread_ctx->state != TS_WAITING)
301 && (target->thread_ctx->state != TS_INTER));
302
303 /* Switch to itself? */
304 BUG_ON(target == current_thread);
305
306 /*
307 * We need to ensure the target thread kstack is free
308 * before switching to it.
309 *
310 * Otherwise, wrong cases may occur. For example:
311 * Time-1: CPU-0: T1 sends ipc to T2 ->
312 * T2 is enqueued and T1 is current thread
313 *
314 * Time-2: CPU-1: T2 runs and returns to T1 ->
315 * T1's state may be TS_READY.
316 *
317 * Time-3: CPU-0: sched.c finds old thread (T1)'s state is
318 * TS_READY (triggers BUG_ON).
319 *
320 * Another example:
321 * If T1 want to direct switch to T2.
322 * The target (server) thread may be still executing after
323 * IPC returns (a very small time window),
324 * so we need to wait until its stack is free.
325 */
326 wait_for_kernel_stack(target);
327
328 /*
329 * Since the target thread is waiting or be set to TS_INTER in
330 * signal_notific, its is_fpu_owner state will not change
331 * or can only be changed to -1.
332 */
333 is_fpu_owner = target->thread_ctx->is_fpu_owner;
334
335 if ((is_fpu_owner >= 0) && (is_fpu_owner != smp_get_cpu_id())) {
336 /*
337 * Slow path: if target thread is fpu_owner of some other CPUs,
338 * local CPU cannot direct switch to it.
339 */
340
341 target->thread_ctx->state = TS_INTER;
342 BUG_ON(sched_enqueue(target));
343
344 sched();
345 } else {
346 /* Fast path: direct switch to target thread (without
347 * scheduling). */
348
349 /*
350 * Note: if disallow sched_to_thread in notification,
351 * we can add BUG_ON(current_thread->thread_ctx->state !=
352 * TS_WAITING) here and remove the below if statement.
353 */
354
355 /* If current thread has not been set to TS_WAITING,
356 * put it into the ready queue before switching to
357 * the target thread.
358 */
359 if (current_thread->thread_ctx->state != TS_WAITING)
360 BUG_ON(sched_enqueue(current_thread));
361
362 switch_to_thread(target);
363 }
364
365 eret_to_thread(switch_context());
366 }
367
368 /* Pending rescheduling will be done when the kernel returns to userspace */
add_pending_resched(unsigned int cpuid)369 void add_pending_resched(unsigned int cpuid)
370 {
371 BUG_ON(cpuid >= PLAT_CPU_NUM);
372 resched_bitmaps[smp_get_cpu_id()] |= BIT(cpuid);
373 }
374
wait_for_kernel_stack(struct thread * thread)375 void wait_for_kernel_stack(struct thread *thread)
376 {
377 /*
378 * Handle IPI tx while waiting to avoid deadlock.
379 *
380 * Deadlock example:
381 * CPU 0: waiting for CPU 1 to release its kernel stack,
382 * cannot receive IPI as it is in kernel mode;
383 * CPU 1: waiting for CPU 0 to finish an IPI tx,
384 * cannot release its kernel stack as it is
385 * executing on that stack.
386 */
387 while (thread->thread_ctx->kernel_stack_state != KS_FREE) {
388 handle_ipi();
389 }
390 }
391
init_idle_threads(void)392 static void init_idle_threads(void)
393 {
394 unsigned int i;
395 const char *idle_name = "KERNEL-IDLE";
396 unsigned int idle_name_len = strlen(idle_name);
397 struct cap_group *idle_cap_group;
398 struct vmspace *idle_vmspace;
399
400 /* Create a fake idle cap group to store the name */
401 idle_cap_group = kzalloc(sizeof(*idle_cap_group));
402 idle_name_len = MIN(idle_name_len, MAX_GROUP_NAME_LEN);
403 memcpy(idle_cap_group->cap_group_name, idle_name, idle_name_len);
404 init_list_head(&idle_cap_group->thread_list);
405
406 idle_vmspace = create_idle_vmspace();
407
408 for (i = 0; i < PLAT_CPU_NUM; i++) {
409 idle_threads[i].thread_ctx = create_thread_ctx(TYPE_IDLE);
410 BUG_ON(idle_threads[i].thread_ctx == NULL);
411
412 init_thread_ctx(&idle_threads[i], 0, 0, IDLE_PRIO, TYPE_IDLE, i);
413
414 arch_idle_ctx_init(idle_threads[i].thread_ctx, idle_thread_routine);
415
416 idle_threads[i].cap_group = idle_cap_group;
417 idle_threads[i].vmspace = idle_vmspace;
418 list_add(&idle_threads[i].node, &idle_cap_group->thread_list);
419 }
420 kdebug("Create %d idle threads.\n", i);
421 }
422
init_current_threads(void)423 static void init_current_threads(void)
424 {
425 int i;
426
427 for (i = 0; i < PLAT_CPU_NUM; i++) {
428 current_threads[i] = NULL;
429 }
430 }
431
init_resched_bitmaps(void)432 static void init_resched_bitmaps(void)
433 {
434 memset(resched_bitmaps, 0, sizeof(resched_bitmaps));
435 }
436
sched_init(struct sched_ops * sched_ops)437 int sched_init(struct sched_ops *sched_ops)
438 {
439 BUG_ON(sched_ops == NULL);
440
441 init_idle_threads();
442 init_current_threads();
443 init_resched_bitmaps();
444
445 cur_sched_ops = sched_ops;
446 cur_sched_ops->sched_init();
447
448 return 0;
449 }
450
451 /* SYSCALL functions */
sys_yield(void)452 void sys_yield(void)
453 {
454 current_thread->thread_ctx->sc->budget = 0;
455 sched();
456 eret_to_thread(switch_context());
457 }
458
sys_top(void)459 void sys_top(void)
460 {
461 cur_sched_ops->sched_top();
462 }
463