refactor(kernel): centralize selftests and split scheduler code
This commit is contained in:
parent
20cdf179d7
commit
7db47d0e38
2
Makefile
2
Makefile
@ -31,7 +31,7 @@ include kernel/Makefile
|
|||||||
all: $(BOOT_EFI) $(KERNEL_ELF)
|
all: $(BOOT_EFI) $(KERNEL_ELF)
|
||||||
|
|
||||||
dirs:
|
dirs:
|
||||||
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/arch/mm $(BUILD_DIR)/kernel $(BUILD_DIR)/kernel/locking $(BUILD_DIR)/mm $(EFI_DIR)
|
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/arch/mm $(BUILD_DIR)/kernel $(BUILD_DIR)/kernel/locking $(BUILD_DIR)/kernel/sched $(BUILD_DIR)/kernel/selftest $(BUILD_DIR)/kernel/time $(BUILD_DIR)/mm $(EFI_DIR)
|
||||||
|
|
||||||
$(BUILD_DIR)/arch/boot/%.obj: $(ARCH_DIR)/boot/%.c $(ARCH_DIR)/include/efi.h $(ARCH_DIR)/boot/debug_log.h include/tianole/boot_info.h include/tianole/elf.h | dirs
|
$(BUILD_DIR)/arch/boot/%.obj: $(ARCH_DIR)/boot/%.c $(ARCH_DIR)/include/efi.h $(ARCH_DIR)/boot/debug_log.h include/tianole/boot_info.h include/tianole/elf.h | dirs
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|||||||
@ -34,7 +34,9 @@ KERNEL_CFLAGS := \
|
|||||||
-Wall \
|
-Wall \
|
||||||
-Wextra \
|
-Wextra \
|
||||||
-Werror \
|
-Werror \
|
||||||
|
-I. \
|
||||||
-Iinclude \
|
-Iinclude \
|
||||||
|
-Ikernel \
|
||||||
-I$(ARCH_DIR)/include
|
-I$(ARCH_DIR)/include
|
||||||
|
|
||||||
KERNEL_ASFLAGS := \
|
KERNEL_ASFLAGS := \
|
||||||
|
|||||||
@ -4,11 +4,12 @@
|
|||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/mm.h>
|
#include <tianole/mm.h>
|
||||||
|
|
||||||
|
#include "page_table.h"
|
||||||
|
|
||||||
#define ENTRY_COUNT 512
|
#define ENTRY_COUNT 512
|
||||||
#define PAGE_MASK 0x000ffffffffff000ull
|
#define PAGE_MASK 0x000ffffffffff000ull
|
||||||
#define PAGE_SIZE_FLAG (1ull << 7)
|
#define PAGE_SIZE_FLAG (1ull << 7)
|
||||||
#define MAX_RESERVED_TABLE_PAGES 4096
|
#define MAX_RESERVED_TABLE_PAGES 4096
|
||||||
#define TEST_VIRTUAL_PAGE 0xffffff0000000000ull
|
|
||||||
|
|
||||||
static phys_addr_t reserved_table_pages[MAX_RESERVED_TABLE_PAGES];
|
static phys_addr_t reserved_table_pages[MAX_RESERVED_TABLE_PAGES];
|
||||||
static uint64_t reserved_table_page_count;
|
static uint64_t reserved_table_page_count;
|
||||||
@ -141,7 +142,7 @@ static uint64_t make_table_entry(phys_addr_t table)
|
|||||||
return table | PAGE_PRESENT | PAGE_WRITABLE;
|
return table | PAGE_PRESENT | PAGE_WRITABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void page_tables_init(void)
|
void page_tables_init(void)
|
||||||
{
|
{
|
||||||
uint64_t index;
|
uint64_t index;
|
||||||
uint64_t *firmware_pml4;
|
uint64_t *firmware_pml4;
|
||||||
@ -268,38 +269,3 @@ int virt_to_phys(virt_addr_t virt, phys_addr_t *phys)
|
|||||||
*phys = (*entry & PAGE_MASK) | (virt & (PAGE_SIZE - 1));
|
*phys = (*entry & PAGE_MASK) | (virt & (PAGE_SIZE - 1));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void page_table_selftest(void)
|
|
||||||
{
|
|
||||||
phys_addr_t page = alloc_page();
|
|
||||||
phys_addr_t resolved;
|
|
||||||
volatile uint64_t *mapped =
|
|
||||||
(volatile uint64_t *)(uintptr_t)TEST_VIRTUAL_PAGE;
|
|
||||||
|
|
||||||
if (page == 0) {
|
|
||||||
panic("page table selftest allocation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
page_tables_init();
|
|
||||||
|
|
||||||
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
|
|
||||||
panic("page table selftest map failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
|
|
||||||
resolved != page) {
|
|
||||||
panic("page table selftest resolve failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
*mapped = 0x54494f4c45504d4dull;
|
|
||||||
if (*mapped != 0x54494f4c45504d4dull) {
|
|
||||||
panic("page table selftest access failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unmap_page(TEST_VIRTUAL_PAGE) != 0) {
|
|
||||||
panic("page table selftest unmap failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
free_page(page);
|
|
||||||
early_log_puts("page table selftest ok\n");
|
|
||||||
}
|
|
||||||
|
|||||||
8
arch/x86/mm/page_table.h
Normal file
8
arch/x86/mm/page_table.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef ARCH_X86_MM_PAGE_TABLE_H
|
||||||
|
#define ARCH_X86_MM_PAGE_TABLE_H
|
||||||
|
|
||||||
|
#include <tianole/mm.h>
|
||||||
|
|
||||||
|
void page_tables_init(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -67,6 +67,8 @@
|
|||||||
- 可独立执行的检查放在 `scripts/checks/`。
|
- 可独立执行的检查放在 `scripts/checks/`。
|
||||||
- 多个检查共享的函数放在 `scripts/lib/`。
|
- 多个检查共享的函数放在 `scripts/lib/`。
|
||||||
- 不把所有检查逻辑持续堆进 `scripts/check.sh`。
|
- 不把所有检查逻辑持续堆进 `scripts/check.sh`。
|
||||||
|
- 项目结构规则放在 `scripts/checks/structure.sh`,优先用宿主 Linux/LLVM 工具实现底层扫描,把 Tianole 自己的目录和 include 约束固化在脚本里。
|
||||||
|
- 启动阶段内核自测集中放在 `kernel/selftest/`,不要散落在具体实现目录里。
|
||||||
|
|
||||||
## 验证
|
## 验证
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,10 @@
|
|||||||
## 建议边界
|
## 建议边界
|
||||||
|
|
||||||
- `mm/`:架构无关内存管理。
|
- `mm/`:架构无关内存管理。
|
||||||
- `arch/x86/mm/`:页表格式、地址空间切换、TLB 操作。
|
- `arch/x86/mm/page_table.c`:页表格式、地址空间切换、TLB 操作和 map/unmap/query 主路径。
|
||||||
|
- `arch/x86/mm/fault.c`:page fault 诊断。
|
||||||
|
- `arch/x86/mm/page_table.h`:x86 页表子目录私有接口。
|
||||||
|
- `kernel/selftest/page_table.c`:页表 map/unmap/query 启动自测。
|
||||||
- `include/tianole/`:通用内存接口。
|
- `include/tianole/`:通用内存接口。
|
||||||
|
|
||||||
## 实现内容
|
## 实现内容
|
||||||
@ -55,6 +58,7 @@
|
|||||||
- 已切换到内核自有 PML4,不再直接修改固件页表。
|
- 已切换到内核自有 PML4,不再直接修改固件页表。
|
||||||
- 已提供最小 `map_page()`、`unmap_page()` 和 `virt_to_phys()` 接口。
|
- 已提供最小 `map_page()`、`unmap_page()` 和 `virt_to_phys()` 接口。
|
||||||
- 已加入页表 map/unmap/query selftest。
|
- 已加入页表 map/unmap/query selftest。
|
||||||
|
- 已把 x86 页表 selftest 从 `page_table.c` 移到 `kernel/selftest/page_table.c`,避免页表主路径和启动验证逻辑混在同一目录边界。
|
||||||
- 已拆出 x86 page fault 诊断路径,能输出 fault address、错误码、访问类型和权限来源。
|
- 已拆出 x86 page fault 诊断路径,能输出 fault address、错误码、访问类型和权限来源。
|
||||||
- 已建立最小内核堆,提供 `kmalloc()` 和 `kfree()`,底层通过页表按需映射物理页。
|
- 已建立最小内核堆,提供 `kmalloc()` 和 `kfree()`,底层通过页表按需映射物理页。
|
||||||
- 已加入内核堆分配、写入、释放、复用 selftest。
|
- 已加入内核堆分配、写入、释放、复用 selftest。
|
||||||
|
|||||||
@ -12,7 +12,12 @@
|
|||||||
|
|
||||||
## 建议边界
|
## 建议边界
|
||||||
|
|
||||||
- `kernel/sched/`:线程、调度器、等待队列。
|
- `kernel/sched/core.c`:run queue、调度选择、tick 和 IRQ exit 调度边界。
|
||||||
|
- `kernel/sched/thread.c`:线程对象创建、初始栈、退出和 DEAD 线程回收。
|
||||||
|
- `kernel/sched/wait.c`:等待队列。
|
||||||
|
- `kernel/sched/idle.c`:idle thread。
|
||||||
|
- `kernel/sched/sched.h`:调度子系统私有接口,不向通用内核层公开内部状态。
|
||||||
|
- `kernel/selftest/sched.c`:当前阶段的调度自测和启动演示线程。
|
||||||
- `kernel/time/`:通用时间与 timer 抽象。
|
- `kernel/time/`:通用时间与 timer 抽象。
|
||||||
- `arch/x86/`:具体 timer、上下文切换。
|
- `arch/x86/`:具体 timer、上下文切换。
|
||||||
|
|
||||||
@ -66,6 +71,7 @@
|
|||||||
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
||||||
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
||||||
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
||||||
|
- 已把调度代码按职责拆分为 `core.c`、`thread.c`、`wait.c`、`idle.c` 和私有 `sched.h`,并把当前阶段自测/演示线程移到 `kernel/selftest/sched.c`。
|
||||||
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()` 和 wait queue wakeup。
|
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()` 和 wait queue wakeup。
|
||||||
|
|
||||||
后续扩展:
|
后续扩展:
|
||||||
|
|||||||
@ -3,7 +3,12 @@ KERNEL_SRCS := \
|
|||||||
kernel/boot_report.c \
|
kernel/boot_report.c \
|
||||||
kernel/early_log.c \
|
kernel/early_log.c \
|
||||||
kernel/locking/spinlock.c \
|
kernel/locking/spinlock.c \
|
||||||
|
kernel/sched/core.c \
|
||||||
|
kernel/sched/idle.c \
|
||||||
kernel/sched/thread.c \
|
kernel/sched/thread.c \
|
||||||
|
kernel/sched/wait.c \
|
||||||
|
kernel/selftest/page_table.c \
|
||||||
|
kernel/selftest/sched.c \
|
||||||
kernel/time/timer.c
|
kernel/time/timer.c
|
||||||
|
|
||||||
KERNEL_OBJS := \
|
KERNEL_OBJS := \
|
||||||
|
|||||||
171
kernel/sched/core.c
Normal file
171
kernel/sched/core.c
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <arch/switch.h>
|
||||||
|
|
||||||
|
#include <tianole/early_log.h>
|
||||||
|
#include <tianole/sched.h>
|
||||||
|
#include <tianole/timer.h>
|
||||||
|
|
||||||
|
#include "sched.h"
|
||||||
|
|
||||||
|
struct thread *run_queue_head;
|
||||||
|
struct thread *run_queue_tail;
|
||||||
|
struct thread *current_thread;
|
||||||
|
uintptr_t boot_stack_pointer;
|
||||||
|
uint64_t next_thread_id = 1;
|
||||||
|
int scheduler_ready;
|
||||||
|
int schedule_locked;
|
||||||
|
int need_resched;
|
||||||
|
struct thread *idle_thread;
|
||||||
|
struct spinlock scheduler_lock = SPINLOCK_INITIALIZER;
|
||||||
|
|
||||||
|
void enqueue_thread(struct thread *thread)
|
||||||
|
{
|
||||||
|
thread->next = 0;
|
||||||
|
|
||||||
|
if (run_queue_tail != 0) {
|
||||||
|
run_queue_tail->next = thread;
|
||||||
|
} else {
|
||||||
|
run_queue_head = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_queue_tail = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct thread *next_runnable_thread(void)
|
||||||
|
{
|
||||||
|
struct thread *start;
|
||||||
|
struct thread *thread;
|
||||||
|
|
||||||
|
if (current_thread == 0 || current_thread->next == 0) {
|
||||||
|
start = run_queue_head;
|
||||||
|
} else {
|
||||||
|
start = current_thread->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread = start;
|
||||||
|
while (thread != 0) {
|
||||||
|
if (thread->state == THREAD_READY && thread != idle_thread) {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
thread = thread->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (thread = run_queue_head; thread != start; thread = thread->next) {
|
||||||
|
if (thread->state == THREAD_READY && thread != idle_thread) {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idle_thread != 0 && idle_thread->state == THREAD_READY) {
|
||||||
|
return idle_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wake_sleeping_threads(uint64_t tick)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
|
||||||
|
for (thread = run_queue_head; thread != 0; thread = thread->next) {
|
||||||
|
if (thread->state == THREAD_SLEEPING &&
|
||||||
|
thread->wake_tick <= tick) {
|
||||||
|
thread->wake_tick = 0;
|
||||||
|
thread->state = THREAD_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_yield(void)
|
||||||
|
{
|
||||||
|
struct thread *prev;
|
||||||
|
struct thread *next;
|
||||||
|
|
||||||
|
if (schedule_locked != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_reap_dead_threads();
|
||||||
|
|
||||||
|
prev = current_thread;
|
||||||
|
next = next_runnable_thread();
|
||||||
|
|
||||||
|
if (next == 0 || next == prev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_locked = 1;
|
||||||
|
|
||||||
|
if (prev != 0 && prev->state == THREAD_RUNNING) {
|
||||||
|
prev->state = THREAD_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
next->state = THREAD_RUNNING;
|
||||||
|
current_thread = next;
|
||||||
|
schedule_locked = 0;
|
||||||
|
|
||||||
|
if (prev == 0) {
|
||||||
|
arch_context_switch(&boot_stack_pointer, next->stack_pointer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arch_context_switch(&prev->stack_pointer, next->stack_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_tick(uint64_t tick)
|
||||||
|
{
|
||||||
|
wake_sleeping_threads(tick);
|
||||||
|
|
||||||
|
if (current_thread != 0 && current_thread->state == THREAD_RUNNING) {
|
||||||
|
need_resched = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_irq_exit(void)
|
||||||
|
{
|
||||||
|
if (need_resched == 0 || current_thread == 0 || schedule_locked != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
need_resched = 0;
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_sleep(uint64_t ticks)
|
||||||
|
{
|
||||||
|
uint64_t now;
|
||||||
|
|
||||||
|
if (current_thread == 0 || ticks == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
now = timer_ticks();
|
||||||
|
current_thread->wake_tick = now + ticks;
|
||||||
|
current_thread->state = THREAD_SLEEPING;
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_init(void)
|
||||||
|
{
|
||||||
|
if (scheduler_ready != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_queue_head = 0;
|
||||||
|
run_queue_tail = 0;
|
||||||
|
idle_thread = 0;
|
||||||
|
scheduler_ready = 1;
|
||||||
|
|
||||||
|
early_log_puts("scheduler initialized\n");
|
||||||
|
sched_selftest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_start(void)
|
||||||
|
{
|
||||||
|
if (sched_idle_create() != 0) {
|
||||||
|
panic("idle thread creation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_demo_start();
|
||||||
|
}
|
||||||
22
kernel/sched/idle.c
Normal file
22
kernel/sched/idle.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include <tianole/sched.h>
|
||||||
|
|
||||||
|
#include "sched.h"
|
||||||
|
|
||||||
|
static void idle_thread_entry(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
__asm__ volatile("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sched_idle_create(void)
|
||||||
|
{
|
||||||
|
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
|
||||||
|
if (idle_thread == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
26
kernel/sched/sched.h
Normal file
26
kernel/sched/sched.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef KERNEL_SCHED_SCHED_H
|
||||||
|
#define KERNEL_SCHED_SCHED_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/sched.h>
|
||||||
|
#include <tianole/spinlock.h>
|
||||||
|
|
||||||
|
extern struct thread *run_queue_head;
|
||||||
|
extern struct thread *run_queue_tail;
|
||||||
|
extern struct thread *current_thread;
|
||||||
|
extern uintptr_t boot_stack_pointer;
|
||||||
|
extern uint64_t next_thread_id;
|
||||||
|
extern int scheduler_ready;
|
||||||
|
extern int schedule_locked;
|
||||||
|
extern int need_resched;
|
||||||
|
extern struct thread *idle_thread;
|
||||||
|
extern struct spinlock scheduler_lock;
|
||||||
|
|
||||||
|
void enqueue_thread(struct thread *thread);
|
||||||
|
void sched_reap_dead_threads(void);
|
||||||
|
void sched_selftest(void);
|
||||||
|
int sched_idle_create(void);
|
||||||
|
void sched_demo_start(void) __attribute__((noreturn));
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,28 +1,16 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <arch/switch.h>
|
|
||||||
|
|
||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/mm.h>
|
#include <tianole/mm.h>
|
||||||
#include <tianole/sched.h>
|
#include <tianole/sched.h>
|
||||||
#include <tianole/spinlock.h>
|
#include <tianole/spinlock.h>
|
||||||
#include <tianole/timer.h>
|
|
||||||
|
#include "sched.h"
|
||||||
|
|
||||||
#define KERNEL_STACK_SIZE (PAGE_SIZE * 4u)
|
#define KERNEL_STACK_SIZE (PAGE_SIZE * 4u)
|
||||||
#define STACK_ALIGNMENT 16u
|
#define STACK_ALIGNMENT 16u
|
||||||
|
|
||||||
static struct thread *run_queue_head;
|
|
||||||
static struct thread *run_queue_tail;
|
|
||||||
static struct thread *current_thread;
|
|
||||||
static uintptr_t boot_stack_pointer;
|
|
||||||
static uint64_t next_thread_id = 1;
|
|
||||||
static int scheduler_ready;
|
|
||||||
static int schedule_locked;
|
|
||||||
static int need_resched;
|
|
||||||
static struct thread *idle_thread;
|
|
||||||
static struct spinlock scheduler_lock = SPINLOCK_INITIALIZER;
|
|
||||||
|
|
||||||
static void thread_trampoline(void) __attribute__((noreturn));
|
static void thread_trampoline(void) __attribute__((noreturn));
|
||||||
|
|
||||||
static uintptr_t align_down_uintptr(uintptr_t value, uintptr_t alignment)
|
static uintptr_t align_down_uintptr(uintptr_t value, uintptr_t alignment)
|
||||||
@ -49,53 +37,6 @@ static void copy_thread_name(char *dest, size_t dest_size, const char *src)
|
|||||||
dest[index] = '\0';
|
dest[index] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enqueue_thread(struct thread *thread)
|
|
||||||
{
|
|
||||||
thread->next = 0;
|
|
||||||
|
|
||||||
if (run_queue_tail != 0) {
|
|
||||||
run_queue_tail->next = thread;
|
|
||||||
} else {
|
|
||||||
run_queue_head = thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_queue_tail = thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void release_thread(struct thread *thread)
|
|
||||||
{
|
|
||||||
kfree(thread->stack_base);
|
|
||||||
kfree(thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reap_dead_threads(void)
|
|
||||||
{
|
|
||||||
struct thread *prev = 0;
|
|
||||||
struct thread *thread = run_queue_head;
|
|
||||||
|
|
||||||
while (thread != 0) {
|
|
||||||
struct thread *next = thread->next;
|
|
||||||
|
|
||||||
if (thread->state == THREAD_DEAD && thread != current_thread) {
|
|
||||||
if (prev != 0) {
|
|
||||||
prev->next = next;
|
|
||||||
} else {
|
|
||||||
run_queue_head = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run_queue_tail == thread) {
|
|
||||||
run_queue_tail = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
release_thread(thread);
|
|
||||||
} else {
|
|
||||||
prev = thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uintptr_t prepare_initial_stack(uintptr_t stack_top)
|
static uintptr_t prepare_initial_stack(uintptr_t stack_top)
|
||||||
{
|
{
|
||||||
uintptr_t *stack = (uintptr_t *)stack_top;
|
uintptr_t *stack = (uintptr_t *)stack_top;
|
||||||
@ -155,178 +96,37 @@ struct thread *kernel_thread_create(
|
|||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct thread *next_runnable_thread(void)
|
static void release_thread(struct thread *thread)
|
||||||
{
|
{
|
||||||
struct thread *start;
|
kfree(thread->stack_base);
|
||||||
struct thread *thread;
|
kfree(thread);
|
||||||
|
|
||||||
if (current_thread == 0 || current_thread->next == 0) {
|
|
||||||
start = run_queue_head;
|
|
||||||
} else {
|
|
||||||
start = current_thread->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thread = start;
|
void sched_reap_dead_threads(void)
|
||||||
|
{
|
||||||
|
struct thread *prev = 0;
|
||||||
|
struct thread *thread = run_queue_head;
|
||||||
|
|
||||||
while (thread != 0) {
|
while (thread != 0) {
|
||||||
if (thread->state == THREAD_READY && thread != idle_thread) {
|
struct thread *next = thread->next;
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
thread = thread->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (thread = run_queue_head; thread != start; thread = thread->next) {
|
if (thread->state == THREAD_DEAD && thread != current_thread) {
|
||||||
if (thread->state == THREAD_READY && thread != idle_thread) {
|
if (prev != 0) {
|
||||||
return thread;
|
prev->next = next;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idle_thread != 0 && idle_thread->state == THREAD_READY) {
|
|
||||||
return idle_thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wake_sleeping_threads(uint64_t tick)
|
|
||||||
{
|
|
||||||
struct thread *thread;
|
|
||||||
|
|
||||||
for (thread = run_queue_head; thread != 0; thread = thread->next) {
|
|
||||||
if (thread->state == THREAD_SLEEPING &&
|
|
||||||
thread->wake_tick <= tick) {
|
|
||||||
thread->wake_tick = 0;
|
|
||||||
thread->state = THREAD_READY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sched_yield(void)
|
|
||||||
{
|
|
||||||
struct thread *prev;
|
|
||||||
struct thread *next;
|
|
||||||
|
|
||||||
if (schedule_locked != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
reap_dead_threads();
|
|
||||||
|
|
||||||
prev = current_thread;
|
|
||||||
next = next_runnable_thread();
|
|
||||||
|
|
||||||
if (next == 0 || next == prev) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule_locked = 1;
|
|
||||||
|
|
||||||
if (prev != 0 && prev->state == THREAD_RUNNING) {
|
|
||||||
prev->state = THREAD_READY;
|
|
||||||
}
|
|
||||||
|
|
||||||
next->state = THREAD_RUNNING;
|
|
||||||
current_thread = next;
|
|
||||||
schedule_locked = 0;
|
|
||||||
|
|
||||||
if (prev == 0) {
|
|
||||||
arch_context_switch(&boot_stack_pointer, next->stack_pointer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
arch_context_switch(&prev->stack_pointer, next->stack_pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sched_tick(uint64_t tick)
|
|
||||||
{
|
|
||||||
wake_sleeping_threads(tick);
|
|
||||||
|
|
||||||
if (current_thread != 0 && current_thread->state == THREAD_RUNNING) {
|
|
||||||
need_resched = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sched_irq_exit(void)
|
|
||||||
{
|
|
||||||
if (need_resched == 0 || current_thread == 0 || schedule_locked != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
need_resched = 0;
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sched_sleep(uint64_t ticks)
|
|
||||||
{
|
|
||||||
uint64_t now;
|
|
||||||
|
|
||||||
if (current_thread == 0 || ticks == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
now = timer_ticks();
|
|
||||||
current_thread->wake_tick = now + ticks;
|
|
||||||
current_thread->state = THREAD_SLEEPING;
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_queue_init(struct wait_queue *queue)
|
|
||||||
{
|
|
||||||
if (queue == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue->head = 0;
|
|
||||||
queue->tail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread)
|
|
||||||
{
|
|
||||||
thread->wait_next = 0;
|
|
||||||
|
|
||||||
if (queue->tail != 0) {
|
|
||||||
queue->tail->wait_next = thread;
|
|
||||||
} else {
|
} else {
|
||||||
queue->head = thread;
|
run_queue_head = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue->tail = thread;
|
if (run_queue_tail == thread) {
|
||||||
|
run_queue_tail = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_queue_sleep(struct wait_queue *queue)
|
release_thread(thread);
|
||||||
{
|
} else {
|
||||||
if (queue == 0 || current_thread == 0) {
|
prev = thread;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_queue_enqueue(queue, current_thread);
|
thread = next;
|
||||||
current_thread->state = THREAD_WAITING;
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_queue_wake_one(struct wait_queue *queue)
|
|
||||||
{
|
|
||||||
struct thread *thread;
|
|
||||||
|
|
||||||
if (queue == 0 || queue->head == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread = queue->head;
|
|
||||||
queue->head = thread->wait_next;
|
|
||||||
if (queue->head == 0) {
|
|
||||||
queue->tail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread->wait_next = 0;
|
|
||||||
if (thread->state == THREAD_WAITING) {
|
|
||||||
thread->state = THREAD_READY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_queue_wake_all(struct wait_queue *queue)
|
|
||||||
{
|
|
||||||
while (queue != 0 && queue->head != 0) {
|
|
||||||
wait_queue_wake_one(queue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,128 +145,3 @@ static void thread_trampoline(void)
|
|||||||
sched_yield();
|
sched_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void thread_selftest_entry(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scheduler_selftest(void)
|
|
||||||
{
|
|
||||||
struct spinlock test_lock;
|
|
||||||
struct thread *first =
|
|
||||||
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
|
||||||
struct thread *second =
|
|
||||||
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
|
||||||
uint64_t flags;
|
|
||||||
|
|
||||||
test_lock.locked = 0;
|
|
||||||
|
|
||||||
if (first == 0 || second == 0 || first == second) {
|
|
||||||
panic("kernel thread selftest allocation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first->id == second->id || first->state != THREAD_READY ||
|
|
||||||
second->state != THREAD_READY) {
|
|
||||||
panic("kernel thread selftest state failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((first->stack_top & (STACK_ALIGNMENT - 1)) != 0 ||
|
|
||||||
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
|
||||||
panic("kernel thread selftest stack alignment failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run_queue_head != first || first->next != second ||
|
|
||||||
run_queue_tail != second) {
|
|
||||||
panic("kernel thread selftest run queue failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irqsave(&test_lock, &flags);
|
|
||||||
spin_unlock_irqrestore(&test_lock, flags);
|
|
||||||
|
|
||||||
early_log_puts("kernel thread selftest ok\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scheduler_demo_entry(void *arg)
|
|
||||||
{
|
|
||||||
uint64_t id = (uint64_t)(uintptr_t)arg;
|
|
||||||
uint64_t step;
|
|
||||||
|
|
||||||
for (step = 1; step <= 3; step++) {
|
|
||||||
early_log_puts("preempt thread ");
|
|
||||||
early_log_u64_decimal(id);
|
|
||||||
early_log_puts(" step=");
|
|
||||||
early_log_u64_decimal(step);
|
|
||||||
early_log_puts("\n");
|
|
||||||
sched_sleep(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wait_queue demo_wait_queue;
|
|
||||||
|
|
||||||
static void wait_queue_demo_waiter(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
|
|
||||||
early_log_puts("waiter sleeping\n");
|
|
||||||
wait_queue_sleep(&demo_wait_queue);
|
|
||||||
early_log_puts("waiter woke\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wait_queue_demo_waker(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
|
|
||||||
early_log_puts("waker sleeping\n");
|
|
||||||
sched_sleep(4);
|
|
||||||
early_log_puts("waker wake_one\n");
|
|
||||||
wait_queue_wake_one(&demo_wait_queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void idle_thread_entry(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
__asm__ volatile("hlt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sched_init(void)
|
|
||||||
{
|
|
||||||
if (scheduler_ready != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_queue_head = 0;
|
|
||||||
run_queue_tail = 0;
|
|
||||||
idle_thread = 0;
|
|
||||||
scheduler_ready = 1;
|
|
||||||
|
|
||||||
early_log_puts("scheduler initialized\n");
|
|
||||||
scheduler_selftest();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sched_start(void)
|
|
||||||
{
|
|
||||||
struct thread *first = kernel_thread_create(
|
|
||||||
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
|
|
||||||
struct thread *second = kernel_thread_create(
|
|
||||||
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
|
||||||
struct thread *waiter =
|
|
||||||
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
|
||||||
struct thread *waker =
|
|
||||||
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
|
||||||
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
|
|
||||||
|
|
||||||
if (first == 0 || second == 0 || waiter == 0 || waker == 0 ||
|
|
||||||
idle_thread == 0) {
|
|
||||||
panic("scheduler demo thread creation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_queue_init(&demo_wait_queue);
|
|
||||||
early_log_puts("scheduler starting\n");
|
|
||||||
sched_yield();
|
|
||||||
|
|
||||||
panic("scheduler returned to boot context");
|
|
||||||
}
|
|
||||||
|
|||||||
64
kernel/sched/wait.c
Normal file
64
kernel/sched/wait.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <tianole/sched.h>
|
||||||
|
|
||||||
|
#include "sched.h"
|
||||||
|
|
||||||
|
void wait_queue_init(struct wait_queue *queue)
|
||||||
|
{
|
||||||
|
if (queue == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->head = 0;
|
||||||
|
queue->tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread)
|
||||||
|
{
|
||||||
|
thread->wait_next = 0;
|
||||||
|
|
||||||
|
if (queue->tail != 0) {
|
||||||
|
queue->tail->wait_next = thread;
|
||||||
|
} else {
|
||||||
|
queue->head = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->tail = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_queue_sleep(struct wait_queue *queue)
|
||||||
|
{
|
||||||
|
if (queue == 0 || current_thread == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_queue_enqueue(queue, current_thread);
|
||||||
|
current_thread->state = THREAD_WAITING;
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_queue_wake_one(struct wait_queue *queue)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
|
||||||
|
if (queue == 0 || queue->head == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread = queue->head;
|
||||||
|
queue->head = thread->wait_next;
|
||||||
|
if (queue->head == 0) {
|
||||||
|
queue->tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->wait_next = 0;
|
||||||
|
if (thread->state == THREAD_WAITING) {
|
||||||
|
thread->state = THREAD_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_queue_wake_all(struct wait_queue *queue)
|
||||||
|
{
|
||||||
|
while (queue != 0 && queue->head != 0) {
|
||||||
|
wait_queue_wake_one(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
kernel/selftest/page_table.c
Normal file
43
kernel/selftest/page_table.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/early_log.h>
|
||||||
|
#include <tianole/mm.h>
|
||||||
|
|
||||||
|
#include "arch/x86/mm/page_table.h"
|
||||||
|
|
||||||
|
#define TEST_VIRTUAL_PAGE 0xffffff0000000000ull
|
||||||
|
|
||||||
|
void page_table_selftest(void)
|
||||||
|
{
|
||||||
|
phys_addr_t page = alloc_page();
|
||||||
|
phys_addr_t resolved;
|
||||||
|
volatile uint64_t *mapped =
|
||||||
|
(volatile uint64_t *)(uintptr_t)TEST_VIRTUAL_PAGE;
|
||||||
|
|
||||||
|
if (page == 0) {
|
||||||
|
panic("page table selftest allocation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
page_tables_init();
|
||||||
|
|
||||||
|
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
|
||||||
|
panic("page table selftest map failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
|
||||||
|
resolved != page) {
|
||||||
|
panic("page table selftest resolve failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
*mapped = 0x54494f4c45504d4dull;
|
||||||
|
if (*mapped != 0x54494f4c45504d4dull) {
|
||||||
|
panic("page table selftest access failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unmap_page(TEST_VIRTUAL_PAGE) != 0) {
|
||||||
|
panic("page table selftest unmap failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
free_page(page);
|
||||||
|
early_log_puts("page table selftest ok\n");
|
||||||
|
}
|
||||||
108
kernel/selftest/sched.c
Normal file
108
kernel/selftest/sched.c
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/early_log.h>
|
||||||
|
#include <tianole/sched.h>
|
||||||
|
#include <tianole/spinlock.h>
|
||||||
|
|
||||||
|
#include "sched/sched.h"
|
||||||
|
|
||||||
|
#define STACK_ALIGNMENT 16u
|
||||||
|
|
||||||
|
static void thread_selftest_entry(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_selftest(void)
|
||||||
|
{
|
||||||
|
struct spinlock test_lock;
|
||||||
|
struct thread *first =
|
||||||
|
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
||||||
|
struct thread *second =
|
||||||
|
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
||||||
|
uint64_t flags;
|
||||||
|
|
||||||
|
test_lock.locked = 0;
|
||||||
|
|
||||||
|
if (first == 0 || second == 0 || first == second) {
|
||||||
|
panic("kernel thread selftest allocation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first->id == second->id || first->state != THREAD_READY ||
|
||||||
|
second->state != THREAD_READY) {
|
||||||
|
panic("kernel thread selftest state failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((first->stack_top & (STACK_ALIGNMENT - 1)) != 0 ||
|
||||||
|
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
||||||
|
panic("kernel thread selftest stack alignment failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run_queue_head != first || first->next != second ||
|
||||||
|
run_queue_tail != second) {
|
||||||
|
panic("kernel thread selftest run queue failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&test_lock, &flags);
|
||||||
|
spin_unlock_irqrestore(&test_lock, flags);
|
||||||
|
|
||||||
|
early_log_puts("kernel thread selftest ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scheduler_demo_entry(void *arg)
|
||||||
|
{
|
||||||
|
uint64_t id = (uint64_t)(uintptr_t)arg;
|
||||||
|
uint64_t step;
|
||||||
|
|
||||||
|
for (step = 1; step <= 3; step++) {
|
||||||
|
early_log_puts("preempt thread ");
|
||||||
|
early_log_u64_decimal(id);
|
||||||
|
early_log_puts(" step=");
|
||||||
|
early_log_u64_decimal(step);
|
||||||
|
early_log_puts("\n");
|
||||||
|
sched_sleep(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wait_queue demo_wait_queue;
|
||||||
|
|
||||||
|
static void wait_queue_demo_waiter(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
early_log_puts("waiter sleeping\n");
|
||||||
|
wait_queue_sleep(&demo_wait_queue);
|
||||||
|
early_log_puts("waiter woke\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_queue_demo_waker(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
early_log_puts("waker sleeping\n");
|
||||||
|
sched_sleep(4);
|
||||||
|
early_log_puts("waker wake_one\n");
|
||||||
|
wait_queue_wake_one(&demo_wait_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_demo_start(void)
|
||||||
|
{
|
||||||
|
struct thread *first = kernel_thread_create(
|
||||||
|
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
|
||||||
|
struct thread *second = kernel_thread_create(
|
||||||
|
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
||||||
|
struct thread *waiter =
|
||||||
|
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
||||||
|
struct thread *waker =
|
||||||
|
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
||||||
|
|
||||||
|
if (first == 0 || second == 0 || waiter == 0 || waker == 0) {
|
||||||
|
panic("scheduler demo thread creation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_queue_init(&demo_wait_queue);
|
||||||
|
early_log_puts("scheduler starting\n");
|
||||||
|
sched_yield();
|
||||||
|
|
||||||
|
panic("scheduler returned to boot context");
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ set -euo pipefail
|
|||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
./scripts/checks/format.sh
|
./scripts/checks/format.sh
|
||||||
|
./scripts/checks/structure.sh
|
||||||
./scripts/checks/boot.sh
|
./scripts/checks/boot.sh
|
||||||
./scripts/checks/trap.sh
|
./scripts/checks/trap.sh
|
||||||
./scripts/checks/page-fault.sh
|
./scripts/checks/page-fault.sh
|
||||||
|
|||||||
39
scripts/checks/structure.sh
Executable file
39
scripts/checks/structure.sh
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/../.."
|
||||||
|
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "structure check failed: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_no_relative_parent_includes()
|
||||||
|
{
|
||||||
|
local matches
|
||||||
|
|
||||||
|
matches=$(find arch include kernel mm -name '*.c' -o -name '*.h' |
|
||||||
|
xargs grep -nE '^[[:space:]]*#include[[:space:]]+[<"]\.\./' || true)
|
||||||
|
|
||||||
|
if [ -n "$matches" ]; then
|
||||||
|
echo "$matches" >&2
|
||||||
|
fail "do not use ../ includes; promote shared headers or add a private include root"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_selftests_are_centralized()
|
||||||
|
{
|
||||||
|
local matches
|
||||||
|
|
||||||
|
matches=$(find arch kernel mm -name '*selftest*.c' |
|
||||||
|
grep -v '^kernel/selftest/' || true)
|
||||||
|
|
||||||
|
if [ -n "$matches" ]; then
|
||||||
|
echo "$matches" >&2
|
||||||
|
fail "kernel selftests belong under kernel/selftest/"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_no_relative_parent_includes
|
||||||
|
check_selftests_are_centralized
|
||||||
Loading…
Reference in New Issue
Block a user