mirror of
https://github.com/rdmsr/zag.git
synced 2026-11-01 23:58:21 +00:00
fix(ke/sched): fix various bugs
This commit is contained in:
@@ -40,11 +40,11 @@ pub const ThreadContext = extern struct {
|
|||||||
var ctx: @This() = undefined;
|
var ctx: @This() = undefined;
|
||||||
var sp: usize = stack + stack_size;
|
var sp: usize = stack + stack_size;
|
||||||
|
|
||||||
if (sp % 16 != 0) {
|
sp = sp & ~@as(usize, 15);
|
||||||
sp -= (sp % 16);
|
sp = sp - @sizeOf(ThreadFrame);
|
||||||
}
|
sp = (sp & ~@as(usize, 15)) - 8;
|
||||||
|
|
||||||
const frame: *ThreadFrame = @ptrFromInt(sp - @sizeOf(ThreadFrame));
|
const frame: *ThreadFrame = @ptrFromInt(sp);
|
||||||
|
|
||||||
frame.* = std.mem.zeroes(ThreadFrame);
|
frame.* = std.mem.zeroes(ThreadFrame);
|
||||||
|
|
||||||
|
|||||||
+35
-13
@@ -185,6 +185,9 @@ pub fn handle_preemption(cpu: *PerCpu) void {
|
|||||||
cpu.queues_lock.release_no_ipl();
|
cpu.queues_lock.release_no_ipl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next.lock.acquire_no_ipl();
|
||||||
|
next.lock.release_no_ipl();
|
||||||
|
|
||||||
do_switch(cpu, cur, next);
|
do_switch(cpu, cur, next);
|
||||||
|
|
||||||
// cur.lock dropped
|
// cur.lock dropped
|
||||||
@@ -244,21 +247,27 @@ pub fn block() void {
|
|||||||
const td = percpu.local().current_thread.?;
|
const td = percpu.local().current_thread.?;
|
||||||
|
|
||||||
td.lock.acquire_no_ipl();
|
td.lock.acquire_no_ipl();
|
||||||
|
block_locked(td);
|
||||||
td.state = .Blocked;
|
|
||||||
td.sleep_start = ke.time.read_time();
|
|
||||||
td.runq = null;
|
|
||||||
|
|
||||||
yield(ke.cpu.current());
|
|
||||||
|
|
||||||
// td lock released
|
// td lock released
|
||||||
ke.ipl.lower(ipl);
|
ke.ipl.lower(ipl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Block the currently running thread with its lock held.
|
||||||
|
pub fn block_locked(curtd: *ke.Thread) void {
|
||||||
|
curtd.state = .Blocked;
|
||||||
|
curtd.sleep_start = ke.time.read_time();
|
||||||
|
curtd.runq = null;
|
||||||
|
|
||||||
|
yield(ke.cpu.current());
|
||||||
|
}
|
||||||
|
|
||||||
/// Unblock a thread.
|
/// Unblock a thread.
|
||||||
pub fn unblock(td: *ke.Thread) void {
|
pub fn unblock(td: *ke.Thread) void {
|
||||||
const ipl = td.lock.acquire();
|
const ipl = td.lock.acquire();
|
||||||
|
|
||||||
|
std.debug.assert(td.state == .Blocked);
|
||||||
|
|
||||||
td.sleep_time = (ke.time.read_time() - td.sleep_start) / std.time.ns_per_ms;
|
td.sleep_time = (ke.time.read_time() - td.sleep_start) / std.time.ns_per_ms;
|
||||||
|
|
||||||
if (td.sleep_time >= config.sched_timeslice and td.priority_class == .Batch) {
|
if (td.sleep_time >= config.sched_timeslice and td.priority_class == .Batch) {
|
||||||
@@ -356,10 +365,10 @@ fn pick_realtime_thread(cpu: *PerCpu, minprio: u8, migrate: bool) ?*ke.Thread {
|
|||||||
while (td.pinned) {
|
while (td.pinned) {
|
||||||
const entry = td.runq_link.next;
|
const entry = td.runq_link.next;
|
||||||
|
|
||||||
td = @fieldParentPtr("runq_link", entry);
|
|
||||||
|
|
||||||
// Every thread is pinned, go through another queue
|
// Every thread is pinned, go through another queue
|
||||||
if (entry == &curr_queue.head) continue :qloop;
|
if (entry == &curr_queue.head) continue :qloop;
|
||||||
|
|
||||||
|
td = @fieldParentPtr("runq_link", entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,10 +419,10 @@ fn pick_batch_thread(cpu: *PerCpu, migrate: bool) ?*ke.Thread {
|
|||||||
while (td.pinned) {
|
while (td.pinned) {
|
||||||
const entry = td.runq_link.next;
|
const entry = td.runq_link.next;
|
||||||
|
|
||||||
td = @fieldParentPtr("runq_link", entry);
|
|
||||||
|
|
||||||
// Every thread is pinned, go through another queue
|
// Every thread is pinned, go through another queue
|
||||||
if (entry == &curr_queue.head) continue :qloop;
|
if (entry == &curr_queue.head) continue :qloop;
|
||||||
|
|
||||||
|
td = @fieldParentPtr("runq_link", entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,10 +451,12 @@ fn pick_idle_thread(cpu: *PerCpu, migrate: bool) ?*ke.Thread {
|
|||||||
// Just get the first thread from the queue.
|
// Just get the first thread from the queue.
|
||||||
const td: *ke.Thread = @fieldParentPtr("runq_link", cpu.idle_queue.first());
|
const td: *ke.Thread = @fieldParentPtr("runq_link", cpu.idle_queue.first());
|
||||||
|
|
||||||
if (!(migrate and td.pinned)) {
|
if (migrate and td.pinned) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// We can safely remove it.
|
// We can safely remove it.
|
||||||
td.runq_link.remove();
|
td.runq_link.remove();
|
||||||
}
|
|
||||||
|
|
||||||
return td;
|
return td;
|
||||||
}
|
}
|
||||||
@@ -655,6 +666,8 @@ fn should_preempt_cpu(td: *ke.Thread, cpu: *PerCpu) bool {
|
|||||||
|
|
||||||
// Enqueue a thread on a CPU.
|
// Enqueue a thread on a CPU.
|
||||||
fn enqueue_on_cpu(c: u32, td: *ke.Thread) void {
|
fn enqueue_on_cpu(c: u32, td: *ke.Thread) void {
|
||||||
|
std.debug.assert(td.lock.is_locked());
|
||||||
|
|
||||||
const cpu = percpu.remote(c);
|
const cpu = percpu.remote(c);
|
||||||
cpu.queues_lock.acquire_no_ipl();
|
cpu.queues_lock.acquire_no_ipl();
|
||||||
|
|
||||||
@@ -813,8 +826,15 @@ fn yield(cpu: u32) void {
|
|||||||
|
|
||||||
sched_cpu.queues_lock.release_no_ipl();
|
sched_cpu.queues_lock.release_no_ipl();
|
||||||
|
|
||||||
// Switch into the thread.
|
if (next != null and cur.? != next.?) {
|
||||||
|
// Switch into the thread. Take its lock here to ensure it is not still on its stack.
|
||||||
|
next.?.lock.acquire_no_ipl();
|
||||||
|
next.?.lock.release_no_ipl();
|
||||||
|
|
||||||
do_switch(sched_cpu, cur.?, next.?);
|
do_switch(sched_cpu, cur.?, next.?);
|
||||||
|
} else {
|
||||||
|
cur.?.lock.release_no_ipl();
|
||||||
|
}
|
||||||
|
|
||||||
// cur lock dropped
|
// cur lock dropped
|
||||||
}
|
}
|
||||||
@@ -934,7 +954,9 @@ pub fn idle(_: ?*anyopaque) noreturn {
|
|||||||
// Steal a thread.
|
// Steal a thread.
|
||||||
if (steal_work(cpu_id)) |td| {
|
if (steal_work(cpu_id)) |td| {
|
||||||
// Enqueue it on this CPU.
|
// Enqueue it on this CPU.
|
||||||
|
td.lock.acquire_no_ipl();
|
||||||
enqueue_on_cpu(cpu_id, td);
|
enqueue_on_cpu(cpu_id, td);
|
||||||
|
td.lock.release_no_ipl();
|
||||||
}
|
}
|
||||||
|
|
||||||
ke.ipl.lower(ipl);
|
ke.ipl.lower(ipl);
|
||||||
|
|||||||
+12
-3
@@ -61,8 +61,13 @@ pub fn send_resched_ipi(cpu: u32) void {
|
|||||||
|
|
||||||
pub const ThreadContext = struct {
|
pub const ThreadContext = struct {
|
||||||
ucontext: c.ucontext_t,
|
ucontext: c.ucontext_t,
|
||||||
|
old_lock: ?*ke.SpinLock,
|
||||||
|
|
||||||
fn real_entry(entry_int: c_ulong, arg_int: c_ulong) callconv(.c) void {
|
fn real_entry(entry_int: c_ulong, arg_int: c_ulong) callconv(.c) void {
|
||||||
|
if (ki.sched.percpu.local().current_thread.?.context.old_lock) |lock| {
|
||||||
|
lock.release_no_ipl();
|
||||||
|
ki.sched.percpu.local().current_thread.?.context.old_lock = null;
|
||||||
|
}
|
||||||
const entry: *const fn (?*anyopaque) void = @ptrFromInt(entry_int);
|
const entry: *const fn (?*anyopaque) void = @ptrFromInt(entry_int);
|
||||||
const arg: ?*anyopaque = @ptrFromInt(arg_int);
|
const arg: ?*anyopaque = @ptrFromInt(arg_int);
|
||||||
ke.ipl.lower(.Passive);
|
ke.ipl.lower(.Passive);
|
||||||
@@ -70,7 +75,7 @@ pub const ThreadContext = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(stack: r.VAddr, stack_size: usize, entry: *const fn (?*anyopaque) void, arg: ?*anyopaque) ThreadContext {
|
pub fn init(stack: r.VAddr, stack_size: usize, entry: *const fn (?*anyopaque) void, arg: ?*anyopaque) ThreadContext {
|
||||||
var new: ThreadContext = .{ .ucontext = undefined };
|
var new: ThreadContext = .{ .ucontext = undefined, .old_lock = null };
|
||||||
|
|
||||||
if (c.getcontext(&new.ucontext) == -1) {
|
if (c.getcontext(&new.ucontext) == -1) {
|
||||||
@panic("getcontext() failed");
|
@panic("getcontext() failed");
|
||||||
@@ -101,12 +106,16 @@ pub const ThreadContext = struct {
|
|||||||
|
|
||||||
pub fn switch_to(self: *ThreadContext, new: *ThreadContext) void {
|
pub fn switch_to(self: *ThreadContext, new: *ThreadContext) void {
|
||||||
const thread: *ke.Thread = @fieldParentPtr("context", self);
|
const thread: *ke.Thread = @fieldParentPtr("context", self);
|
||||||
|
new.old_lock = &thread.lock;
|
||||||
thread.lock.release_no_ipl();
|
|
||||||
|
|
||||||
if (c.swapcontext(&self.ucontext, &new.ucontext) == -1) {
|
if (c.swapcontext(&self.ucontext, &new.ucontext) == -1) {
|
||||||
@panic("swapcontext() failed");
|
@panic("swapcontext() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self.old_lock) |lock| {
|
||||||
|
self.old_lock = null;
|
||||||
|
lock.release_no_ipl();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+17
-5
@@ -132,18 +132,20 @@ pub fn wait_any(objects: []*DispatchHeader, timeout: ?r.Nanoseconds, waitblocks:
|
|||||||
if (satisfier != null or (has_timeout and timeout.? == 0)) {
|
if (satisfier != null or (has_timeout and timeout.? == 0)) {
|
||||||
std.debug.assert(curtd.wait_status.load(.acquire) == .Satisfied);
|
std.debug.assert(curtd.wait_status.load(.acquire) == .Satisfied);
|
||||||
|
|
||||||
if (satisfier) |sat| {
|
const cleanup_count = satisfier orelse total_count;
|
||||||
// Remove any wait block we might've installed
|
|
||||||
for (0..sat) |i| {
|
// Remove any wait block we might've installed.
|
||||||
|
for (0..cleanup_count) |i| {
|
||||||
const is_timer = has_timeout and i == timer_i;
|
const is_timer = has_timeout and i == timer_i;
|
||||||
var obj = if (is_timer) &timer.hdr else objects[i];
|
var obj = if (is_timer) &timer.hdr else objects[i];
|
||||||
var wb = if (is_timer) timer_block else &blocks[i];
|
var wb = if (is_timer) timer_block else &blocks[i];
|
||||||
|
|
||||||
obj.lock.acquire_no_ipl();
|
obj.lock.acquire_no_ipl();
|
||||||
|
if (wb.status == .Active) {
|
||||||
wb.status = .Inactive;
|
wb.status = .Inactive;
|
||||||
wb.link.remove();
|
wb.link.remove();
|
||||||
obj.lock.release_no_ipl();
|
|
||||||
}
|
}
|
||||||
|
obj.lock.release_no_ipl();
|
||||||
}
|
}
|
||||||
|
|
||||||
return satisfier orelse error.Timeout;
|
return satisfier orelse error.Timeout;
|
||||||
@@ -153,12 +155,18 @@ pub fn wait_any(objects: []*DispatchHeader, timeout: ?r.Nanoseconds, waitblocks:
|
|||||||
ke.timer.set(timer, timeout.?, null);
|
ke.timer.set(timer, timeout.?, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curtd.lock.acquire_no_ipl();
|
||||||
|
|
||||||
// Now try committing the wait.
|
// Now try committing the wait.
|
||||||
// While we're trying to commit the wait, the object locks have been released, and the state could therefore change.
|
// While we're trying to commit the wait, the object locks have been released, and the state could therefore change.
|
||||||
// We need to re-check the state of the wait before actually blocking.
|
// We need to re-check the state of the wait before actually blocking.
|
||||||
if (curtd.wait_status.cmpxchgStrong(.InProgress, .Committed, .acq_rel, .monotonic) == null) {
|
if (curtd.wait_status.cmpxchgStrong(.InProgress, .Committed, .acq_rel, .monotonic) == null) {
|
||||||
// We're good, now actually block.
|
// We're good, now actually block.
|
||||||
ke.sched.block();
|
//std.log.debug("Blocking {*} on {*}", .{ curtd, objects[0] });
|
||||||
|
ki.sched.block_locked(curtd);
|
||||||
|
} else {
|
||||||
|
// Could not commit.
|
||||||
|
curtd.lock.release_no_ipl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're back!
|
// We're back!
|
||||||
@@ -193,6 +201,8 @@ pub fn wait_any(objects: []*DispatchHeader, timeout: ?r.Nanoseconds, waitblocks:
|
|||||||
|
|
||||||
/// Satisfy a wait on an object.
|
/// Satisfy a wait on an object.
|
||||||
pub fn satisfy_wait(obj: *DispatchHeader) void {
|
pub fn satisfy_wait(obj: *DispatchHeader) void {
|
||||||
|
std.debug.assert(obj.lock.is_locked());
|
||||||
|
|
||||||
const all = obj.type == .Notification;
|
const all = obj.type == .Notification;
|
||||||
|
|
||||||
// Go through (potentially) all wait blocks and try to satisfy them.
|
// Go through (potentially) all wait blocks and try to satisfy them.
|
||||||
@@ -200,6 +210,8 @@ pub fn satisfy_wait(obj: *DispatchHeader) void {
|
|||||||
// Get the first waitblock from the list.
|
// Get the first waitblock from the list.
|
||||||
var wb: *WaitBlock = @fieldParentPtr("link", obj.waitblocks.first());
|
var wb: *WaitBlock = @fieldParentPtr("link", obj.waitblocks.first());
|
||||||
var td = wb.thread;
|
var td = wb.thread;
|
||||||
|
std.debug.assert(wb.status == .Active);
|
||||||
|
std.debug.assert(wb.object == obj);
|
||||||
|
|
||||||
// Remove it.
|
// Remove it.
|
||||||
wb.link.remove();
|
wb.link.remove();
|
||||||
|
|||||||
Reference in New Issue
Block a user