irq: Add irq_tasks, move NVMe to it

This commit is contained in:
2026-10-30 00:21:36 +01:00
parent 49c454dd97
commit 829490f8c8
7 changed files with 107 additions and 55 deletions
+79 -37
View File
@@ -1,10 +1,14 @@
// SPDX-License-Identifier: ISC
#include <zinnia/alloc.h>
#include <zinnia/assert.h>
#include <zinnia/atomic.h>
#include <zinnia/irq.h>
#include <zinnia/list.h>
#include <zinnia/mutex.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <zinnia/process.h>
#include <zinnia/sched.h>
#include <zinnia/utils.h>
#include <arch/cpu.h>
@@ -34,8 +38,12 @@ void irq_lock() {
void irq_unlock() {
struct irq_percpu* state = percpu_ref(irq_data);
if (--state->depth == 0 && state->saved_state)
if (--state->depth == 0 && state->saved_state) {
struct scheduler* sched = &percpu_get()->sched;
if (sched->owe_preempt && !sched->preempt_level && sched->current)
sched_reschedule(sched);
arch_irq_set_state(true);
}
}
void irq_line_program(struct irq_line* line, enum irq_trigger_mode trigger, enum irq_polarity polarity) {
@@ -45,37 +53,47 @@ void irq_line_program(struct irq_line* line, enum irq_trigger_mode trigger, enum
irq_unlock();
}
static bool holds_mask_while_threaded(struct irq_line* line) {
struct irq_task {
struct irq_line* line;
struct task* task;
_Atomic(bool) need;
_Atomic(bool) masked;
_Atomic(bool) running;
};
static mutex_t irq_task_lock;
static bool holds_mask(struct irq_line* line) {
return line->trigger == IRQ_TRIGGER_LEVEL && (line->mode & IRQ_MODE_MASKABLE) && line->mask && line->unmask;
}
static void wake_thread(struct irq_line* line, struct irq_handler* handler) {
if (holds_mask_while_threaded(line)) {
spin_lock(&line->thread_lock);
if (!handler->holds_mask) {
handler->holds_mask = true;
if (line->threads_pending++ == 0)
line->mask(line);
}
spin_unlock(&line->thread_lock);
}
static void irq_task_fn(uintptr_t arg) {
struct irq_task* it = (struct irq_task*)arg;
struct irq_line* line = it->line;
work_queue(handler->wq, &handler->work);
while (true) {
atomic_store(&it->running, true, ATOMIC_SEQ_CST);
while (atomic_exchange(&it->need, false, ATOMIC_ACQ_REL)) {
struct irq_handler* handler;
list_foreach(handler, &line->handlers, next) {
if (atomic_exchange(&handler->need, false, ATOMIC_ACQ_REL))
handler->thread(handler);
}
if (atomic_exchange(&it->masked, false, ATOMIC_ACQ_REL))
line->unmask(line);
}
atomic_store(&it->running, false, ATOMIC_SEQ_CST);
if (!atomic_load(&it->need, ATOMIC_ACQUIRE))
sched_block(&percpu_get()->sched);
}
}
static void irq_thread_work(struct work* work) {
struct irq_handler* handler = containerof(work, struct irq_handler, work);
struct irq_line* line = handler->line;
handler->thread(handler);
spin_lock(&line->thread_lock);
if (handler->holds_mask) {
handler->holds_mask = false;
if (--line->threads_pending == 0)
line->unmask(line);
}
spin_unlock(&line->thread_lock);
static void irq_task_schedule(struct irq_task* it) {
if (holds_mask(it->line) && !atomic_exchange(&it->masked, true, ATOMIC_ACQ_REL))
it->line->mask(it->line);
atomic_store(&it->need, true, ATOMIC_RELEASE);
sched_wake(it->task);
}
void irq_line_raise(struct irq_line* line) {
@@ -85,17 +103,21 @@ void irq_line_raise(struct irq_line* line) {
line->is_busy = true;
bool claimed = false;
bool wake = false;
struct irq_handler* handler;
list_foreach(handler, &line->handlers, next) {
enum irq_status status = handler->raise ? handler->raise(handler) : IRQ_STATUS_WAKE_THREAD;
if (status == IRQ_STATUS_WAKE_THREAD && handler->wq) {
wake_thread(line, handler);
if (status == IRQ_STATUS_WAKE_THREAD && handler->thread && line->task) {
atomic_store(&handler->need, true, ATOMIC_RELEASE);
wake = true;
claimed = true;
} else if (status != IRQ_STATUS_IGNORED) {
claimed = true;
}
}
if (wake)
irq_task_schedule(line->task);
if (!claimed)
log("irq: Spurious interrupt\n");
@@ -107,6 +129,7 @@ void irq_line_raise(struct irq_line* line) {
}
void irq_line_attach(struct irq_line* line, struct irq_handler* handler) {
handler->line = line;
irq_lock();
list_insert_head(&line->handlers, handler, next);
irq_unlock();
@@ -123,21 +146,40 @@ void irq_line_detach(struct irq_line* line, struct irq_handler* handler) {
while (__atomic_load_n(&line->is_busy, __ATOMIC_ACQUIRE))
arch_cpu_pause();
atomic_exchange(&handler->need, false, ATOMIC_SEQ_CST);
if (line->task) {
while (atomic_load(&line->task->running, ATOMIC_SEQ_CST))
sched_reschedule(&percpu_get()->sched);
}
}
errno_t irq_line_attach_threaded(struct irq_line* line, struct irq_handler* handler, const char* name) {
errno_t irq_line_attach_task(struct irq_line* line, struct irq_handler* handler, const char* name) {
if (!handler->thread)
return EINVAL;
char task_name[TASK_NAME_MAX];
ksnprintf(task_name, sizeof(task_name), "irq/%s", name);
mutex_lock(&irq_task_lock);
if (!line->task) {
struct irq_task* it = mem_alloc(sizeof(*it), 0);
if (!it) {
mutex_unlock(&irq_task_lock);
return ENOMEM;
}
it->line = line;
handler->line = line;
work_init(&handler->work, irq_thread_work);
handler->holds_mask = false;
errno_t status = workqueue_create(task_name, SCHED_PRIO_IRQ, &handler->wq);
if (status)
return status;
char task_name[TASK_NAME_MAX];
ksnprintf(task_name, sizeof(task_name), "irq/%s", name);
errno_t status = task_create(task_name, process_kernel(), irq_task_fn, (uintptr_t)it, &it->task);
if (status) {
mem_free(it);
mutex_unlock(&irq_task_lock);
return status;
}
sched_set_priority(it->task, SCHED_PRIO_IRQ);
sched_add_task(&percpu_get()->sched, it->task);
line->task = it;
}
mutex_unlock(&irq_task_lock);
irq_line_attach(line, handler);
return 0;