752 lines
28 KiB
C
752 lines
28 KiB
C
/* sched.c — SAS-R0-PCQ 调度器实现(ROADMAP Phase 7)
|
||||
|
|
*
|
|||
|
|
* 冻结 ABI(include/utsm/sched.h)9 个 API 的真实现 + 内核扩展层
|
|||
|
|
* (include/utsm/sched_ext.h)。
|
|||
|
|
*
|
|||
|
|
* 设计要点:
|
|||
|
|
* - O(1) 热路径:ready_bitmap ctz 选最高优先级(0 最高),head[] 取出
|
|||
|
|
* 即出队;enqueue/dequeue 均为 O(1) 侵入式 FIFO(slot 索引非指针)。
|
|||
|
|
* - 抢占 = xv6 式栈驱动切换:IRQ 帧落在被抢占任务自己的栈上,切换只换
|
|||
|
|
* rsp(arch.rsp);恢复时沿原栈 iretq。TCB arch.rip/rflags 占位零。
|
|||
|
|
* - tick 源 = LAPIC timer(宿主自持,见 arch/x86_64/lapic_timer.c)。
|
|||
|
|
* - 本迭代 BSP-only:runqueue 数组结构就位,仅用 cpu0。
|
|||
|
|
* - 切换路径只触碰 crypto_context 指针;禁止扫描 capability / 计算
|
|||
|
|
* MAC / 写 checkpoint(设计约束,见 docs/RE/UTSM_算法记录.md §10)。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include <utsm/sched.h>
|
|||
|
|
#include <utsm/sched_ext.h>
|
|||
|
|
#include <utsm/log.h>
|
|||
|
|
#include <utsm/arena.h>
|
|||
|
|
#include <utsm/config.h>
|
|||
|
|
#include <utsm/process.h>
|
|||
|
|
#include <utsm/idt.h>
|
|||
|
|
#include <utsm/drr.h>
|
|||
|
|
#include <utsm/lapic_timer.h>
|
|||
|
|
#include <utsm/dkm.h>
|
|||
|
|
|
|||
|
|
void utsm_sched_switch(utsm_sched_tcb *prev, utsm_sched_tcb *next); /* switch.S */
|
|||
|
|
void utsm_task_trampoline(void); /* switch.S */
|
|||
|
|
|
|||
|
|
#define UTSM_SCHED_MAX_TASKS 64
|
|||
|
|
#define UTSM_SCHED_MAX_CPUS 8
|
|||
|
|
#define UTSM_SCHED_NO_PROCESS 0xFFFFFFFFu
|
|||
|
|
|
|||
|
|
struct task_aux {
|
|||
|
|
void (*entry)(void *);
|
|||
|
|
void *arg;
|
|||
|
|
u64 stack_base;
|
|||
|
|
u64 run_start_ns;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
static utsm_sched_tcb g_task_table[UTSM_SCHED_MAX_TASKS];
|
|||
|
|
static utsm_sched_run_queue g_runqueues[UTSM_SCHED_MAX_CPUS];
|
|||
|
|
static utsm_sched_run_queue *g_rq_ptrs[UTSM_SCHED_MAX_CPUS];
|
|||
|
|
static struct task_aux g_task_aux[UTSM_SCHED_MAX_TASKS];
|
|||
|
|
|
|||
|
|
static volatile int g_sched_enabled = 0;
|
|||
|
|
static volatile int g_switch_lock = 0;
|
|||
|
|
static u64 g_uptime_ns = 0;
|
|||
|
|
static u64 g_tick_ns = 10ULL * 1000ULL * 1000ULL; /* 100Hz */
|
|||
|
|
static u64 g_timeslice_ns = 10ULL * 1000ULL * 1000ULL; /* 10ms */
|
|||
|
|
static u32 g_cfg_tick_hz = 100;
|
|||
|
|
static u32 g_demo_ms = 1000;
|
|||
|
|
static int g_demo_fault = 0;
|
|||
|
|
static int g_wake_pending = 0;
|
|||
|
|
static u32 g_bootstrap_slot = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
|
|||
|
|
/* ---- switch.S 依赖的 TCB/arch 布局断言 ---- */
|
|||
|
|
#define OFFS(t, m) ((u32)__builtin_offsetof(t, m))
|
|||
|
|
_Static_assert(OFFS(utsm_sched_tcb, arch) == 80, "switch.S: arch@80");
|
|||
|
|
_Static_assert(OFFS(utsm_sched_arch_context, rsp) == 0, "switch.S: rsp@0");
|
|||
|
|
_Static_assert(OFFS(utsm_sched_arch_context, rbx) == 24, "switch.S: rbx@24");
|
|||
|
|
_Static_assert(OFFS(utsm_sched_arch_context, rbp) == 32, "switch.S: rbp@32");
|
|||
|
|
_Static_assert(OFFS(utsm_sched_arch_context, r12) == 40, "switch.S: r12@40");
|
|||
|
|
_Static_assert(OFFS(utsm_sched_arch_context, r15) == 64, "switch.S: r15@64");
|
|||
|
|
_Static_assert(UTSM_SCHED_PRIORITY_LOWEST == 63, "switch/bitmap: 64 prio");
|
|||
|
|
|
|||
|
|
static u32 task_slot_of(const utsm_sched_tcb *t) {
|
|||
|
|
return (u32)(t - g_task_table);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ===================================================================
|
|||
|
|
* 冻结 ABI 实现(sched.h)
|
|||
|
|
* =================================================================== */
|
|||
|
|
|
|||
|
|
int utsm_sched_init(void) {
|
|||
|
|
for (u32 i = 0; i < UTSM_SCHED_MAX_TASKS; i++) {
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[i];
|
|||
|
|
u64 *w = (u64 *)t;
|
|||
|
|
for (u64 k = 0; k < sizeof(*t) / sizeof(u64); k++) w[k] = 0;
|
|||
|
|
t->abi_version = UTSM_SCHED_ABI_VERSION;
|
|||
|
|
t->struct_size = (u32)sizeof(utsm_sched_tcb);
|
|||
|
|
t->state = UTSM_SCHED_TASK_UNUSED;
|
|||
|
|
t->runq_prev = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
t->runq_next = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
g_task_aux[i].entry = 0;
|
|||
|
|
g_task_aux[i].arg = 0;
|
|||
|
|
g_task_aux[i].stack_base = 0;
|
|||
|
|
g_task_aux[i].run_start_ns = 0;
|
|||
|
|
}
|
|||
|
|
for (u32 c = 0; c < UTSM_SCHED_MAX_CPUS; c++) g_rq_ptrs[c] = &g_runqueues[c];
|
|||
|
|
g_sched_enabled = 0;
|
|||
|
|
g_uptime_ns = 0;
|
|||
|
|
g_bootstrap_slot = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_cpu_init(u32 cpu_id, utsm_sched_run_queue *queue) {
|
|||
|
|
if (cpu_id >= UTSM_SCHED_MAX_CPUS) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
utsm_sched_run_queue *rq = queue ? queue : &g_runqueues[cpu_id];
|
|||
|
|
u64 *w = (u64 *)rq;
|
|||
|
|
for (u64 k = 0; k < sizeof(*rq) / sizeof(u64); k++) w[k] = 0;
|
|||
|
|
rq->abi_version = UTSM_SCHED_ABI_VERSION;
|
|||
|
|
rq->struct_size = (u32)sizeof(utsm_sched_run_queue);
|
|||
|
|
rq->cpu_id = cpu_id;
|
|||
|
|
for (u32 p = 0; p < UTSM_SCHED_PRIORITY_COUNT; p++) {
|
|||
|
|
rq->head[p] = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
rq->tail[p] = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
}
|
|||
|
|
rq->current_task_slot = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
rq->idle_task_slot = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
rq->ready_count = 0;
|
|||
|
|
g_rq_ptrs[cpu_id] = rq;
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_task_init(utsm_sched_tcb *task, u64 task_id,
|
|||
|
|
u32 process_slot, u32 priority, u64 affinity_mask) {
|
|||
|
|
if (!task || priority >= UTSM_SCHED_PRIORITY_COUNT) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
u32 slot = task_slot_of(task);
|
|||
|
|
if (slot >= UTSM_SCHED_MAX_TASKS) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
if (task->state != UTSM_SCHED_TASK_UNUSED) return UTSM_SCHED_ERR_STATE;
|
|||
|
|
task->task_id = task_id;
|
|||
|
|
task->process_slot = process_slot;
|
|||
|
|
task->flags = 0;
|
|||
|
|
task->state = UTSM_SCHED_TASK_NEW;
|
|||
|
|
task->priority = priority;
|
|||
|
|
task->assigned_cpu = 0;
|
|||
|
|
task->last_cpu = 0;
|
|||
|
|
task->affinity_mask = affinity_mask;
|
|||
|
|
task->timeslice_ns = 0;
|
|||
|
|
task->wake_deadline_ns = 0;
|
|||
|
|
task->crypto_context = 0;
|
|||
|
|
task->runq_prev = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
task->runq_next = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
task->arch.rsp = 0;
|
|||
|
|
task->arch.rip = 0;
|
|||
|
|
task->arch.rflags = 0;
|
|||
|
|
task->arch.rbx = 0;
|
|||
|
|
task->arch.rbp = 0;
|
|||
|
|
task->arch.r12 = 0;
|
|||
|
|
task->arch.r13 = 0;
|
|||
|
|
task->arch.r14 = 0;
|
|||
|
|
task->arch.r15 = 0;
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void rq_link(utsm_sched_run_queue *rq, utsm_sched_tcb *t) {
|
|||
|
|
u32 slot = task_slot_of(t);
|
|||
|
|
u32 p = t->priority;
|
|||
|
|
t->runq_next = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
if (rq->head[p] == UTSM_SCHED_INVALID_SLOT) {
|
|||
|
|
t->runq_prev = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
rq->head[p] = slot;
|
|||
|
|
rq->tail[p] = slot;
|
|||
|
|
rq->ready_bitmap |= 1ULL << p;
|
|||
|
|
} else {
|
|||
|
|
u32 tail = rq->tail[p];
|
|||
|
|
t->runq_prev = tail;
|
|||
|
|
g_task_table[tail].runq_next = slot;
|
|||
|
|
rq->tail[p] = slot;
|
|||
|
|
}
|
|||
|
|
rq->ready_count++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void rq_unlink(utsm_sched_run_queue *rq, utsm_sched_tcb *t) {
|
|||
|
|
u32 slot = task_slot_of(t);
|
|||
|
|
u32 p = t->priority;
|
|||
|
|
u32 prev = t->runq_prev;
|
|||
|
|
u32 next = t->runq_next;
|
|||
|
|
if (prev != UTSM_SCHED_INVALID_SLOT) g_task_table[prev].runq_next = next;
|
|||
|
|
if (next != UTSM_SCHED_INVALID_SLOT) g_task_table[next].runq_prev = prev;
|
|||
|
|
if (rq->head[p] == slot) rq->head[p] = next;
|
|||
|
|
if (rq->tail[p] == slot) rq->tail[p] = prev;
|
|||
|
|
if (rq->head[p] == UTSM_SCHED_INVALID_SLOT) {
|
|||
|
|
rq->ready_bitmap &= ~(1ULL << p);
|
|||
|
|
}
|
|||
|
|
t->runq_prev = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
t->runq_next = UTSM_SCHED_INVALID_SLOT;
|
|||
|
|
if (rq->ready_count > 0) rq->ready_count--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_enqueue(u32 cpu_id, utsm_sched_tcb *task) {
|
|||
|
|
if (!task || cpu_id >= UTSM_SCHED_MAX_CPUS) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
if (task->state == UTSM_SCHED_TASK_READY ||
|
|||
|
|
task->state == UTSM_SCHED_TASK_RUNNING) {
|
|||
|
|
return UTSM_SCHED_ERR_STATE;
|
|||
|
|
}
|
|||
|
|
task->assigned_cpu = cpu_id;
|
|||
|
|
task->state = UTSM_SCHED_TASK_READY;
|
|||
|
|
rq_link(g_rq_ptrs[cpu_id], task);
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_dequeue(utsm_sched_tcb *task) {
|
|||
|
|
if (!task) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
if (task->state != UTSM_SCHED_TASK_READY) return UTSM_SCHED_OK; /* 幂等 */
|
|||
|
|
rq_unlink(g_rq_ptrs[task->assigned_cpu], task);
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_block(utsm_sched_tcb *task, u32 blocked_state) {
|
|||
|
|
if (!task) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
if (blocked_state != UTSM_SCHED_TASK_BLOCKED &&
|
|||
|
|
blocked_state != UTSM_SCHED_TASK_SLEEPING) {
|
|||
|
|
return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
}
|
|||
|
|
if (task->state == UTSM_SCHED_TASK_READY) {
|
|||
|
|
rq_unlink(g_rq_ptrs[task->assigned_cpu], task);
|
|||
|
|
} else if (task->state != UTSM_SCHED_TASK_RUNNING) {
|
|||
|
|
return UTSM_SCHED_ERR_STATE;
|
|||
|
|
}
|
|||
|
|
task->state = blocked_state;
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_wake(utsm_sched_tcb *task) {
|
|||
|
|
if (!task) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
if (task->state != UTSM_SCHED_TASK_BLOCKED &&
|
|||
|
|
task->state != UTSM_SCHED_TASK_SLEEPING) {
|
|||
|
|
return UTSM_SCHED_ERR_STATE;
|
|||
|
|
}
|
|||
|
|
return utsm_sched_enqueue(task->assigned_cpu, task);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* pick 即出队;纯队列操作,不改 current_task_slot(selftest 依赖此语义) */
|
|||
|
|
int utsm_sched_pick_next(u32 cpu_id, utsm_sched_tcb **out_task) {
|
|||
|
|
if (!out_task || cpu_id >= UTSM_SCHED_MAX_CPUS) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
utsm_sched_run_queue *rq = g_rq_ptrs[cpu_id];
|
|||
|
|
if (rq->ready_bitmap == 0) {
|
|||
|
|
*out_task = 0;
|
|||
|
|
return UTSM_SCHED_ERR_NO_TASK;
|
|||
|
|
}
|
|||
|
|
u32 p = (u32)__builtin_ctzll(rq->ready_bitmap); /* 0 = 最高优先级 */
|
|||
|
|
u32 slot = rq->head[p];
|
|||
|
|
if (slot >= UTSM_SCHED_MAX_TASKS) {
|
|||
|
|
*out_task = 0;
|
|||
|
|
return UTSM_SCHED_ERR_NO_TASK;
|
|||
|
|
}
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[slot];
|
|||
|
|
rq_unlink(rq, t);
|
|||
|
|
t->state = UTSM_SCHED_TASK_RUNNING;
|
|||
|
|
t->last_cpu = cpu_id;
|
|||
|
|
g_task_aux[slot].run_start_ns = g_uptime_ns;
|
|||
|
|
*out_task = t;
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_tick(u32 cpu_id, u64 elapsed_ns, utsm_sched_tcb **out_preempted_task) {
|
|||
|
|
if (out_preempted_task) *out_preempted_task = 0;
|
|||
|
|
g_wake_pending = 0;
|
|||
|
|
g_uptime_ns += elapsed_ns;
|
|||
|
|
|
|||
|
|
/* 唤醒到期 sleeper(非热路径:任务数 <= 64 线性扫描) */
|
|||
|
|
for (u32 i = 0; i < UTSM_SCHED_MAX_TASKS; i++) {
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[i];
|
|||
|
|
if (t->state == UTSM_SCHED_TASK_SLEEPING &&
|
|||
|
|
t->wake_deadline_ns != 0 && t->wake_deadline_ns <= g_uptime_ns) {
|
|||
|
|
t->wake_deadline_ns = 0;
|
|||
|
|
utsm_sched_wake(t);
|
|||
|
|
g_wake_pending = 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
utsm_sched_run_queue *rq = g_rq_ptrs[cpu_id];
|
|||
|
|
u32 cur_slot = rq->current_task_slot;
|
|||
|
|
if (cur_slot == UTSM_SCHED_INVALID_SLOT) return UTSM_SCHED_OK;
|
|||
|
|
utsm_sched_tcb *cur = &g_task_table[cur_slot];
|
|||
|
|
if (cur->state != UTSM_SCHED_TASK_RUNNING) return UTSM_SCHED_OK;
|
|||
|
|
u64 ran = g_uptime_ns - g_task_aux[cur_slot].run_start_ns;
|
|||
|
|
if (ran >= g_timeslice_ns && out_preempted_task) {
|
|||
|
|
*out_preempted_task = cur;
|
|||
|
|
}
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ===================================================================
|
|||
|
|
* 内核扩展层(sched_ext.h)
|
|||
|
|
* =================================================================== */
|
|||
|
|
|
|||
|
|
void utsm_sched_config(u32 tick_hz, u32 timeslice_ms, u32 demo_ms, int demo_fault) {
|
|||
|
|
g_cfg_tick_hz = tick_hz ? tick_hz : 100;
|
|||
|
|
if (timeslice_ms) g_timeslice_ns = (u64)timeslice_ms * 1000000ULL;
|
|||
|
|
g_demo_ms = demo_ms;
|
|||
|
|
g_demo_fault = demo_fault;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_bootstrap_init(void) {
|
|||
|
|
utsm_sched_init();
|
|||
|
|
utsm_sched_cpu_init(0, 0);
|
|||
|
|
|
|||
|
|
u32 slot = UTSM_SCHED_MAX_TASKS;
|
|||
|
|
for (u32 i = 0; i < UTSM_SCHED_MAX_TASKS; i++) {
|
|||
|
|
if (g_task_table[i].state == UTSM_SCHED_TASK_UNUSED) { slot = i; break; }
|
|||
|
|
}
|
|||
|
|
if (slot == UTSM_SCHED_MAX_TASKS) return UTSM_SCHED_ERR_NO_TASK;
|
|||
|
|
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[slot];
|
|||
|
|
utsm_sched_task_init(t, 1, UTSM_SCHED_NO_PROCESS,
|
|||
|
|
UTSM_SCHED_PRIORITY_LOWEST, ~0ULL);
|
|||
|
|
t->flags = UTSM_SCHED_TASK_F_KERNEL | UTSM_SCHED_TASK_F_NO_MIGRATE;
|
|||
|
|
t->state = UTSM_SCHED_TASK_RUNNING;
|
|||
|
|
u64 rsp;
|
|||
|
|
__asm__ volatile("mov %%rsp, %0" : "=r"(rsp));
|
|||
|
|
t->arch.rsp = rsp; /* bootstrap 复用 boot.S kernel_stack,不迁移栈 */
|
|||
|
|
|
|||
|
|
g_task_aux[slot].entry = 0;
|
|||
|
|
g_task_aux[slot].arg = 0;
|
|||
|
|
g_task_aux[slot].stack_base = 0;
|
|||
|
|
g_task_aux[slot].run_start_ns = 0;
|
|||
|
|
g_rq_ptrs[0]->current_task_slot = slot;
|
|||
|
|
g_bootstrap_slot = slot;
|
|||
|
|
|
|||
|
|
log_info("[SCHED] bootstrap bound");
|
|||
|
|
log_hex64("[SCHED] slot=", slot);
|
|||
|
|
return (int)slot;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_task_create(u32 process_slot, u32 priority, void (*entry)(void *), void *arg) {
|
|||
|
|
if (!entry || priority >= UTSM_SCHED_PRIORITY_COUNT) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
|
|||
|
|
u32 slot = UTSM_SCHED_MAX_TASKS;
|
|||
|
|
for (u32 i = 0; i < UTSM_SCHED_MAX_TASKS; i++) {
|
|||
|
|
if (g_task_table[i].state == UTSM_SCHED_TASK_UNUSED) { slot = i; break; }
|
|||
|
|
}
|
|||
|
|
if (slot == UTSM_SCHED_MAX_TASKS) return UTSM_SCHED_ERR_NO_TASK;
|
|||
|
|
|
|||
|
|
void *stack = kmem_alloc_page(4); /* 16KB 任务栈(arena,非 DMA 资源) */
|
|||
|
|
if (!stack) return UTSM_SCHED_ERR_NO_TASK;
|
|||
|
|
u64 top = ((u64)stack + 4 * UTSM_PAGE_SIZE) & ~15ULL;
|
|||
|
|
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[slot];
|
|||
|
|
int rc = utsm_sched_task_init(t, 0x1000ULL + slot, process_slot, priority, ~0ULL);
|
|||
|
|
if (rc != UTSM_SCHED_OK) return rc;
|
|||
|
|
t->flags = UTSM_SCHED_TASK_F_KERNEL;
|
|||
|
|
|
|||
|
|
/* crypto_context 指针:切换路径唯一触碰的加密状态 */
|
|||
|
|
if (process_slot != UTSM_SCHED_NO_PROCESS) {
|
|||
|
|
utsm_process_context *proc = utsm_process_get(process_slot);
|
|||
|
|
if (proc) t->crypto_context = &proc->crypto_ctx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 伪造初始栈帧(与 switch.S 的 push/pop 布局逐字节对应):
|
|||
|
|
* sp[0]=r15 sp[1]=r14 sp[2]=r13 sp[3]=r12 sp[4]=rbx(slot) sp[5]=rbp
|
|||
|
|
* sp[6]=&trampoline;ret 后 rsp=top(16 对齐,满足 call ABI) */
|
|||
|
|
u64 *sp = (u64 *)(top - 56);
|
|||
|
|
sp[0] = 0;
|
|||
|
|
sp[1] = 0;
|
|||
|
|
sp[2] = 0;
|
|||
|
|
sp[3] = 0;
|
|||
|
|
sp[4] = (u64)slot;
|
|||
|
|
sp[5] = 0;
|
|||
|
|
sp[6] = (u64)&utsm_task_trampoline;
|
|||
|
|
t->arch.rsp = (u64)sp;
|
|||
|
|
|
|||
|
|
g_task_aux[slot].entry = entry;
|
|||
|
|
g_task_aux[slot].arg = arg;
|
|||
|
|
g_task_aux[slot].stack_base = (u64)stack;
|
|||
|
|
g_task_aux[slot].run_start_ns = 0;
|
|||
|
|
|
|||
|
|
rc = utsm_sched_enqueue(0, t);
|
|||
|
|
if (rc != UTSM_SCHED_OK) return rc;
|
|||
|
|
return (int)slot;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_task_main(u64 slot) {
|
|||
|
|
if (slot >= UTSM_SCHED_MAX_TASKS) {
|
|||
|
|
drr_system_rollback("scheduler: trampoline bad slot");
|
|||
|
|
}
|
|||
|
|
struct task_aux *aux = &g_task_aux[slot];
|
|||
|
|
void (*entry)(void *) = aux->entry;
|
|||
|
|
void *arg = aux->arg;
|
|||
|
|
if (entry) entry(arg);
|
|||
|
|
utsm_sched_task_exit();
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_schedule(void) {
|
|||
|
|
utsm_sched_run_queue *rq = g_rq_ptrs[0];
|
|||
|
|
u32 cur_slot = rq->current_task_slot;
|
|||
|
|
if (cur_slot == UTSM_SCHED_INVALID_SLOT) return;
|
|||
|
|
utsm_sched_tcb *cur = &g_task_table[cur_slot];
|
|||
|
|
|
|||
|
|
u64 flags;
|
|||
|
|
__asm__ volatile("pushfq; popq %0; cli" : "=r"(flags) :: "memory");
|
|||
|
|
g_switch_lock = 1;
|
|||
|
|
|
|||
|
|
if (cur->state == UTSM_SCHED_TASK_RUNNING) {
|
|||
|
|
cur->state = UTSM_SCHED_TASK_READY;
|
|||
|
|
rq_link(rq, cur);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
utsm_sched_tcb *next = 0;
|
|||
|
|
int rc = utsm_sched_pick_next(0, &next);
|
|||
|
|
if (rc != UTSM_SCHED_OK || !next) {
|
|||
|
|
if (cur->state == UTSM_SCHED_TASK_READY) {
|
|||
|
|
/* 仅当前任务可运行:取回继续 */
|
|||
|
|
rq_unlink(rq, cur);
|
|||
|
|
cur->state = UTSM_SCHED_TASK_RUNNING;
|
|||
|
|
g_task_aux[cur_slot].run_start_ns = g_uptime_ns;
|
|||
|
|
g_switch_lock = 0;
|
|||
|
|
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
/* 当前任务不可运行且无任务可切换 → 恢复根接管 */
|
|||
|
|
g_switch_lock = 0;
|
|||
|
|
drr_system_rollback("scheduler: no runnable task");
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
if (next == cur) {
|
|||
|
|
cur->state = UTSM_SCHED_TASK_RUNNING;
|
|||
|
|
g_task_aux[cur_slot].run_start_ns = g_uptime_ns;
|
|||
|
|
g_switch_lock = 0;
|
|||
|
|
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
rq->current_task_slot = task_slot_of(next);
|
|||
|
|
g_switch_lock = 0;
|
|||
|
|
utsm_sched_switch(cur, next); /* 切走;恢复点在下一行 */
|
|||
|
|
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_yield(void) {
|
|||
|
|
utsm_sched_schedule();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_sleep_ms(u32 ms) {
|
|||
|
|
utsm_sched_run_queue *rq = g_rq_ptrs[0];
|
|||
|
|
u32 cur_slot = rq->current_task_slot;
|
|||
|
|
if (cur_slot == UTSM_SCHED_INVALID_SLOT) return;
|
|||
|
|
utsm_sched_tcb *cur = &g_task_table[cur_slot];
|
|||
|
|
cur->wake_deadline_ns = g_uptime_ns + (u64)ms * 1000000ULL;
|
|||
|
|
utsm_sched_block(cur, UTSM_SCHED_TASK_SLEEPING);
|
|||
|
|
utsm_sched_schedule();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_task_exit(void) {
|
|||
|
|
utsm_sched_run_queue *rq = g_rq_ptrs[0];
|
|||
|
|
u32 cur_slot = rq->current_task_slot;
|
|||
|
|
if (cur_slot == UTSM_SCHED_INVALID_SLOT) {
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
utsm_sched_tcb *cur = &g_task_table[cur_slot];
|
|||
|
|
|
|||
|
|
/* crypto erase:推进纪元使该进程旧密文全部失效 */
|
|||
|
|
utsm_crypto_context *cc = (utsm_crypto_context *)cur->crypto_context;
|
|||
|
|
if (cc) cc->crypto_epoch++;
|
|||
|
|
drr_watchdog_unregister(cur_slot); /* 防止看门狗对已退出任务误报 */
|
|||
|
|
cur->state = UTSM_SCHED_TASK_ZOMBIE;
|
|||
|
|
|
|||
|
|
__asm__ volatile("cli" ::: "memory");
|
|||
|
|
utsm_sched_tcb *next = 0;
|
|||
|
|
if (utsm_sched_pick_next(0, &next) != UTSM_SCHED_OK || !next) {
|
|||
|
|
drr_system_rollback("scheduler: no task after exit");
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
rq->current_task_slot = task_slot_of(next);
|
|||
|
|
utsm_sched_switch(cur, next); /* 不返回 */
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_task_kill(u32 task_slot) {
|
|||
|
|
if (task_slot >= UTSM_SCHED_MAX_TASKS) return UTSM_SCHED_ERR_INVALID;
|
|||
|
|
if (task_slot == g_bootstrap_slot) return UTSM_SCHED_ERR_STATE;
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[task_slot];
|
|||
|
|
if (t->state == UTSM_SCHED_TASK_UNUSED || t->state == UTSM_SCHED_TASK_ZOMBIE) {
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
if (t->state == UTSM_SCHED_TASK_RUNNING) return UTSM_SCHED_ERR_STATE;
|
|||
|
|
utsm_crypto_context *cc = (utsm_crypto_context *)t->crypto_context;
|
|||
|
|
if (cc) cc->crypto_epoch++;
|
|||
|
|
drr_watchdog_unregister(task_slot);
|
|||
|
|
utsm_sched_dequeue(t);
|
|||
|
|
t->state = UTSM_SCHED_TASK_ZOMBIE;
|
|||
|
|
return UTSM_SCHED_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_fault_task(u32 task_slot) {
|
|||
|
|
if (task_slot >= UTSM_SCHED_MAX_TASKS) return;
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[task_slot];
|
|||
|
|
if (t->state == UTSM_SCHED_TASK_UNUSED || t->state == UTSM_SCHED_TASK_ZOMBIE) return;
|
|||
|
|
|
|||
|
|
utsm_crypto_context *cc = (utsm_crypto_context *)t->crypto_context;
|
|||
|
|
if (cc) cc->crypto_epoch++;
|
|||
|
|
drr_watchdog_unregister(task_slot);
|
|||
|
|
t->state = UTSM_SCHED_TASK_FAULTED;
|
|||
|
|
utsm_sched_dequeue(t);
|
|||
|
|
|
|||
|
|
utsm_sched_run_queue *rq = g_rq_ptrs[0];
|
|||
|
|
if (rq->current_task_slot == task_slot) {
|
|||
|
|
/* fault 任务是当前任务(tick/异常上下文):杀掉并切走,不返回 */
|
|||
|
|
t->state = UTSM_SCHED_TASK_ZOMBIE;
|
|||
|
|
__asm__ volatile("cli" ::: "memory");
|
|||
|
|
utsm_sched_tcb *next = 0;
|
|||
|
|
if (utsm_sched_pick_next(0, &next) != UTSM_SCHED_OK || !next) {
|
|||
|
|
drr_system_rollback("scheduler: no task after fault");
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
rq->current_task_slot = task_slot_of(next);
|
|||
|
|
utsm_sched_switch(t, next);
|
|||
|
|
for (;;) { __asm__ volatile("cli; hlt"); }
|
|||
|
|
}
|
|||
|
|
t->state = UTSM_SCHED_TASK_ZOMBIE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
u32 utsm_sched_current_slot(void) {
|
|||
|
|
return g_rq_ptrs[0]->current_task_slot;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ===================================================================
|
|||
|
|
* tick / 异常钩子 / enable / quiesce / demo
|
|||
|
|
* =================================================================== */
|
|||
|
|
|
|||
|
|
static int utsm_tick_handler(u8 vector) {
|
|||
|
|
(void)vector;
|
|||
|
|
lapic_timer_eoi(); /* EOI 先行:切换不得悬挂 in-service 位 */
|
|||
|
|
drr_heartbeat_tick();
|
|||
|
|
if (!g_sched_enabled) return 1;
|
|||
|
|
drr_watchdog_check(); /* 可能杀当前任务并切换(不返回) */
|
|||
|
|
utsm_sched_tcb *preempted = 0;
|
|||
|
|
utsm_sched_tick(0, g_tick_ns, &preempted);
|
|||
|
|
if (preempted || g_wake_pending) {
|
|||
|
|
utsm_sched_schedule();
|
|||
|
|
}
|
|||
|
|
return 1; /* 自负 EOI 全责(idt.c 约定) */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int utsm_sched_exc_hook(u64 vector, u64 err, u64 rip) {
|
|||
|
|
(void)err;
|
|||
|
|
if (!g_sched_enabled) return 0; /* 原路径:全量日志 + 停机 */
|
|||
|
|
u32 cur = g_rq_ptrs[0]->current_task_slot;
|
|||
|
|
if (cur == UTSM_SCHED_INVALID_SLOT || cur == g_bootstrap_slot) return 0;
|
|||
|
|
/* 任务级 fault:DRR 接管(页级回滚 + 杀任务 + 切换),本函数不返回 */
|
|||
|
|
log_error("[SCHED] task fault");
|
|||
|
|
log_hex64("[SCHED] vector=", vector);
|
|||
|
|
log_hex64("[SCHED] rip=", rip);
|
|||
|
|
drr_handle_task_fault(cur, "cpu exception");
|
|||
|
|
return 1; /* 不可达:fault 路径已切换任务或 system rollback */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_enable(u32 tick_hz) {
|
|||
|
|
if (tick_hz == 0) tick_hz = g_cfg_tick_hz;
|
|||
|
|
g_tick_ns = 1000000000ULL / tick_hz;
|
|||
|
|
|
|||
|
|
if (lapic_timer_init(tick_hz) != 0) {
|
|||
|
|
log_error("[SCHED] lapic timer init failed, stays cooperative");
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
int vec = lapic_timer_vector();
|
|||
|
|
if (vec < 0 || irq_register((u8)vec, utsm_tick_handler) != 0) {
|
|||
|
|
lapic_timer_stop();
|
|||
|
|
log_error("[SCHED] tick handler registration failed");
|
|||
|
|
return -2;
|
|||
|
|
}
|
|||
|
|
idt_set_exception_hook(utsm_sched_exc_hook);
|
|||
|
|
drr_set_sched_fault_cb(utsm_sched_fault_task);
|
|||
|
|
lapic_timer_start(); /* handler 就位后再放开投递 */
|
|||
|
|
g_sched_enabled = 1;
|
|||
|
|
log_info("[SCHED] enable (LAPIC timer)");
|
|||
|
|
log_hex64("[SCHED] tick_hz=", tick_hz);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_quiesce(void) {
|
|||
|
|
__asm__ volatile("cli" ::: "memory");
|
|||
|
|
g_sched_enabled = 0;
|
|||
|
|
idt_set_exception_hook(0); /* 恢复 DSK 阶段异常诊断行为 */
|
|||
|
|
drr_set_sched_fault_cb(0);
|
|||
|
|
lapic_timer_stop();
|
|||
|
|
for (u32 i = 0; i < UTSM_SCHED_MAX_TASKS; i++) {
|
|||
|
|
if (i == g_bootstrap_slot) continue;
|
|||
|
|
utsm_sched_tcb *t = &g_task_table[i];
|
|||
|
|
if (t->state == UTSM_SCHED_TASK_UNUSED || t->state == UTSM_SCHED_TASK_ZOMBIE) continue;
|
|||
|
|
utsm_crypto_context *cc = (utsm_crypto_context *)t->crypto_context;
|
|||
|
|
if (cc) cc->crypto_epoch++;
|
|||
|
|
utsm_sched_dequeue(t);
|
|||
|
|
t->state = UTSM_SCHED_TASK_ZOMBIE;
|
|||
|
|
}
|
|||
|
|
log_info("[SCHED] quiesce");
|
|||
|
|
log_info("[SCHED] timer stopped");
|
|||
|
|
/* BUG-GP-IRET: 保持 IF=0 交接不变式(恢复漂移前行为)。
|
|||
|
|
* VM-exit 后 host RFLAGS=0x2(IF=0),DSM/selftest/DSK 交接全程 IF=0;
|
|||
|
|
* 此处 sti 会让 apic_route_legacy=1 放开的 IRQ1/11/12 在 LAPIC IRR
|
|||
|
|
* 挂起的中断于 dsk_load_and_jump 首条日志(log_emit popfq 恢复 IF=1)
|
|||
|
|
* 之后立即投递,其 handler 返回的 iretq 触发 #GP(err=0)。DSK 加载
|
|||
|
|
* 失败路径的日志在 IF=0 下同样可用(log_emit 不依赖 IF)。 */
|
|||
|
|
__asm__ volatile("cli" ::: "memory");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ---- demo 任务(抢占证据:日志交错 + 看门狗负向演练) ---- */
|
|||
|
|
|
|||
|
|
static void demo_task_body(void *arg) {
|
|||
|
|
u64 id = (u64)arg; /* 1=A, 2=B, 3=C */
|
|||
|
|
static const char *const born_msg[4] = { "", "[TASK A] born", "[TASK B] born", "[TASK C] born" };
|
|||
|
|
static const char *const iter_msg[4] = { "", "[TASK A] iter=", "[TASK B] iter=", "[TASK C] iter=" };
|
|||
|
|
static const char *const prio_msg[4] = { "", "[TASK A] prio=", "[TASK B] prio=", "[TASK C] prio=" };
|
|||
|
|
static const char *const exit_msg[4] = { "", "[TASK A] exit", "[TASK B] exit", "[TASK C] exit" };
|
|||
|
|
u32 idx = (id < 4) ? (u32)id : 0;
|
|||
|
|
|
|||
|
|
u32 slot = utsm_sched_current_slot();
|
|||
|
|
u32 prio = g_task_table[slot].priority;
|
|||
|
|
log_info(born_msg[idx]);
|
|||
|
|
log_hex64(prio_msg[idx], prio);
|
|||
|
|
drr_watchdog_register(slot, 40); /* 400ms @100Hz */
|
|||
|
|
|
|||
|
|
for (u64 iter = 1; iter <= 3; iter++) {
|
|||
|
|
drr_watchdog_kick(slot);
|
|||
|
|
log_hex64(iter_msg[idx], iter);
|
|||
|
|
utsm_sched_sleep_ms(50);
|
|||
|
|
if (g_demo_fault && id == 3) {
|
|||
|
|
/* 负向演练:停止 kick + 忙循环 → 看门狗 400ms 后捕获本任务 */
|
|||
|
|
for (;;) { __asm__ volatile("pause"); }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
log_info(exit_msg[idx]);
|
|||
|
|
/* entry 返回 → trampoline 自动 task_exit */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void utsm_sched_demo_window(void) {
|
|||
|
|
if (g_demo_ms == 0) {
|
|||
|
|
log_info("[SCHED] demo disabled (demo_ms=0)");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
log_hex64("[SCHED] demo window enter, demo_ms=", g_demo_ms);
|
|||
|
|
|
|||
|
|
int a = utsm_task_create(UTSM_SCHED_NO_PROCESS, 1, demo_task_body, (void *)1ULL);
|
|||
|
|
int b = utsm_task_create(UTSM_SCHED_NO_PROCESS, 20, demo_task_body, (void *)2ULL);
|
|||
|
|
int c = utsm_task_create(UTSM_SCHED_NO_PROCESS, 40, demo_task_body, (void *)3ULL);
|
|||
|
|
log_hex64("[SCHED] demo tasks=", (u64)((a >= 0) + (b >= 0) + (c >= 0)));
|
|||
|
|
|
|||
|
|
if (utsm_sched_enable(0) != 0) {
|
|||
|
|
log_warn("[SCHED] enable failed, demo window skipped");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* bootstrap 忙等窗口:bootstrap 优先级最低(63),每个时间片被
|
|||
|
|
* tick 抢占后 demo 任务优先获得 CPU → 日志交错 = 抢占证据。
|
|||
|
|
* 失速兜底:若心跳长时间无进展(tick 源失效/未投递),立即中止
|
|||
|
|
* 窗口继续启动,绝不卡死 bootstrap。 */
|
|||
|
|
u64 start = drr_heartbeat_get();
|
|||
|
|
u64 ticks_needed = ((u64)g_demo_ms * 1000000ULL) / g_tick_ns + 2;
|
|||
|
|
u64 last_hb = start;
|
|||
|
|
u64 spin = 0;
|
|||
|
|
while (drr_heartbeat_get() - start < ticks_needed) {
|
|||
|
|
__asm__ volatile("pause");
|
|||
|
|
if (++spin >= 20000000ULL) { /* ~0.1-0.5s 无进展即复查 */
|
|||
|
|
u64 now = drr_heartbeat_get();
|
|||
|
|
if (now == last_hb) {
|
|||
|
|
log_warn("[SCHED] tick stall detected, abort demo window");
|
|||
|
|
log_hex64("[SCHED] heartbeat=", now);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
last_hb = now;
|
|||
|
|
spin = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
log_info("[SCHED] demo window done");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ===================================================================
|
|||
|
|
* kernel_api 服务表 + selftest 钩子
|
|||
|
|
* =================================================================== */
|
|||
|
|
|
|||
|
|
static u64 sched_api_uptime_ns(void) { return g_uptime_ns; }
|
|||
|
|
|
|||
|
|
static const utsm_sched_api g_sched_api = {
|
|||
|
|
.uptime_ns = sched_api_uptime_ns,
|
|||
|
|
.yield = utsm_sched_yield,
|
|||
|
|
.current_task_slot = utsm_sched_current_slot,
|
|||
|
|
.task_create = utsm_task_create,
|
|||
|
|
.sleep_ms = utsm_sched_sleep_ms
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const utsm_sched_api *utsm_sched_get_api(void) {
|
|||
|
|
return &g_sched_api;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ---- selftest(协作式,无 tick) ---- */
|
|||
|
|
|
|||
|
|
static u32 st_alloc_slot(void) {
|
|||
|
|
for (u32 i = 0; i < UTSM_SCHED_MAX_TASKS; i++) {
|
|||
|
|
if (g_task_table[i].state == UTSM_SCHED_TASK_UNUSED) return i;
|
|||
|
|
}
|
|||
|
|
return UTSM_SCHED_MAX_TASKS;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_selftest_runqueue(void) {
|
|||
|
|
utsm_sched_tcb *t[3];
|
|||
|
|
const u32 prios[3] = { 5, 10, 63 };
|
|||
|
|
for (int k = 0; k < 3; k++) {
|
|||
|
|
u32 slot = st_alloc_slot();
|
|||
|
|
if (slot == UTSM_SCHED_MAX_TASKS) return -1;
|
|||
|
|
t[k] = &g_task_table[slot];
|
|||
|
|
if (utsm_sched_task_init(t[k], 0x9000ULL + k, UTSM_SCHED_NO_PROCESS,
|
|||
|
|
prios[k], ~0ULL) != UTSM_SCHED_OK) return -1;
|
|||
|
|
t[k]->flags = UTSM_SCHED_TASK_F_KERNEL;
|
|||
|
|
if (utsm_sched_enqueue(0, t[k]) != UTSM_SCHED_OK) return -1;
|
|||
|
|
}
|
|||
|
|
/* 三轮:pick 顺序必须 5,10,63(pick 即出队,不重排 → 下一优先级) */
|
|||
|
|
for (int round = 0; round < 3; round++) {
|
|||
|
|
for (int k = 0; k < 3; k++) {
|
|||
|
|
utsm_sched_tcb *p = 0;
|
|||
|
|
if (utsm_sched_pick_next(0, &p) != UTSM_SCHED_OK || p != t[k]) return -2;
|
|||
|
|
p->state = UTSM_SCHED_TASK_NEW; /* 模拟"不在队列" */
|
|||
|
|
}
|
|||
|
|
for (int k = 0; k < 3; k++) {
|
|||
|
|
if (utsm_sched_enqueue(0, t[k]) != UTSM_SCHED_OK) return -3;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
for (int k = 0; k < 3; k++) {
|
|||
|
|
utsm_sched_dequeue(t[k]);
|
|||
|
|
t[k]->state = UTSM_SCHED_TASK_UNUSED;
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_selftest_blockwake(void) {
|
|||
|
|
u32 sa = st_alloc_slot();
|
|||
|
|
if (sa == UTSM_SCHED_MAX_TASKS) return -1;
|
|||
|
|
utsm_sched_tcb *a = &g_task_table[sa];
|
|||
|
|
if (utsm_sched_task_init(a, 0xA001, UTSM_SCHED_NO_PROCESS, 20, ~0ULL) != UTSM_SCHED_OK) return -1;
|
|||
|
|
u32 sb = st_alloc_slot(); /* a 已 init(NEW),不会重复分配同一 slot */
|
|||
|
|
if (sb == UTSM_SCHED_MAX_TASKS) return -1;
|
|||
|
|
utsm_sched_tcb *b = &g_task_table[sb];
|
|||
|
|
if (utsm_sched_task_init(b, 0xA002, UTSM_SCHED_NO_PROCESS, 30, ~0ULL) != UTSM_SCHED_OK) return -1;
|
|||
|
|
utsm_sched_enqueue(0, a);
|
|||
|
|
utsm_sched_enqueue(0, b);
|
|||
|
|
|
|||
|
|
/* block a → pick 跳过 a 取 b */
|
|||
|
|
utsm_sched_block(a, UTSM_SCHED_TASK_BLOCKED);
|
|||
|
|
utsm_sched_tcb *p = 0;
|
|||
|
|
if (utsm_sched_pick_next(0, &p) != UTSM_SCHED_OK || p != b) return -2;
|
|||
|
|
p->state = UTSM_SCHED_TASK_NEW;
|
|||
|
|
|
|||
|
|
/* wake a → 回到队列 → pick 取 a */
|
|||
|
|
utsm_sched_wake(a);
|
|||
|
|
if (utsm_sched_pick_next(0, &p) != UTSM_SCHED_OK || p != a) return -3;
|
|||
|
|
p->state = UTSM_SCHED_TASK_NEW;
|
|||
|
|
|
|||
|
|
utsm_sched_dequeue(a);
|
|||
|
|
utsm_sched_dequeue(b);
|
|||
|
|
a->state = UTSM_SCHED_TASK_UNUSED;
|
|||
|
|
b->state = UTSM_SCHED_TASK_UNUSED;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int utsm_sched_debug_ready_count(u32 cpu_id) {
|
|||
|
|
if (cpu_id >= UTSM_SCHED_MAX_CPUS) return -1;
|
|||
|
|
return (int)g_rq_ptrs[cpu_id]->ready_count;
|
|||
|
|
}
|