Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions kernel/arch/aarch64/timer/timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ __PRIVILEGED_CODE bool on_interrupt() {
if (t->timer_deadline > now) break;
state.sleep_queue.pop_front();
t->timer_deadline = 0;
t->block_kind = sched::TASK_BLOCK_NONE;
t->blocked_wait_queue = nullptr;
sched::wake(t);
}

Expand Down Expand Up @@ -188,6 +190,8 @@ __PRIVILEGED_CODE void schedule_sleep(sched::task* t, uint64_t deadline_ns) {
timer_cpu_state& state = this_cpu(cpu_timer_state);
sync::irq_state irq = sync::spin_lock_irqsave(state.lock);

t->block_kind = sched::TASK_BLOCK_TIMER;
t->blocked_wait_queue = nullptr;
t->timer_deadline = deadline_ns;
state.sleep_queue.insert_sorted(t,
[](sched::task* a, sched::task* b) {
Expand All @@ -202,4 +206,47 @@ __PRIVILEGED_CODE void schedule_sleep(sched::task* t, uint64_t deadline_ns) {
sync::spin_unlock_irqrestore(state.lock, irq);
}

/**
* @note Privilege: **required**
*/
__PRIVILEGED_CODE void cancel_sleep(sched::task* t) {
if (!t) {
return;
}

uint32_t target_cpu = __atomic_load_n(&t->exec.cpu, __ATOMIC_ACQUIRE);
if (target_cpu >= MAX_CPUS) {
return;
}

timer_cpu_state& state = per_cpu_on(cpu_timer_state, target_cpu);
bool should_wake = false;

sync::irq_state irq = sync::spin_lock_irqsave(state.lock);
if (t->state == sched::TASK_STATE_BLOCKED &&
t->block_kind == sched::TASK_BLOCK_TIMER &&
t->timer_link.prev && t->timer_link.next) {
state.sleep_queue.remove(t);
t->timer_deadline = 0;
t->block_kind = sched::TASK_BLOCK_NONE;
t->blocked_wait_queue = nullptr;
should_wake = true;

uint64_t next_event = state.next_tick_ns;
if (!state.sleep_queue.empty()) {
uint64_t front_deadline = state.sleep_queue.front()->timer_deadline;
if (front_deadline < next_event) {
next_event = front_deadline;
}
}
state.programmed_ns = next_event;
program_oneshot(next_event);
}
sync::spin_unlock_irqrestore(state.lock, irq);

if (should_wake) {
sched::wake(t);
}
}

} // namespace timer
5 changes: 5 additions & 0 deletions kernel/arch/aarch64/trap/trap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "common/types.h"
#include "common/logging.h"
#include "debug/panic.h"
#include "sched/sched.h"
#include "sched/task_exec_core.h"
#include "percpu/percpu.h"
#include "dynpriv/dynpriv.h"
Expand Down Expand Up @@ -74,6 +75,7 @@ void stlx_aarch64_el0_irq_handler(aarch64::trap_frame* tf) {
sched::on_tick(tf);
}
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand All @@ -82,6 +84,7 @@ void stlx_aarch64_el0_irq_handler(aarch64::trap_frame* tf) {
serial::on_rx_irq();
irq::eoi(irq_id);
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand Down Expand Up @@ -139,6 +142,7 @@ void stlx_aarch64_el1_irq_handler(aarch64::trap_frame* tf) {
sched::on_tick(tf);
}
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand All @@ -147,6 +151,7 @@ void stlx_aarch64_el1_irq_handler(aarch64::trap_frame* tf) {
serial::on_rx_irq();
irq::eoi(irq_id);
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand Down
47 changes: 47 additions & 0 deletions kernel/arch/x86_64/timer/timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ __PRIVILEGED_CODE bool on_interrupt() {
if (t->timer_deadline > now) break;
state.sleep_queue.pop_front();
t->timer_deadline = 0;
t->block_kind = sched::TASK_BLOCK_NONE;
t->blocked_wait_queue = nullptr;
sched::wake(t);
}

Expand Down Expand Up @@ -254,6 +256,8 @@ __PRIVILEGED_CODE void schedule_sleep(sched::task* t, uint64_t deadline_ns) {
timer_cpu_state& state = this_cpu(cpu_timer_state);
sync::irq_state irq = sync::spin_lock_irqsave(state.lock);

t->block_kind = sched::TASK_BLOCK_TIMER;
t->blocked_wait_queue = nullptr;
t->timer_deadline = deadline_ns;
state.sleep_queue.insert_sorted(t,
[](sched::task* a, sched::task* b) {
Expand All @@ -268,4 +272,47 @@ __PRIVILEGED_CODE void schedule_sleep(sched::task* t, uint64_t deadline_ns) {
sync::spin_unlock_irqrestore(state.lock, irq);
}

/**
* @note Privilege: **required**
*/
__PRIVILEGED_CODE void cancel_sleep(sched::task* t) {
if (!t) {
return;
}

uint32_t target_cpu = __atomic_load_n(&t->exec.cpu, __ATOMIC_ACQUIRE);
if (target_cpu >= MAX_CPUS) {
return;
}

timer_cpu_state& state = per_cpu_on(cpu_timer_state, target_cpu);
bool should_wake = false;

sync::irq_state irq = sync::spin_lock_irqsave(state.lock);
if (t->state == sched::TASK_STATE_BLOCKED &&
t->block_kind == sched::TASK_BLOCK_TIMER &&
t->timer_link.prev && t->timer_link.next) {
state.sleep_queue.remove(t);
t->timer_deadline = 0;
t->block_kind = sched::TASK_BLOCK_NONE;
t->blocked_wait_queue = nullptr;
should_wake = true;

uint64_t next_event = state.next_tick_ns;
if (!state.sleep_queue.empty()) {
uint64_t front_deadline = state.sleep_queue.front()->timer_deadline;
if (front_deadline < next_event) {
next_event = front_deadline;
}
}
state.programmed_ns = next_event;
program_oneshot(next_event);
}
sync::spin_unlock_irqrestore(state.lock, irq);

if (should_wake) {
sched::wake(t);
}
}

} // namespace timer
4 changes: 4 additions & 0 deletions kernel/arch/x86_64/trap/trap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "io/serial.h"
#include "timer/timer.h"
#include "debug/panic.h"
#include "sched/sched.h"
#include "sched/task_exec_core.h"
#include "percpu/percpu.h"
#include "dynpriv/dynpriv.h"
Expand Down Expand Up @@ -36,6 +37,7 @@ extern "C" __PRIVILEGED_CODE void stlx_x86_64_trap_handler(x86::trap_frame* tf)
sched::on_yield(tf);
// Clear the IRQ flag on the originally interrupted task, not the post-switch task.
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand All @@ -48,6 +50,7 @@ extern "C" __PRIVILEGED_CODE void stlx_x86_64_trap_handler(x86::trap_frame* tf)
}
// Clear IRQ state on the interrupted task to avoid stale IN_IRQ ownership.
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand All @@ -56,6 +59,7 @@ extern "C" __PRIVILEGED_CODE void stlx_x86_64_trap_handler(x86::trap_frame* tf)
irq::eoi(0);
serial::on_rx_irq();
irq_task_core->flags &= ~sched::TASK_FLAG_IN_IRQ;
sched::maybe_terminate_current();
restore_post_trap_elevation_state();
return;
}
Expand Down
148 changes: 132 additions & 16 deletions kernel/resource/providers/proc_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace resource::proc_provider {

static uint32_t g_next_terminate_epoch = 1;

__PRIVILEGED_CODE void proc_resource::ref_destroy(proc_resource* self) {
heap::kfree_delete(self);
}
Expand All @@ -27,6 +29,126 @@ __PRIVILEGED_CODE static ssize_t proc_write(
return ERR_UNSUP;
}

__PRIVILEGED_CODE static uint32_t allocate_terminate_epoch() {
uint32_t epoch = __atomic_fetch_add(&g_next_terminate_epoch, 1, __ATOMIC_ACQ_REL);
if (epoch == 0) {
epoch = __atomic_fetch_add(&g_next_terminate_epoch, 1, __ATOMIC_ACQ_REL);
}
return epoch;
}

__PRIVILEGED_CODE static resource_object* acquire_process_handle_at(
sched::task* task,
uint32_t index
) {
if (!task || index >= resource::MAX_TASK_HANDLES) {
return nullptr;
}

resource_object* obj = nullptr;
sync::irq_state irq = sync::spin_lock_irqsave(task->handles.lock);
const resource::handle_entry& entry = task->handles.entries[index];
if (entry.used &&
entry.type == resource::resource_type::PROCESS &&
entry.obj) {
resource::resource_add_ref(entry.obj);
obj = entry.obj;
}
sync::spin_unlock_irqrestore(task->handles.lock, irq);
return obj;
}

__PRIVILEGED_CODE static int32_t terminate_proc_resource_with_epoch(
proc_resource* pr,
int32_t exit_code,
uint32_t epoch
) {
if (!pr) {
return ERR_INVAL;
}

sched::task* created_child = nullptr;
sched::task* target_child = nullptr;
sync::irq_state irq = sync::spin_lock_irqsave(pr->lock);

if (!pr->child || pr->exited) {
pr->terminate_in_progress = false;
sync::spin_unlock_irqrestore(pr->lock, irq);
return OK;
}

if (pr->terminate_in_progress) {
if (pr->terminate_epoch == epoch) {
// Cycle detected in the same recursive termination traversal.
// Avoid waiting here to prevent deadlock (A->B->A style graphs).
sync::spin_unlock_irqrestore(pr->lock, irq);
return OK;
}

while (!pr->exited) {
irq = sync::wait(pr->wait_queue, pr->lock, irq);
}
sync::spin_unlock_irqrestore(pr->lock, irq);
return OK;
}

pr->terminate_in_progress = true;
pr->terminate_epoch = epoch;

if (pr->child->state == sched::TASK_STATE_CREATED) {
created_child = pr->child;
pr->exit_code = exit_code;
pr->exited = true;
pr->child = nullptr;
pr->terminate_in_progress = false;
sync::wake_all(pr->wait_queue);
sync::spin_unlock_irqrestore(pr->lock, irq);

if (created_child->proc_res) {
(void)created_child->proc_res->release();
created_child->proc_res = nullptr;
}
destroy_unstarted_task(created_child);
return OK;
}

target_child = pr->child;
sync::spin_unlock_irqrestore(pr->lock, irq);

for (uint32_t i = 0; i < resource::MAX_TASK_HANDLES; i++) {
resource_object* descendant_obj = acquire_process_handle_at(target_child, i);
if (!descendant_obj) {
continue;
}

proc_resource* child_pr = get_proc_resource(descendant_obj);
if (child_pr) {
(void)terminate_proc_resource_with_epoch(child_pr, exit_code, epoch);
}
resource::resource_release(descendant_obj);
}

irq = sync::spin_lock_irqsave(pr->lock);
if (!pr->exited && pr->child) {
sched::request_terminate(pr->child, exit_code);
}

while (!pr->exited) {
irq = sync::wait(pr->wait_queue, pr->lock, irq);
}
pr->terminate_in_progress = false;
sync::spin_unlock_irqrestore(pr->lock, irq);
return OK;
}

__PRIVILEGED_CODE int32_t terminate_proc_resource(
proc_resource* pr,
int32_t exit_code
) {
uint32_t epoch = allocate_terminate_epoch();
return terminate_proc_resource_with_epoch(pr, exit_code, epoch);
}

__PRIVILEGED_CODE static void proc_close(resource_object* obj) {
if (!obj || !obj->impl) {
return;
Expand All @@ -35,25 +157,17 @@ __PRIVILEGED_CODE static void proc_close(resource_object* obj) {
auto* impl = static_cast<proc_resource_impl*>(obj->impl);
auto* pr = impl->proc.ptr();

bool should_terminate = false;
sync::irq_state irq = sync::spin_lock_irqsave(pr->lock);

if (pr->child && pr->child->state == sched::TASK_STATE_CREATED) {
auto* child = pr->child;
pr->child = nullptr;
sync::spin_unlock_irqrestore(pr->lock, irq);

if (child->proc_res) {
(void)child->proc_res->release();
child->proc_res = nullptr;
}
destroy_unstarted_task(child);
should_terminate = true;
} else if (pr->child && !pr->exited && !pr->detached) {
uint32_t child_tid = pr->child->tid;
sync::spin_unlock_irqrestore(pr->lock, irq);
log::fatal("proc_close: parent exiting with running attached child tid=%u",
child_tid);
} else {
sync::spin_unlock_irqrestore(pr->lock, irq);
should_terminate = true;
}
sync::spin_unlock_irqrestore(pr->lock, irq);

if (should_terminate) {
(void)terminate_proc_resource(pr, PROC_KILL_EXIT_CODE);
}

heap::kfree_delete(impl);
Expand Down Expand Up @@ -84,8 +198,10 @@ __PRIVILEGED_CODE int32_t create_proc_resource(
pr->child = child_task;
pr->wait_queue.init();
pr->exit_code = 0;
pr->terminate_epoch = 0;
pr->exited = false;
pr->detached = false;
pr->terminate_in_progress = false;

pr->add_ref(); // refcount 1 -> 2 (second ref for the child task)

Expand Down
Loading