From c4a44542ef2cfc1eb66ccd58fef48faae2ebb21f Mon Sep 17 00:00:00 2001 From: Yi-Ting Shih Date: Tue, 15 Apr 2025 17:22:45 +0800 Subject: [PATCH] Fix: interrupt preemption --- include/logger.h | 8 ++--- kernel/include/interrupt.h | 2 ++ kernel/lib/interrupt.c | 17 ++++++++-- kernel/lib/kmalloc.c | 1 - kernel/lib/mman.c | 8 ++--- kernel/lib/shell.c | 1 + kernel/lib/timer.c | 40 ++++++++++++++++++----- kernel/lib/uart.c | 67 +++++++++++++++++++++----------------- 8 files changed, 93 insertions(+), 51 deletions(-) diff --git a/include/logger.h b/include/logger.h index f472760..3cec69d 100644 --- a/include/logger.h +++ b/include/logger.h @@ -101,11 +101,11 @@ logger_cur = _Generic((msg), \ // #define DEBUG_DTB(val) DEBUG(val) #define DEBUG_DTB(val) CLEAN -// #define DEBUG_EXCEP(val) DEBUG(val) -#define DEBUG_EXCEP(val) CLEAN +#define DEBUG_EXCEP(val) DEBUG(val) +// #define DEBUG_EXCEP(val) CLEAN -#define DEBUG_MEM(val) DEBUG(val) -// #define DEBUG_MEM(val) CLEAN +// #define DEBUG_MEM(val) DEBUG(val) +#define DEBUG_MEM(val) CLEAN // #define DEBUG_INITRD(val) DEBUG(val) #define DEBUG_INITRD(val) CLEAN diff --git a/kernel/include/interrupt.h b/kernel/include/interrupt.h index 25b2349..1823384 100644 --- a/kernel/include/interrupt.h +++ b/kernel/include/interrupt.h @@ -11,6 +11,8 @@ typedef struct interrupt { uint64_t priority; interrupt_callback_func_t func; uint64_t param; + + int is_start; } interrupt_t; void add_interrupt_task(uint64_t priority, diff --git a/kernel/lib/interrupt.c b/kernel/lib/interrupt.c index 96fe077..1157ca4 100644 --- a/kernel/lib/interrupt.c +++ b/kernel/lib/interrupt.c @@ -73,16 +73,27 @@ void add_interrupt_task(uint64_t priority, .priority = priority, .func = func, - .param = param + .param = param, + + .is_start = false, }; global_interrupt_pool = _merge(global_interrupt_pool, newint); + + while (global_interrupt_pool) { + // LOG("check interrupt"); DEBUG_EXCEP(global_interrupt_pool->priority); + if (global_interrupt_pool->is_start) + return; + global_interrupt_pool->is_start = true; + global_interrupt_pool->func(global_interrupt_pool->param); + global_interrupt_pool = _pop(global_interrupt_pool); + } } void init_interrupt(void) { - W_SYSREG(cntp_ctl_el0, 1); _enable_interrupt(); + _enable_timer_irq(true); } void irq_handler(void) @@ -99,6 +110,8 @@ void irq_handler(void) void wfe(void) { + return; // do nothing for now + if (!global_interrupt_pool) { // asm volatile("wfe"); asm volatile("nop"); diff --git a/kernel/lib/kmalloc.c b/kernel/lib/kmalloc.c index e8dea32..16c519b 100644 --- a/kernel/lib/kmalloc.c +++ b/kernel/lib/kmalloc.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/kernel/lib/mman.c b/kernel/lib/mman.c index 8f0ed5d..4820e58 100644 --- a/kernel/lib/mman.c +++ b/kernel/lib/mman.c @@ -118,16 +118,12 @@ uint64_t _allocate_page(size_t req, int idx, uint64_t l, uint64_t r) switch (CUR->state) { case PAGE_FREE: if (req == sz) { - LOG("page allocated"); - LOG(l); - DEBUG_MEM(r); + LOG("page allocated"); LOG(l); DEBUG_MEM(r); CUR->state = PAGE_ALLOCATED; CUR->size = 0; return l; } - LOG("page divided"); - LOG(l); - DEBUG(r); + LOG("page divided"); LOG(l); DEBUG_MEM(r); LCH->state = RCH->state = PAGE_FREE; LCH->size = m - l; RCH->size = r - m; diff --git a/kernel/lib/shell.c b/kernel/lib/shell.c index 23566fe..fc04dbe 100644 --- a/kernel/lib/shell.c +++ b/kernel/lib/shell.c @@ -125,6 +125,7 @@ static inline void _settimeout_cb_func(uint64_t args) { vector_t *v = (void *)args; + uart_puts("timeout: "); for (int i = 2; i < (int)v->size; ++i) { uart_puts(VEC_AT(char, v, i)); uart_puts(" "); diff --git a/kernel/lib/timer.c b/kernel/lib/timer.c index 3728312..cb1448e 100644 --- a/kernel/lib/timer.c +++ b/kernel/lib/timer.c @@ -47,7 +47,7 @@ timer_t *_pop(timer_t *t) } static inline -void _set_timer_interrupt() +void _check_enable_timer() { if (global_timer) { uint64_t cntpct_el0; @@ -59,8 +59,9 @@ void _set_timer_interrupt() else W_SYSREG(cntp_cval_el0, global_timer->data.firing_tick); - _enable_timer_irq(true); - } + W_SYSREG(cntp_ctl_el0, 1); + } else + W_SYSREG(cntp_ctl_el0, 0); } static inline @@ -87,8 +88,23 @@ void add_timer_task(task_t task) global_timer = _merge(global_timer, newtimer); - _traverse(global_timer); - _set_timer_interrupt(); + // _traverse(global_timer); + _check_enable_timer(); +} + +typedef struct { + interrupt_callback_func_t func; + uint64_t param; +} _timer_task_wrapper_param_t; + +static inline +void _timer_task_wrapper(uint64_t param) +{ + _timer_task_wrapper_param_t *data = (void *)param; + data->func(data->param); + kfree(data); + + _enable_timer_irq(true); } void timer_irq_handler(void) @@ -100,9 +116,15 @@ void timer_irq_handler(void) LOG("timer irq"); DEBUG_EXCEP(cntpct_el0); if (global_timer) { - add_interrupt_task(20, global_timer->data.func, global_timer->data.param); - global_timer = _pop(global_timer); - } + _timer_task_wrapper_param_t *param = + kmalloc(sizeof(_timer_task_wrapper_param_t)); + *param = (_timer_task_wrapper_param_t){ + .func = global_timer->data.func, + .param = global_timer->data.param, + }; + add_interrupt_task(20, _timer_task_wrapper, (uint64_t)param); - _set_timer_interrupt(); + global_timer = _pop(global_timer); + _check_enable_timer(); + } } diff --git a/kernel/lib/uart.c b/kernel/lib/uart.c index efe6248..aa5a6b9 100644 --- a/kernel/lib/uart.c +++ b/kernel/lib/uart.c @@ -29,6 +29,8 @@ size_t (*uart_putb)(const uint8_t *bytes, size_t len) = uart_putb_sync; size_t (*uart_getb)(uint8_t *bytes, size_t len) = uart_getb_sync; int is_uart_inited = false; +int is_uart_in_interrupt_queue = false; + ringbuffer_t *uart_readbuf; ringbuffer_t *uart_writebuf; @@ -83,37 +85,47 @@ void uart_init(void) is_uart_inited = true; } -void uart_transmit_interrupt_handler() +static inline +void _enable_uart_interrupt() { + _uart_enable_receive_interrupt(true); if (uart_writebuf->size > 0) - _uart_write_data(ringbuffer_bump(uart_writebuf)); + _uart_enable_transmit_interrupt(true); } -typedef struct { - ringbuffer_t *buf; - uint8_t val; -} uart_interrupt_callback_payload_t; - static inline -void uart_receive_interrupt_callback(uint64_t param) +void _uart_transmit_interrupt_callback(uint64_t) { - uart_interrupt_callback_payload_t *payload = (void *)param; - ringbuffer_push(payload->buf, payload->val); - kfree(payload); + _uart_write_data(ringbuffer_bump(uart_writebuf)); + + is_uart_in_interrupt_queue = false; + _enable_uart_interrupt(); } -void uart_receive_interrupt_handler() +static inline +void _uart_transmit_interrupt_handler() +{ + if (uart_writebuf->size > 0) { + is_uart_in_interrupt_queue = true; + add_interrupt_task(11, _uart_transmit_interrupt_callback, 0x0); + } +} + +static inline +void _uart_receive_interrupt_callback(uint64_t) +{ + ringbuffer_push(uart_readbuf, _uart_read_data()); + + is_uart_in_interrupt_queue = false; + _enable_uart_interrupt(); +} + +static inline +void _uart_receive_interrupt_handler() { if (uart_readbuf->size < uart_readbuf->cap) { - uint8_t b = _uart_read_data(); - - uart_interrupt_callback_payload_t *param = kmalloc( - sizeof(uart_interrupt_callback_payload_t)); - *param = (uart_interrupt_callback_payload_t){ - .buf = uart_readbuf, - .val = b, - }; - add_interrupt_task(10, uart_receive_interrupt_callback, (uint64_t)param); + is_uart_in_interrupt_queue = true; + add_interrupt_task(12, _uart_receive_interrupt_callback, 0x0); } } @@ -121,14 +133,11 @@ void uart_irq_handler(void) { _uart_enable_receive_interrupt(false); _uart_enable_transmit_interrupt(false); + if (_uart_receive_interrupt()) - uart_receive_interrupt_handler(); + _uart_receive_interrupt_handler(); if (_uart_transmit_interrupt()) - uart_transmit_interrupt_handler(); - - _uart_enable_receive_interrupt(true); - if (uart_writebuf->size > 0) - _uart_enable_transmit_interrupt(true); + _uart_transmit_interrupt_handler(); } size_t uart_putb_async(const uint8_t *bytes, size_t len) @@ -137,8 +146,8 @@ size_t uart_putb_async(const uint8_t *bytes, size_t len) for (; sentlen < len; ++bytes, ++sentlen) ringbuffer_push(uart_writebuf, *bytes); - if (uart_writebuf->size > 0) - _uart_enable_transmit_interrupt(true); + if (!is_uart_in_interrupt_queue && uart_writebuf) + _enable_uart_interrupt(); return sentlen; }