• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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